cmake incorrectly escapes bison target option - cmake

Take this minimized example
Critical place:
bison_target(parser
numgrammar.y
${CMAKE_CURRENT_BINARY_DIR}/parser.cc
COMPILE_FLAGS "--defines=${CMAKE_CURRENT_BINARY_DIR}/numgrammar.tab.hh")
Then please create some folder with space inside name like:
> mkdir "test folder" && cd "test folder"
> cmake ${ADVGRAMMAR}
> make VERBOSE=1
You will see something like:
> /usr/bin/bison --defines=/mnt/c/research/test folder/advgrammar/build/numgrammar.tab.hh -d -o ....
You see the problem: space in "test folder" not escaped.
Ok, now lets try to escape:
bison_target(parser
numgrammar.y
${CMAKE_CURRENT_BINARY_DIR}/parser.cc
COMPILE_FLAGS "--defines=\"${CMAKE_CURRENT_BINARY_DIR}/numgrammar.tab.hh\"")
You will see:
/usr/bin/bison --defines=\"/mnt/c/research/test folder/advgrammar/build/numgrammar.tab.hh\" -d -o ....
Now cmake added wrong escape mark for quotes.
Of course no problem if cmake in some neutral-named folder without spaces, but I want any folder.
I tried to google really hard. I tried string with CONFIGURE, I tried generator expressions, I tried this and that and everything and looks like I can't get behavior I want.
I want simply this:
/usr/bin/bison --defines="/mnt/c/research/test folder/advgrammar/build/numgrammar.tab.hh" -d -o ....
Of course I can do it with my own custom target. But I want this with bison_target.
Really need cmake experts help. Any ideas appreciated.

This is because the bison_target macro calls1 separate_arguments on the value of the COMPILE_FLAGS without using the new form that respects native shell rules (it just blindly replaces spaces with semicolons).
Unfortunately, the macro doesn't give you a chance to inject flags in a more modern way, either, so the best I could come up with was to use the variable_watch command to hack at the internals of bison_target, at least until this bug is fixed.
Here's a full example:
cmake_minimum_required(VERSION 3.22)
project(test)
find_package(FLEX REQUIRED)
find_package(BISON REQUIRED)
# Hack around FindBISON's incorrect use of separate_arguments
if (CMAKE_VERSION VERSION_LESS 3.24)
function(patch_flags variable access value ip stack)
set(invalid "${CMAKE_CURRENT_BINARY_DIR}")
separate_arguments(invalid)
string(REPLACE "${invalid}" "${CMAKE_CURRENT_BINARY_DIR}" "${variable}" "${value}")
set("${variable}" "${${variable}}" PARENT_SCOPE)
endfunction()
variable_watch(BISON_TARGET_cmdopt patch_flags)
endif ()
flex_target(scanner
numgrammar.l
"${CMAKE_CURRENT_BINARY_DIR}/lexer.cc"
)
bison_target(
parser
numgrammar.y
"${CMAKE_CURRENT_BINARY_DIR}/parser.cc"
COMPILE_FLAGS "--defines=${CMAKE_CURRENT_BINARY_DIR}/numgrammar.tab.hh"
)
add_flex_bison_dependency(scanner parser)
add_executable(
numgrammar
driver.cc
${BISON_parser_OUTPUTS}
${FLEX_scanner_OUTPUTS}
)
And here's a shell interaction:
$ cmake -G Ninja -S . -B "build with space"
$ cmake --build "build with space" -- -nv # n = dry-run, v = verbose
[1/6] cd "/home/alex/test/build with space" && /usr/bin/bison "--defines=/home/alex/test/build with space/numgrammar.tab.hh" -d -o "/home/alex/test/build with space/parser.cc" /home/alex/test/numgrammar.y
[2/6] cd "/home/alex/test/build with space" && /usr/bin/flex "-o/home/alex/test/build with space/lexer.cc" /home/alex/test/numgrammar.l
[3/6] /usr/bin/c++ -MD -MT CMakeFiles/numgrammar.dir/driver.cc.o -MF CMakeFiles/numgrammar.dir/driver.cc.o.d -o CMakeFiles/numgrammar.dir/driver.cc.o -c /home/alex/test/driver.cc
[4/6] /usr/bin/c++ -MD -MT CMakeFiles/numgrammar.dir/parser.cc.o -MF CMakeFiles/numgrammar.dir/parser.cc.o.d -o CMakeFiles/numgrammar.dir/parser.cc.o -c '/home/alex/test/build with space/parser.cc'
[5/6] /usr/bin/c++ -MD -MT CMakeFiles/numgrammar.dir/lexer.cc.o -MF CMakeFiles/numgrammar.dir/lexer.cc.o.d -o CMakeFiles/numgrammar.dir/lexer.cc.o -c '/home/alex/test/build with space/lexer.cc'
[6/6] : && /usr/bin/c++ CMakeFiles/numgrammar.dir/driver.cc.o CMakeFiles/numgrammar.dir/parser.cc.o CMakeFiles/numgrammar.dir/lexer.cc.o -o numgrammar && :
As you can see the bison rule correctly quotes the spaces.
... /usr/bin/bison "--defines=/home/alex/test/build with space/numgrammar.tab.hh" ...
1. See https://github.com/Kitware/CMake/blob/6b6bdcbb64e1aa2ddac4f09a0807553f5684165a/Modules/FindBISON.cmake#L131 and https://github.com/Kitware/CMake/blob/6b6bdcbb64e1aa2ddac4f09a0807553f5684165a/Modules/FindBISON.cmake#L249

