How to escape $ in cmake? - 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/...

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.

cmake incorrectly escapes bison target option

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
...

Using hiredis (redis client library)

I have the redis server installed, and can use it from the command line. Now, I am wanting to write a client program using hiredis. To begin with, I tried to compile example.c which is present in the hiredis directory:
vishal#expmach:~/redis-2.6.14/deps/hiredis$ ls
adapters async.h COPYING dict.h *example.c* example-libevent.c
hiredis.c Makefile net.h sds.c test.c async.c CHANGELOG.md dict.c example-
ae.c example-libev.c fmacros.h hiredis.h net.c README.md sds.h
Here are the commands:
vishal#expmach:~/redis-2.6.14/deps/hiredis$ gcc -c -I hiredis example.c
vishal#expmach:~/redis-2.6.14/deps/hiredis$ gcc -o example -I hiredis -L hiredis -lhiredis -lm
/usr/bin/ld: cannot find -lhiredis
collect2: ld returned 1 exit status
I am not sure how to go about fixing this. Please help.
gcc -o example example.c -lhiredis $(pkg-config --cflags --libs glib-2.0)
why don't you juste juste the provided Makefile?
make
./hiredis-example

Unbound modules in OCaml

My problem is that ocamlc and ocamlopt apear to be refusing to find third party libraries installed through apt-get. I first started having this problem when I tried to incorporate third-party modules into my own OCaml programs, and quickly wrote it off as a personal failing in understanding OCaml compilation. Soon-- however-- I found myself running into the same problem when trying to compile other peoples projects under their own instructions.
Here is the most straight-forward example. The others all use ocamlbuild, which obfuscates things a little bit.
The program: http://groups.google.com/group/fa.caml/msg/5aee553df34548e2
The compilation:
$ocamlc -g -dtypes -pp camlp4oof -I +camlp4 dynlink.cma camlp4lib.cma -cc g++ llvm.cma llvm_bitwriter.cma minml.ml -o minml
File "minml.ml", line 43, characters 0-9:
Error:Unbound module Llvm
Even when I provide ocamlc with the obsolute paths to the llvm files, like so...
$ ocamlc -g -dtypes -pp camlp4oof -I +camlp4 dynlink.cma camlp4lib.cma -cc g++ /usr/lib/ocaml/llvm-2.7/llvm.cma /usr/lib/ocaml/llvm-2.7/llvm_bitwriter.cma minml.ml -o minml
... to no avail.
What am I doing wrong?
Your command is doing two things: it's compiling minml.ml (into minml.cmo), then linking the resulting object into minml.
Compiling a module requires the interfaces of the dependencies. The interfaces contain typing information that is necessary to both the type checker and the code generator; this information is not repeated in the implementation (.cma here). So for the compilation stage, llvm.cmi must be available. The compiler looks for it in the include path, so you need an additional -I +llvm-2.7 (which is short for -I /usr/lib/ocaml/llvm-2.7).
The linking stage requires llvm.cma, which contains the bytecode implementation of the module. Here, you can either use -I or give a full path to let ocamlc know where to find the file.
ocamlc -g -dtypes -I +camlp4 -I +llvm-2.7 -pp camlp4oof -c minml.ml
ocamlc -g -cc g++ -I +camlp4 -I +llvm-2.7 dynlink.cma camlp4lib.cma llvm.cma llvm_bitwriter.cma minml.cmo -o minml
or if you want to do both stages in a single command:
ocamlc -g -dtypes -cc g++ -I +camlp4 -I +llvm-2.7 dynlink.cma camlp4lib.cma llvm.cma llvm_bitwriter.cma -pp camlp4oof minml.ml -o minml

G++ -I option for compiling program

Here is a little problem that cannot be resolved by me such a Linux program newbie.
Now I have a main.cpp program which need to be compiled, there is a
#include "Down.h"
in the front of file.
Actually, this header file exist in the other directory, which locates at
../../../include
directory. Besides, some other header files needed by Down.h also locate at this ../../../include directory.
Here is the problem, I compile main.cpp with command
g++ -I /../../../include main.cpp
However, it gives lots of error info which means it is not correct to be done like this.
Should I also change the include declaration into this one?
#include "../../../include/DownConvert.h"
May you please leave me with some advice? Thanks.
Edit:
After using g++ -I ../../../include main.cpp, I get the following errors:
$ g++ -I ../../../include main.cpp
In file included from ../../../include/DownConvert.h:98,
from main.cpp:92: ../../../include/ResizeParameters.h:4:22: error:
TypeDefs.h: No such file or directory
In file included from /usr/include/c++/4.4/bits/stl_algo.h:61,
from /usr/include/c++/4.4/algorithm:62,
from ../../../include/H2
g++ -I /../../../include main.cpp
See that leading slash after the -I? That's an absolute path.
Change it to a relative path (shown below) and it'll work OK.
g++ -I ../../../include main.cpp
g++ -I ../../../include main.cpp
ought to work
Try to use -v option:
g++ -v -I ../../../include main.cpp
And check that list of directories to search for include files contains your folder and there is no complains that this folder is absent. If there is this sort of complains correct the path that you give after -I