C++ Access Autoconf Variable Datadir - c++

I am creating a program called spellcheck, and I'm using autoconf and automake to create a build system for it. The program relies on the dictionary 'english.dict', which is in the data directory (based on whatever prefix the user selected). I want the data directory path accessible by spellcheck, so I created a custom variable that contained its value:
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT(libspellcheck, 1.25, corinthianmonthly#hotmail.com)
AC_OUTPUT(Makefile libspellcheck/Makefile spellcheck/Makefile man/Makefile)
AC_CONFIG_SRCDIR([])
AC_CONFIG_HEADERS([config.h])
AC_DEFINE_UNQUOTED([DATA_PATH], ["$pkgdatadir"],"DData Directory Path")
AM_INIT_AUTOMAKE
# Checks for programs.
AC_PROG_CXX
AC_PROG_CC
AC_PROG_CXX
AC_PROG_RANLIB
# Checks for libraries.
# Checks for header files.
AC_CHECK_HEADERS([stdlib.h,iostream,fstream,string,stdio.h,sstream,cctype,algorithm,boost/algorithm/string.hpp])
# Checks for typedefs, structures, and compiler characteristics.
AC_CHECK_HEADER_STDBOOL
AC_TYPE_SIZE_T
# Checks for library functions.
AC_OUTPUT
However, in the config.h file, this value is blank:
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* "Description" */
#define DATA_PATH ""
...
I tried changing $pkgdatadir to $datadir, but I got the same result. What am I doing wrong, or is what I am trying to achieve impossible?
EDIT: I redefined the variable in my Makefile.am for spellcheck:
AM_CFLAGS = -DDATA_PATH=\"$(pkgdatadir)\" -m32 -Wall
bin_PROGRAMS = spellcheck
pkgdata_DATA = english.dict
spellcheck_SOURCES = spellcheck.cpp meta.cpp
spellcheck_LDADD = ../libspellcheck/libspellcheck.a
But now it complains about DATA_PATH being nonexistant:
spellcheck.cpp:4:22: error: 'DATA_PATH' was not declared in this scope
#define DEFAULT_DICT DATA_PATH "english.dict"
Because now it seems to be ignoring all CFLAGS:
g++ -DHAVE_CONFIG_H -I. -g -O2 -MT spellcheck.o -MD -MP -MF .deps/spellcheck.Tpo -c -o spellcheck.o spellcheck.cpp

It turns out that I needed to use AM_CPPFLAGS rather than CFLAGS.

Related

Run a script upon linking to a CMake target

I have a situation where I need to run a script when a CMake target is linked-to so that it can automatically generate files in the current project directory that are used to interface with the library.
I know when you link to a CMake target it automatically pulls in the headers for the library so they become visible to the compiler, but I need it to also generate some files within the directory of the linkee that will also be visible to the compiler upon building.
How can I tell CMake that I want to run a script to generate the files every time my_cmake_target is linked to?
Example of linking in CMakeLists.txt:
target_link_libraries(my_executable PRIVATE my_cmake_target)
I want the command to run at the same time that CMake transitively updates the include directories based on the target passed to "target_link_libraries". (Before any building/linking actually takes place)
See here for more info on how that works:
https://schneide.blog/2016/04/08/modern-cmake-with-target_link_libraries/
Using target_link_libraries to link A to an internal target B will not only add the linker flags required to link to B, but also the definitions, include paths and other settings – even transitively – if they are configured that way.
Unfortunately, there's nothing built-in to help you do this. Propagating custom commands through interface properties is not something CMake has implemented (or has plans to, afaik).
However, and this is kind of cursed, here is a way.
You create a function that scans the directory for targets that link to your special library. For each one of those targets, it attaches a special source file in the binary directory and a command for generating that file. It uses a custom property (here, MAGIC) for determining whether to actually generate the source file and include it in your target's sources.
Then, use cmake_language(DEFER CALL ...) to run that function at the end of the current directory's build script. This part ensures the function does not have to be called manually, even in find_package scenarios.
TODOS:
Running this code twice will likely cause errors. However, you can avoid problems by marking whether a target has already been processed with another bespoke property.
# ./CMakeLists.txt
cmake_minimum_required(VERSION 3.22)
project(example LANGUAGES CXX)
add_subdirectory(subdir)
add_executable(my_executable main.cpp)
target_link_libraries(my_executable PRIVATE my_cmake_target)
add_executable(excluded main.cpp default-name.cpp)
# ./subdir/CMakeLists.txt
function (MyProj_post_build)
set(dirs ".")
while (dirs)
list(POP_FRONT dirs dir)
get_property(subdirs DIRECTORY "${dir}" PROPERTY SUBDIRECTORIES)
list(APPEND dirs ${subdirs})
get_property(targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS)
foreach (target IN LISTS targets)
# Do whatever you want here, really. The key is checking
# that $<BOOL:$<TARGET_PROPERTY:MAGIC>> is set on the
# target at generation time. I use a custom command here,
# but you could use file(GENERATE).
add_custom_command(
OUTPUT "MyProj_${target}.cpp"
COMMAND "${CMAKE_COMMAND}" -E echo "const char* Name = \"$<TARGET_PROPERTY:${target},NAME>\";" > "MyProj_${target}.cpp"
VERBATIM
)
target_sources(
"${target}"
PRIVATE
"$<$<BOOL:$<TARGET_PROPERTY:MAGIC>>:$<TARGET_OUT/MyProj_${target}.cpp>"
)
endforeach ()
endwhile ()
endfunction ()
cmake_language(DEFER DIRECTORY "${CMAKE_SOURCE_DIR}" CALL MyProj_post_build)
add_library(my_cmake_target INTERFACE)
set_target_properties(my_cmake_target PROPERTIES INTERFACE_MAGIC ON)
set_property(TARGET my_cmake_target APPEND PROPERTY COMPATIBLE_INTERFACE_STRING MAGIC)
// main.cpp
#include <iostream>
extern const char* Name;
int main () { std::cout << Name << "\n"; }
// default-name.cpp
const char* Name = "default";
Here's proof it works...
$ cmake -G Ninja -S . -B build
[1/7] cd /home/alex/test/build && /usr/bin/cmake -E echo "const char* Name = \"my_executable\";" > MyProj_my_executable.cpp
[2/7] /usr/bin/c++ -MD -MT CMakeFiles/excluded.dir/default-name.cpp.o -MF CMakeFiles/excluded.dir/default-name.cpp.o.d -o CMakeFiles/excluded.dir/default-name.cpp.o -c /home/alex/test/default-name.cpp
[3/7] /usr/bin/c++ -MD -MT CMakeFiles/my_executable.dir/MyProj_my_executable.cpp.o -MF CMakeFiles/my_executable.dir/MyProj_my_executable.cpp.o.d -o CMakeFiles/my_executable.dir/MyProj_my_executable.cpp.o -c /home/alex/test/build/MyProj_my_executable.cpp
[4/7] /usr/bin/c++ -MD -MT CMakeFiles/excluded.dir/main.cpp.o -MF CMakeFiles/excluded.dir/main.cpp.o.d -o CMakeFiles/excluded.dir/main.cpp.o -c /home/alex/test/main.cpp
[5/7] /usr/bin/c++ -MD -MT CMakeFiles/my_executable.dir/main.cpp.o -MF CMakeFiles/my_executable.dir/main.cpp.o.d -o CMakeFiles/my_executable.dir/main.cpp.o -c /home/alex/test/main.cpp
[6/7] : && /usr/bin/c++ CMakeFiles/my_executable.dir/main.cpp.o CMakeFiles/my_executable.dir/MyProj_my_executable.cpp.o -o my_executable && :
[7/7] : && /usr/bin/c++ CMakeFiles/excluded.dir/main.cpp.o CMakeFiles/excluded.dir/default-name.cpp.o -o excluded && :
$ ./build/my_executable
my_executable
$ ./build/excluded
default

Using ArduinoCore-avr library in arduino project

I want to use ArduinoCore-avr in my project. I don't want to use arduino's IDE default function setup() and loop(). I also don't want to use arduino's IDE to compile and burn hex file into my device. ArduinoCore-avr library is downloaded from ArduinoCore.
Hardware: arduino uno (atmega328p)
Directory structure:
./ArduinoCore-avr/cores/arduino/Arduino.h
./main.c
./Makefile
main.c:
#ifndef F_CPU
#define F_CPU 16000000UL // or whatever may be your frequency
#endif
#include <avr/io.h>
#include <util/delay.h> // for _delay_ms()
#include "ArduinoCore-avr/cores/arduino/Arduino.h" // This line gave me error.
#define LED_PIN 13
int main(void)
{
DDRB=0b11100000;//pin 13 is in output mode
while (1) {
PORTB=0b11100000; //make pin 13 high and power on the led
_delay_ms(1000);
PORTB=0b11000000; //make pin 13 low and power off the led
_delay_ms(1000);
}
}
Makefile:
# Name: Makefile
# Author: <insert your name here>
# Copyright: <insert your copyright message here>
# License: <insert your license reference here>
# DEVICE ....... The AVR device you compile for
# CLOCK ........ Target AVR clock rate in Hertz
# OBJECTS ...... The object files created from your source files. This list is
# usually the same as the list of source files with suffix ".o".
# PROGRAMMER ... Options to avrdude which define the hardware you use for
# uploading to the AVR and the interface where this hardware
# is connected.
# FUSES ........ Parameters for avrdude to flash the fuses appropriately.
DEVICE = atmega328p
AVRDUDE_DEVICE = m328p
CLOCK = 16000000
PROGRAMMER = -c arduino -P /dev/tty.usbmodem1411
OBJECTS = main.o
FUSES = -U lfuse:w:0x64:m -U hfuse:w:0xdd:m -U efuse:w:0xff:m
HEADER = ArduinoCore-avr/cores/arduino/Arduino.h
######################################################################
######################################################################
# Tune the lines below only if you know what you are doing:
AVRDUDE = avrdude $(PROGRAMMER) -p $(AVRDUDE_DEVICE)
COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(DEVICE)
# symbolic targets:
all: main.hex
.c.o:
$(COMPILE) -c $< -o $#
.S.o:
$(COMPILE) -x assembler-with-cpp -c $< -o $#
# "-x assembler-with-cpp" should not be necessary since this is the default
# file type for the .S (with capital S) extension. However, upper case
# characters are not always preserved on Windows. To ensure WinAVR
# compatibility define the file type manually.
.c.s:
$(COMPILE) -S $< -o $#
flash: all
$(AVRDUDE) -U flash:w:main.hex:i
fuse:
$(AVRDUDE) $(FUSES)
install: flash fuse
# if you use a bootloader, change the command below appropriately:
load: all
bootloadHID main.hex
clean:
rm -f main.hex main.elf $(OBJECTS)
# file targets:
main.elf: $(OBJECTS)
$(COMPILE) -o main.elf $(OBJECTS)
main.hex: main.elf
rm -f main.hex
avr-objcopy -j .text -j .data -O ihex main.elf main.hex
# If you have an EEPROM section, you must also create a hex file for the
# EEPROM and add it to the "flash" target.
# Targets for code debugging and analysis:
disasm: main.elf
avr-objdump -d main.elf
cpp:
$(COMPILE) -E main.c
When compiling with the following command.
make
It shows the following error.
In file included from main.c:7:
ArduinoCore-avr/cores/arduino/Arduino.h:257:10: fatal error: pins_arduino.h: No such file or directory
#include "pins_arduino.h"
^~~~~~~~~~~~~~~~
compilation terminated.
I know that I have to add the library in my Makefile, but I don't know how to do that.
When including the header file in main.c, should I use #include "ArduinoCore-avr/cores/arduino/Arduino.h" or #include "Arduino.h".
The reason why I want to include Arduino.h file is that I want to use pinMode and digitalWrite function in my main.c. I don't want to reinvent the wheel (write the driver from scratch). Is that possible? I've tried to search the example code, but they all use arduino's default function which are loop and setup which is what I want to avoid.
Thank you.
Yes, it should be possible to the use Arduino AVR core code if you can manage to invoke the compiler in the right way to compile it and link it into your progam.
I am not going to give you an entire tutorial to solve all the problems you might find along the way. I think it will be sufficient to just point out how to solve the error you are currently getting, which is that pins_arduino.h is not found.
GCC has a list of directories called an include search path. When you #include a file, GCC searches the directories in the include search path for a file of that name. To make sure GCC can find pins_arduino.h, you need to locate the directory that has the right version of pins_arduino.h and add it to your include path using GCC's -I option. For example:
avr-gcc -Ipath/to/folder/ ...
The path can be relative or absolute but relative ones are easier to maintain and share with others.

Makefile.am/Configure.ac Conditional Environment Variable Check

I am trying to enable and disable compile flags for gcov in C++ on Linux. I do not want to have the gcov flags set at all times. I only want them set when I am testing the software. The environment variable I am checking is called TESTENABLED.
In my configure.ac file I have the following line:
AM_CONDITIONAL([ENABLEGCOV],[test x$TESTENABLED = xtrue])
In my Makefile.am file I have the following lines:
if ENABLEGCOV
AM_CXXFLAGS = -Wall -fPIC -fprofile-arcs -ftest-coverage
else
AM_CXXFLAGS = -Wall
endif
However when I build my program I notice that it is not setting AM_CXXFLAGS correctly. So none of my gcov .gcno/.gcda files are being generated. Does anyone see what I am doing wrong?
Do you have your environment variable set to true, or probably to some other truish-value (e.g. 1)?
In any case, the usual way would be add a flag to configure that turns on a certain feature. The following configure.ac snippet adds a --enable-gcov flag to configure; it will also do a printout whether it has enabled gcov or not:
AC_ARG_ENABLE(gcov,[AS_HELP_STRING([--enable-gcov], [enable coverage test])])
AC_MSG_CHECKING([whether to enable gcov])
AS_IF([test "x${enable_gcov}" = "xyes" ], AC_MSG_RESULT([yes]), AC_MSG_RESULT([no]))
AM_CONDITIONAL([ENABLEGCOV],[test "x${enable_gcov}" = "xyes"])
I also find Makefile.am more easy to read by just adding flags to CXXFLAGS if a certain condition is met:
AM_CXXFLAGS = -Wall -fPIC
if ENABLEGCOV
AM_CXXFLAGS += -fprofile-arcs -ftest-coverage
endif

Issue with autotools and generating a c++ library

I am new to autotools and have tried to code up an example. I am creating one library and linking it against another object.
My problem is that the Makefile that is generated for the library seems to be mis-interpreted as C code, but is actually C++. If I replace all occurences of ".c" to ".cpp" in the generated Makefile, all is well.
I am using libtool 2.4.2 and autoconf 2.69. My code structure is listed below:
test/
- Makefile.am
- configure.ac
include/
- mylib.hh
src/
hw/
- Makefile.am
- main.cpp
mylib/
- Makefile.am
- mylib.cpp
Below is my top level Makefile.am:
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src/mylib src/hw
configure.ac
AC_PREREQ([2.65])
AC_INIT([hw],[1.0.0],[foo.bar#example.com])
# directories (relative to top-level) to look into for AutoConf/AutoMake files
AC_CONFIG_MACRO_DIR([m4])
# enable AutoMake
AM_INIT_AUTOMAKE([1.10])
# all defined C macros (HAVE_*) will be saved to this file
AC_CONFIG_HEADERS([config.h])
AC_PROG_CC
AM_PROG_CC_C_O
# Check if you have a C++ compiler
AC_PROG_CXX
AC_PROG_CXX_C_O
AC_PROG_INSTALL
## Initialize GNU LibTool
LT_INIT
AC_CONFIG_FILES([Makefile
src/hw/Makefile
src/mylib/Makefile])
AC_OUTPUT
src/hw/Makefile.am
ACLOCAL_AMFLAGS = -I m4
bin_PROGRAMS = hw
hw_SOURCES = main.cpp
AM_CPPFLAGS = -I$(top_srcdir)/include
hw_LDADD = $(top_srcdir)/src/mylib/libmylib.la
src/mylib/Makefile.am
ACLOCAL_AMFLAGS = -I m4
lib_LTLIBRARIES = libmylib.la
libmylib_la_sources = mylib.cpp
libmylib_la_CPPFLAGS = -I$(top_srcdir)/include
The error I get when running make:
make[2]: *** No rule to make target 'libmylib.c', needed by 'libmylib_la-libmylib.lo'. Stop.
Once again, substituting ".cpp" for all occurrences of ".c" in the generated src/mylib/Makefile, the code compiles, links and runs fine. Any help is appreciated.
Did you try changing:
libmylib_la_sources = mylib.cpp
to
libmylib_la_SOURCES = mylib.cpp
(upper case SOURCES)? m4 is case-sensitive.

Why the success of SCons build depends on variant_dir name?

I am bored to death with such behavior. So in SConstruct file we have the last string like this one:
import compilers, os
env = Environment(ENV = os.environ, TOOLS = ['default'])
def set_compiler(compiler_name):
env.Replace(FORTRAN = compiler_name)
env.Replace(F77 = compiler_name)
env.Replace(F90 = compiler_name)
env.Replace(F95 = compiler_name)
def set_flags(flags):
env.Replace(FORTRANFLAGS = flags)
env.Replace(F77FLAGS = flags)
env.Replace(F90FLAGS = flags)
env.Replace(F95FLAGS = flags)
mod_dir_prefix = {
"gfortran": "-J ",
"ifort": "-???",
"pgfortran": "-module "
}
flags = {
("gfortran", "debug"): "-O0 -g -Wall -Wextra -pedantic -fimplicit-none -fbounds-check -fbacktrace",
("gfortran", "release"): "-O3",
("pgfortran", "debug"): "-O0 -g -C -traceback",
("pgfortran", "release"): "-O4"
}
if not GetOption('clean'):
print "\nAvailable Fortran compilers:\n"
for k, v in compilers.compilers_dict().iteritems():
print "%10s : %s" % (k, v)
compiler = raw_input("\nChoose compiler: ")
set_compiler(compiler)
debug_or_release = raw_input("\nDebug or release: ")
set_flags(flags[(compiler, debug_or_release)])
env.Replace(FORTRANMODDIRPREFIX = mod_dir_prefix[compiler])
env.Replace(LINK = compiler)
env.Replace(LINKCOM = "$LINK -o $TARGET $LINKFLAGS $SOURCES $_LIBDIRFLAGS $_LIBFLAGS $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS")
env.Replace(LINKFLAGS = "")
env.Replace(FORTRANMODDIR = '#Mod')
Export('env')
SConscript('Sources/SConscript', variant_dir='Build', duplicate=0)
compilers.py is my own module to find some Fortran compilers which are available.
In Sources folder we have a couple of Fortran source files.
Sources\SConscript
Import('env')
env.Program('app', Glob('*.f90'))
Scons supports Fortran and everything works fine.
gfortran -o Temp\kinds.o -c -O3 -JMod Sources\kinds.f90
gfortran -o Temp\math.o -c -O3 -JMod Sources\math.f90
gfortran -o Temp\sorts.o -c -O3 -JMod Sources\sorts.f90
gfortran -o Temp\utils.o -c -O3 -JMod Sources\utils.f90
gfortran -o Temp\main.o -c -O3 -JMod Sources\main.f90
gfortran -o Temp\app.exe Temp\kinds.o Temp\main.o Temp\math.o Temp\sorts.o Temp\utils.o
scons: done building targets.
After renaming variant_dir name to let say #Bin or #Build we get error message:
gfortran -o Bin\kinds.o -c -O3 -JMod Sources\kinds.f90
gfortran -o Bin\main.o -c -O3 -JMod Sources\main.f90
Sources\main.f90:3.11:
USE sorts
1
Fatal Error: Can't open module file 'sorts.mod' for reading at (1): No such file or directory
Of course the order of compilation matters. But why it depends on variant_dir name? Seems like a bug, but maybe I'm doing something wrong.
P.S. This behavior doesn't depend on duplicate variable value.
P.P.S. Tested with SCons 2.0.1 on Windows with Python 2.7 and Mac OS X with Python 2.5.1.
This is a reply to an old thread, but I had virtually the same problem and needed to dig around for a solution.
Firstly, your build order is probably off because the dependency scanner for Fortran does not work properly. Try running
scons [your_arguments] -n --tree=all | less
which won't actually compile anything but show you the commands and in the end will print the dependency tree as Scons sees it.
A possible solution:
Try adding the line (I added your source for context):
env.Replace(FORTRANMODDIR = '#Mod')
env.Replace(FORTRANPATH = '.' ]
Export('env')
As far as I understand, paths are relative to the "virtual" location of the SConscript file (i.e. the src directory or the variant build directory), this should add the directory containing the source files to the scanner's search path.
In my version of scons (2.3.0), I cannot use the duplicate=0 argument, since it automatically inserts the original source directory into the module path, causing the command line to look like -module build/ -module src/ (ifort) and essentially overriding my preference not to clutter the source directory. This might be a bug, though.