Just in case: there is an option DEFINES_FILE in the bison_target macro. As for me it looks like more portable. You can use it instead of COMPILE_FLAGS.
bison_target(parser
numgrammar.y
${CMAKE_CURRENT_BINARY_DIR}/parser.cc
## COMPILE_FLAGS "--defines=${CMAKE_CURRENT_BINARY_DIR}/numgrammar.tab.hh")
DEFINES_FILE ${CMAKE_CURRENT_BINARY_DIR}/numgrammar.tab.hh
)
It works fine at least on Catalina.
$ cmake -G Ninja -S . -B "build with spaces"
$ cmake --build "build with spaces" -- -nv
[1/6] cd /Users/tonnyred/td && /usr/local/opt/bison/bin/bison "--defines=/Users/tonnyred/td/build with spaces/numgrammar.tab.hh" -o "/Users/tonnyred/td/build with spaces/parser.cc" numgrammar.y
[2/6] cd /Users/tonnyred/td && /usr/local/opt/flex/bin/flex "-o/Users/tonnyred/td/build with spaces/lexer.cc" numgrammar.l
...

Related

How to call a function at start and end of building a target in cmake

I'm looking for a way to execute shell code when starting and finishing the build of a target in cmake. The final goal is to send a message to a data tracking tool indicating when builds start and finish.
So for example, if "make" build targets alpha, beta and gamma, I'd like to call foo_begin() when alpha starts building and foo_end when target alpha is successfully built, and so on for all the targets.
Is this possible?
It is not possible to call arbitrary CMake functions at build time. Once the build system is generated, all the normal variable state and function definitions are discarded (i.e. they are not in cache). The COMMAND argument to add_custom_command takes a shell command, not a CMake function.
However, you can use CMake's script mode, so if what you want is to use CMake as a scripting language just to implement some build hooks, you can do this:
Let's create a file called proc_target.cmake with these contents:
cmake_minimum_required(VERSION 3.23)
if (PRE_BUILD)
message(STATUS "PRE_BUILD: ${TARGET}")
else ()
message(STATUS "POST_BUILD: ${TARGET}")
endif ()
Now in the CMakeLists.txt we can write:
cmake_minimum_required(VERSION 3.23)
project(example)
add_executable(foo main.cpp)
add_custom_command(
TARGET foo PRE_BUILD
COMMAND "${CMAKE_COMMAND}"
-DTARGET=foo
-DPRE_BUILD=1
-P "${CMAKE_CURRENT_SOURCE_DIR}/proc_target.cmake"
)
add_custom_command(
TARGET foo POST_BUILD
COMMAND "${CMAKE_COMMAND}"
-DTARGET=foo
-DPRE_BUILD=0
-P "${CMAKE_CURRENT_SOURCE_DIR}/proc_target.cmake"
)
Then when you run this build, you'll see the following commands:
$ cmake -G Ninja -S . -B build -DCMAKE_BUILD_TYPE=Release
...
$ cmake --build build -- -nv
[1/2] /usr/bin/c++ -O3 -DNDEBUG -MD -MT CMakeFiles/foo.dir/main.cpp.o -MF CMakeFiles/foo.dir/main.cpp.o.d -o CMakeFiles/foo.dir/main.cpp.o -c /path/to/main.cpp
[2/2] cd /path/to/build && /usr/bin/cmake -DTARGET=foo -DPRE_BUILD=1 -P /path/to/proc_target.cmake && cd /path/to/build && /usr/bin/c++ -O3 -DNDEBUG CMakeFiles/foo.dir/main.cpp.o -o foo && cd /path/to/build && /usr/bin/cmake -DTARGET=foo -DPRE_BUILD=0 -P /path/to/proc_target.cmake
You can see how proc_target.cmake is called twice: once just before and once just after invoking the linker for foo. If you want proc_target to run before any of the sources are compiled, then you would want to write:
add_custom_command(
OUTPUT pre-foo.stamp
COMMAND "${CMAKE_COMMAND}"
-DTARGET=foo
-DPRE_BUILD=1
-P "${CMAKE_CURRENT_SOURCE_DIR}/proc_target.cmake"
COMMAND "${CMAKE_COMMAND}" -E touch pre-foo.stamp
DEPENDS "$<TARGET_PROPERTY:foo,SOURCES>"
)
add_custom_target(pre-foo DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/pre-foo.stamp")
add_dependencies(foo pre-foo)
instead of the PRE_BUILD custom command above. Then you would get:
[1/3] cd /path/to/build && /usr/bin/cmake -DTARGET=foo -DPRE_BUILD=1 -P /path/to/proc_target.cmake
[2/3] /usr/bin/c++ -O3 -DNDEBUG -MD -MT CMakeFiles/foo.dir/main.cpp.o -MF CMakeFiles/foo.dir/main.cpp.o.d -o CMakeFiles/foo.dir/main.cpp.o -c /path/to/main.cpp
[3/3] : && /usr/bin/c++ -O3 -DNDEBUG CMakeFiles/foo.dir/main.cpp.o -o foo && cd /path/to/build && /usr/bin/cmake -DTARGET=foo -DPRE_BUILD=0 -P /path/to/proc_target.cmake
And now you can see that the custom target pre-foo is processed before foo.
This is most likely not what you want, however. It's over-engineered. If you want to generate a source file for a target, you should use add_custom_command(OUTPUT) directly and attach the output to the target as a source file (i.e. when you call add_executable or via target_sources).
Per #botje's comment, it seems I need cmake's add_custom_command with build event specifies. I will however need PRE_BUILD, which the documentation informs me is only available for vs-studio, while I am building use cmake & gcc. So I guess I have a new question: how to duplicate the behavior of PRE_BUILD in a cmake/gcc build environment.

Building LLVM with cmake. Does -D_GLIBCXX_USE_CXX11_ABI=0 get overridden?

I try to build LLVM.
This is the way I try …
mkdir -p src/llvm-$(LLVM_VERSION).src/build
cd src/llvm-$(LLVM_VERSION).src/build && \
$(CMAKE) -GNinja \
-DCMAKE_INSTALL_PREFIX=$(BUNDLE) \
-DCMAKE_CXX_FLAGS="-D_GLIBCXX_USE_CXX11_ABI=0 -std=c++11 -static-libgcc -static-libstdc++ -l:libstdc++.a" \
-DLLVM_TARGETS_TO_BUILD="X86;NVPTX" \
-DLLVM_PARALLEL_COMPILE_JOBS=$(BUILD_JOBS) \
-DLLVM_PARALLEL_LINK_JOBS=2 \
-DLLVM_USE_CRT_RELEASE=MD \
-DLLVM_USE_CRT_DEBUG=MDd \
-DLLVM_STATIC=ON \
-DLLVM_INCLUDE_TESTS=OFF \
-DLLVM_INCLUDE_EXAMPLES=OFF \
-DLLVM_BUILD_LLVM_C_DYLIB=OFF \
-DLLVM_ENABLE_TERMINFO=OFF \
-DLLVM_ENABLE_UNWIND_TABLES=OFF \
-DLLVM_ENABLE_RTTI=ON \
.. && \
$(CMAKE) --build . && \
$(CMAKE) --build . --target install
You can see -D_GLIBCXX_USE_CXX11_ABI=0 in -DCMAKE_CXX_FLAGS. After LLVM finished compiling, libLLVM*.a still contain abi:cxx11 symbols.
llvm-config reveals, llvm build process completely ignored "-D_GLIBCXX_USE_CXX11_ABI=0" flag.
> ./bundle/bin/llvm-config --cxxflags
-I/home/leonard/Documents/Develop/build_as_deps/bundle/include -std=c++11 -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
Do you have an idea how to build llvm in a way that disables abi:cxx11 symbols and prevents "-D_GLIBCXX_USE_CXX11_ABI=0" to be overridden?
It looks like you're confusing the cmake options (which are specified with via -D) with C/C++ compiler macro (which are also specified via -D, but to compiler). cmake happily ignored your -D_GLIBCXX_USE_CXX11_ABI, because it's not a cmake option (and you should see a warning about this, that the value is unused by the build system).
Since you need to add extra macro definition, you'd need to change CMAKE_CXX_FLAGS cmake variable.

How to escape $ in cmake?

I am interested in having a line like:
add_link_options(-Wl,-rpath=$ORIGIN)
However, in the executable, the $ gets expanded to $$. E.g.
15:19:52 Debug # readelf -a mybinary | egrep -i runpath
0x000000000000001d (RUNPATH) Library runpath: [$$ORIGIN]
It is possible to get this effect with patchelf after build, but would like to have it in cmake itself if possible.
I have tried things like \$, $$ and "$ORIGIN" without any luck and can't think of anything except 50¢.
This is with cmake 3.13.1.
Edit: Verbose cmake output:
/usr/bin/c++ -g "-Wl,-rpath=\$$ORIGIN" CMakeFiles/mybinary.dir/....
Edit2: This works:
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} "-Wl,-rpath=$ORIGIN")
...
/usr/bin/c++ -g -Wl,-rpath=$ORIGIN CMakeFiles/mybinary.dir/...

CMake and execute_process

Having a little trouble with cmake. I'm working in a weird mode where I need cmake to call an external cmake script to execute multiple commands as part of a test. I've boiled it down to this example.
test.cmake:
message("CMD: " ${CMD})
message("ARG: " ${ARG})
execute_process(COMMAND ${CMD} ${ARG}
RESULT_VARIABLE result
OUTPUT_VARIABLE output
)
message("RESULT: " ${result})
message("OUTPUT: " ${output})
mytest:
cmake -DCMD="cmake" -DARG="-E sleep 10" -V -P ./test.cmake
output:
CMD: cmake
ARG: -E sleep 10
CMake Error: The source directory "/Users/user/-E sleep 10" does not exist.
Specify --help for usage, or press the help button on the CMake GUI.
RESULT: 1
It works fine for all other CMD settings besides CMD=cmake. Any thoughts?
Passing ARG as "-E;sleep;10" works but my higher level project looks like:
project( test NONE)
cmake_minimum_required(VERSION 3.0)
enable_testing()
set(ARG "-E;sleep;-10")
# set(ARG "-E;sleep -10")
# set(ARG "-E sleep -10")
add_test( NAME test
COMMAND ${CMAKE_COMMAND} -DCMD=cmake -DARG=${ARG} -P test.cmake)
And this fails :/
Tony
To address this, you would have to use a different list separator. For example: ^^ would do it.
Note that updating a list with such a separator is also easy in newer version of CMake:
list(JOIN ${ARGS} "^^" ARG)
Calling test.cmake would the be done using:
cmake -DCMD="cmake" -DARG="-E^^sleep^^1" -V -P ./test.cmake
and the test.cmake would be like this:
message("ARG: ${ARG}")
string(REPLACE "^^" ";" ARGS ${ARG})
message("CMD: ${CMD}")
message("ARGS:")
foreach(argument IN LISTS ARGS)
message(STATUS " ${argument}")
endforeach()
execute_process(COMMAND ${CMD} ${ARGS}
RESULT_VARIABLE result
OUTPUT_VARIABLE output
)
message("RESULT: ${result}")
message("OUTPUT: ${output}")
the output looks like this:
ARG: -E^^sleep^^1
CMD: cmake
ARGS:
-- -E
-- sleep
-- 1
RESULT: 0
OUTPUT:

How to create a makefile for a Fortran program using modules

The challenge is to create a makefile which takes a list of modules and does not require me to sort out precendence. For example, the modules are
mod allocations.f08 mod precision definitions.f08 mod unit values.f08
mod blocks.f08 mod shared.f08 mod parameters.f08
mod timers.f08
The main program is characterize.f08. The error message is
Fatal Error: Can't open module file ‘mprecisiondefinitions.mod’ for reading at (1): No such file or directory
The first statement in the main program is use mPrecisionDefinitions, the module defined in mod precision definitions.f08.
The following makefile, based upon Creating a FORTRAN makefile, is:
# compiler
FC := /usr/local/bin/gfortran
# compile flags
FCFLAGS = -g -c -Wall -Wextra -Wconversion -Og -pedantic -fcheck=bounds -fmax-errors=5
# link flags
FLFLAGS =
# source files and objects
SRCS = $(patsubst %.f08, %.o, $(wildcard *.f08))
# program name
PROGRAM = a.out
all: $(PROGRAM)
$(PROGRAM): $(SRCS)
$(FC) $(FLFLAGS) -o $# $^
%.mod: %.f08
$(FC) $(FCFLAGS) -o $# $<
%.o: %.f08
$(FC) $(FCFLAGS) -o $# $<
clean:
rm -f *.o *.mod
For starters, I recommend to replace all spaces in your file names with underscores or something similar.
Spaces are almost universally used as separators, and any program that is started with something like
gfortran -c -o mod precision definitions.o mod precision definitions.f08
would interpret this line as 'create an object file called mod from the source files precision, definitions.o, mod, precision, and definitions.f08. And while there are ways to do it, with increasing automation, you have to jump more and more hoops.
In contrast, this works well:
gfortran -c -o mod_precision_definitions.o mod_precision_definitions.f08
I would use this command to change all the spaces into underscores:
rename 's/ /_/g' *.f08
If that doesn't work, use this command:
for f in *.f08; do mv "$f" ${f// /_}; done
Next, I wouldn't worry about .mod files. They get generated together with the object files when you compile a module. So while technically some routine that uses a module requires the .mod file for that module, you might as well claim in your Makefile that it depends on the object file itself.
So with that said, here's the Makefile I would use (with some assumed inter-module dependencies added):
# Find all source files, create a list of corresponding object files
SRCS=$(wildcard *.f08)
OBJS=$(patsubst %.f08,%.o,$(SRCS))
# Ditto for mods (They will be in both lists)
MODS=$(wildcard mod*.f08)
MOD_OBJS=$(patsubst %.f08,%.o,$(MODS))
# Compiler/Linker settings
FC = gfortran
FLFLAGS = -g
FCFLAGS = -g -c -Wall -Wextra -Wconversion -Og -pedantic -fcheck=bounds -fmax-errors=5
PROGRAM = characterize
PRG_OBJ = $(PROGRAM).o
# make without parameters will make first target found.
default : $(PROGRAM)
# Compiler steps for all objects
$(OBJS) : %.o : %.f08
$(FC) $(FCFLAGS) -o $# $<
# Linker
$(PROGRAM) : $(OBJS)
$(FC) $(FLFLAGS) -o $# $^
# If something doesn't work right, have a 'make debug' to
# show what each variable contains.
debug:
#echo "SRCS = $(SRCS)"
#echo "OBJS = $(OBJS)"
#echo "MODS = $(MODS)"
#echo "MOD_OBJS = $(MOD_OBJS)"
#echo "PROGRAM = $(PROGRAM)"
#echo "PRG_OBJ = $(PRG_OBJ)"
clean:
rm -rf $(OBJS) $(PROGRAM) $(patsubst %.o,%.mod,$(MOD_OBJS))
.PHONY: debug default clean
# Dependencies
# Main program depends on all modules
$(PRG_OBJ) : $(MOD_OBJS)
# Blocks and allocations depends on shared
mod_blocks.o mod_allocations.o : mod_shared.o