Cmake doesn't honour -D CMAKE_CXX_COMPILER=g++ - c++

I'm trying to force cmake to build my cpp code with g++, as by default it uses clang instead. So I use: cmake -D CMAKE_CXX_COMPILER=g++ ../src/CMakeLists.txt after which cmake checks for gcc and g++ (with success), but nonetheless make VERBOSE=1 yields
/usr/bin/c++ -o CMakeFiles/trial_cpp.dir/trial.cpp.o -c "/Users/Kuba/Code/Sketchpad/Trial project/src/trial.cpp"
Linking CXX executable trial_cpp
/opt/etlocal/bin/cmake -E cmake_link_script CMakeFiles/trial_cpp.dir/link.txt --verbose=1
/usr/bin/c++ -Wl,-search_paths_first -Wl,-headerpad_max_install_names CMakeFiles/trial_cpp.dir/trial.cpp.o -o trial_cpp
As it calls /usr/bin/c++ not /usr/bin/g++ I concur it still uses clang. Any idea what's the problem? I know I have g++ and it's in /usr/bin/.
I'm running Mac OS X 10.8.2

CMAKE_CXX_COMPILER can only be set the first time cmake is run in a given build directory. On subsequent runs it is ignored. In order to change CMAKE_CXX_COMPILER you first need to delete the contents of the build directory and then run cmake again with that option.
Source: http://www.cmake.org/Wiki/CMake_Useful_Variables
I believe the reasoning for only using that variable on the first run is because changing it later would potentially invalidate everything already built including the configuration checks so cmake would have to start from scratch anyway.

I do it like this instead:
CXX=/usr/bin/g++ cmake ../src/CMakeLists.txt

Related

How should I setup script-based source generation with CMake?

I'm managing the build of a certain C++ repository using CMake. In this repository, and among other things, I have a bunch of .hpp files in a directory, and for each of these, I need to compile a generated bit of source code, which depends on its contents.
Naively, I would do this by generating a correspond .cpp file, and include that file in the source files of the CMake target for the library or executable I'm building. Now, since I don't actually need the source files themselves, I could theoretically just arrange for the compiler to get its source from the command-line instead.
My question: How would I set up this source generation and compilation, using CMake as idiomatically as possible?
Notes:
Assume I have, say, a bash script which can read the .hpp and generate the .cpp on the standard output.
CMake version 3.24 or whichever you like.
Should work on Unix-like operating systems, and hopefully on Windows-like OSes other than the fact that the bash script will fail.
Please comment if additional information is necessary to answer my question.
Let's assume for the sake of portability that you have a Python script, rather than a bash script that manages your code generation. Let's say that it takes two arguments: the source .hpp file and the destination .cpp file. We'll assume it is in your source tree under ./tools/codegen.py.
Now let's assume that your .hpp files are in ./src/genmod for "generated module" because the sources for these headers are generated by codegen.py.
Finally, we'll assume there's a final executable target, app, with a single source file, ./src/main.cpp.
Here's a minimal build that will work for this, with some step-by-step discussion.
We start with some boring boilerplate.
cmake_minimum_required(VERSION 3.24)
project(example)
This probably works on earlier versions, I just haven't tested it, so YMMV. Now we'll create the executable target and link it to our generated sources preemptively. Note that the dependent target does not need to exist before calling target_link_libraries.
add_executable(app src/main.cpp)
target_link_libraries(app PRIVATE genmod)
Now we'll find a Python interpreter and write down an absolute path to the codegen tool.
find_package(Python3 REQUIRED)
set(codegen_py "${CMAKE_CURRENT_SOURCE_DIR}/tools/codegen.py")
Next we'll construct the list of input headers. I'm imagining there are three: A.hpp, B.hpp, and C.hpp.
set(input_headers A.hpp B.hpp C.hpp)
list(TRANSFORM input_headers PREPEND src/genmod/)
I used list(TRANSFORM) here to save some typing. Now we'll just create an object library called genmod, which will "hold" the objects for the generated C++ files.
add_library(genmod OBJECT)
And now comes the real meat. For each of the headers, ...
foreach (header IN LISTS input_headers)
we'll construct absolute paths to the header and generated source files ...
string(REGEX REPLACE "\\.hpp$" ".cpp" gensrc "${header}")
set(header "${CMAKE_CURRENT_SOURCE_DIR}/${header}")
set(gensrc "${CMAKE_CURRENT_BINARY_DIR}/${gensrc}")
and then write a custom command that knows how to call codegen.py. We specify the outputs, command arguments, and dependencies. Don't forget to include the generator script as a dependency, and never forget to pass VERBATIM to ensure consistent, cross-platform, argument quoting.
add_custom_command(
OUTPUT "${gensrc}"
COMMAND Python3::Interpreter "${codegen_py}" "${header}" "${gensrc}"
DEPENDS "${header}" "${codegen_py}"
VERBATIM
)
Finally, we attach this source to genmod.
target_sources(genmod PRIVATE "${gensrc}")
endforeach ()
We can test this build using Ninja's dry-run feature to make sure the commands look correct.
$ cmake -G Ninja -S . -B build -DCMAKE_BUILD_TYPE=Release
-- The C compiler identification is GNU 10.2.1
-- The CXX compiler identification is GNU 10.2.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found Python3: /home/reinking/.venv/default/bin/python3.9 (found version "3.9.2") found components: Interpreter
-- Configuring done
-- Generating done
-- Build files have been written to: /home/reinking/test/build
$ cmake --build build -- -nv
[1/8] cd /home/reinking/test/build && /home/reinking/.venv/default/bin/python3.9 /home/reinking/test/tools/codegen.py /home/reinking/test/src/genmod/A.hpp /home/reinking/test/build/src/genmod/A.cpp
[2/8] cd /home/reinking/test/build && /home/reinking/.venv/default/bin/python3.9 /home/reinking/test/tools/codegen.py /home/reinking/test/src/genmod/B.hpp /home/reinking/test/build/src/genmod/B.cpp
[3/8] cd /home/reinking/test/build && /home/reinking/.venv/default/bin/python3.9 /home/reinking/test/tools/codegen.py /home/reinking/test/src/genmod/C.hpp /home/reinking/test/build/src/genmod/C.cpp
[4/8] /usr/bin/c++ -O3 -DNDEBUG -MD -MT CMakeFiles/genmod.dir/src/genmod/A.cpp.o -MF CMakeFiles/genmod.dir/src/genmod/A.cpp.o.d -o CMakeFiles/genmod.dir/src/genmod/A.cpp.o -c /home/reinking/test/build/src/genmod/A.cpp
[5/8] /usr/bin/c++ -O3 -DNDEBUG -MD -MT CMakeFiles/genmod.dir/src/genmod/B.cpp.o -MF CMakeFiles/genmod.dir/src/genmod/B.cpp.o.d -o CMakeFiles/genmod.dir/src/genmod/B.cpp.o -c /home/reinking/test/build/src/genmod/B.cpp
[6/8] /usr/bin/c++ -O3 -DNDEBUG -MD -MT CMakeFiles/genmod.dir/src/genmod/C.cpp.o -MF CMakeFiles/genmod.dir/src/genmod/C.cpp.o.d -o CMakeFiles/genmod.dir/src/genmod/C.cpp.o -c /home/reinking/test/build/src/genmod/C.cpp
[7/8] /usr/bin/c++ -O3 -DNDEBUG -MD -MT CMakeFiles/app.dir/src/main.cpp.o -MF CMakeFiles/app.dir/src/main.cpp.o.d -o CMakeFiles/app.dir/src/main.cpp.o -c /home/reinking/test/src/main.cpp
[8/8] : && /usr/bin/c++ -O3 -DNDEBUG CMakeFiles/genmod.dir/src/genmod/A.cpp.o CMakeFiles/genmod.dir/src/genmod/B.cpp.o CMakeFiles/genmod.dir/src/genmod/C.cpp.o CMakeFiles/app.dir/src/main.cpp.o -o app && :
And indeed we can see that the commands are what we'd naturally expect them to be.

CMake toolchain file - setting CMAKE_CXX_FLAGS

I have seen the following way of setting CMAKE_CXX_FLAGS in the toolchain file:
SET(CMAKE_CXX_FLAGS "-m32" CACHE STRING "C++ compiler flags" FORCE)
Should I use it in the toolchain file instead of
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32")
?
What are differences between them ?
tl;dr: there are two acceptable ways of doing this.
First, and most of the time (90%+), you can use the _INIT variables as suggested by the documentation:
set(CMAKE_CXX_FLAGS_INIT "-m32")
Second, if CMake is adding incorrect/conflicting flags for your compiler/platform combination, you can override it completely by setting the cache variable without FORCE.
set(CMAKE_CXX_FLAGS "-m32" CACHE STRING "C++ compiler flags")
Read on for further details.
Let's run a few experiments. We'll use the following CMakeLists.txt:
cmake_minimum_required(VERSION 3.23)
project(test LANGUAGES CXX)
message(STATUS "CMAKE_CXX_FLAGS_DEBUG = ${CMAKE_CXX_FLAGS_DEBUG}")
On most systems, CMake leaves CMAKE_CXX_FLAGS blank by default. The main exception is Windows with MSVC, where it adds /EHsc and (on older versions) /GR to ensure that standard C++ exception handling and RTTI are enabled.
Since I don't have ready access to a Windows system, I use CMAKE_CXX_FLAGS_DEBUG, which does have default-initialized flags on most compilers. The same principles apply, though, since it is the responsibility of the platform module to set these in both cases.
Experiment 1: No toolchain file
$ cmake -S . -B build
-- The CXX compiler identification is GNU 11.2.0
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- CMAKE_CXX_FLAGS_DEBUG = -g
-- Configuring done
-- Generating done
-- Build files have been written to: /path/to/build
So on this compiler, CMAKE_CXX_FLAGS_DEBUG is set to -g. This is our baseline.
Experiment 2: Set-cache with force
Now we'll create a toolchain file called set-cache-force.cmake:
# set-cache-force.cmake
set(CMAKE_CXX_FLAGS_DEBUG "-DMY_DEBUG" CACHE STRING "C++ compiler flags" FORCE)
We'll configure the project with this toolchain:
$ rm -rf build
$ cmake -S . -B build --toolchain set-cache-force.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
As we can see, the original -g flag was suppressed and the -DMY_DEBUG cache value "won". Of course, this isn't really a debug mode anymore, which should illustrate why overriding all the flags isn't always what we want.
Even worse, using FORCE here disables a user's ability to override CMAKE_CXX_FLAGS_DEBUG themselves:
$ rm -rf build
$ cmake -S . -B build --toolchain set-cache-force.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
This is highly undesirable behavior. A user would need to edit your toolchain file to work around a bug or add further customizations.
Experiment 3: Set-cache without force
If we run the same experiment as before without FORCE setting it, then we still get the same flags, but we retain the ability to incrementally override the toolchain file.
# set-cache.cmake
set(CMAKE_CXX_FLAGS_DEBUG "-DMY_DEBUG" CACHE STRING "C++ compiler flags")
Now we can see that it works:
$ rm -rf build
$ cmake -S . -B build --toolchain set-cache.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
And that it can still be overridden:
$ rm -rf build
$ cmake -S . -B build --toolchain set-cache.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DOVERRIDE
...
And it can even be overridden again:
$ cmake -S . -B build -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE2"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DOVERRIDE2
...
Experiment 4: Set normal variable
Now we'll try to set this as a normal variable. Again, we'll create a toolchain file called set-normal.cmake:
# set-normal.cmake
set(CMAKE_CXX_FLAGS_DEBUG "-DMY_DEBUG")
Again, running this shows that -DMY_DEBUG "wins", overriding CMake's default flags:
$ cmake -S . -B build --toolchain set-normal.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
Like experiment 2, this prevents users from overriding it... bad!
$ cmake -S . -B build --toolchain set-normal.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG
...
Experiment 5: Append normal variable
Now we'll try with the code in your post. Again, we'll use a toolchain called append-normal.cmake:
# append-normal.cmake
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DMY_DEBUG")
Now we get a very different result:
$ rm -rf build
$ cmake -S . -B build --toolchain append-normal.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG -DMY_DEBUG
...
This is just completely wrong! What happened here? Well, the toolchain file gets read multiple times during project initialization, and here this causes the -DMY_DEBUG flag to be appended twice. At least that's what happens on the first run:
$ cmake -S . -B build
...
-- CMAKE_CXX_FLAGS_DEBUG = -g -DMY_DEBUG
...
After the first run, the CMake default gets cached and so we append to that on subsequent runs. Furthermore, CMake only reads your toolchain file once now.
You must always make your toolchain files idempotent. That means that running it twice does the same thing as running it once.
Experiment 6: Using _INIT variables
This is the developer-intended way of doing things per the documentation. See the documentation here: https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_FLAGS_INIT.html
Value used to initialize the CMAKE_<LANG>_FLAGS cache entry the first time a build tree is configured for language <LANG>. This variable is meant to be set by a toolchain file. CMake may prepend or append content to the value based on the environment and target platform.
Now we use a toolchain file called init-var.cmake:
# init-var.cmake
set(CMAKE_CXX_FLAGS_DEBUG_INIT "-DMY_DEBUG")
And we re-run the build:
$ rm -rf build
$ cmake -S . -B build --toolchain init-var.cmake
...
-- CMAKE_CXX_FLAGS_DEBUG = -DMY_DEBUG -g
...
Now we can see that CMake appended its default flags to the initial ones we provided. And indeed this still allows users to override things:
$ cmake -S . -B build --toolchain init-var.cmake -DCMAKE_CXX_FLAGS_DEBUG="-DOVERRIDE"
...
-- CMAKE_CXX_FLAGS_DEBUG = -DOVERRIDE
...
In my experience, 90%+ of the time it's correct to let CMake add in its extra flags using Experiment 6 (the _INIT variables). But every so often you'll want to completely override CMake using Experiment 3 (set(CACHE) without FORCE).
What you do not want to do is anything that behaves differently on subsequent runs (like experiment 5) or that disables key CMake functionality (ie. respecting the cache variable, like experiments 2 and 4).
When you use set(variable "value" CACHE STRING "..." FORCE), the variable is set for all the projects built in the current session (including those that are in the sub-directories).
But simply using set(variable "value") without the cache part only adds the flags for the immediate project scope (current CMakeLists.txt) and not the upper directories that have their own CMakeLists.txt.

Set GCC path in makefile

Whenever I am building my package it uses /usr/bin/g++ (system compiler).
I want to build my package with C++11 constructs. I have tried -std=c++11 option but with system compiler it says unrecognized option.
I want to build my package from a different gcc compiler which will get downloaded as part of my package dependency.
So, how can I specify the location of gcc compiler in Makefile?
There are multiple ways to achieve what you are looking for:
Setting the environment variable CXX just for the process that will run make:
$ CXX=/path-to-your-compiler/g++ make
Exporting the environment variable CXX in your shell:
$ CXX=/path-to-your-compiler/g++
$ export CXX
$ make
Setting CXX at make's command-line:
$ make CXX=/path-to-your-compiler/g++
Inside your makefile:
CXX := /path-to-your-compiler/g++
Note that setting the variable at make's command line overrides the other values, and variables set inside the makefile override the ones obtained from the environment (unless the command-line option -e or --environment-overrides is provided).
Inside your makefile, you can still override any value set by other means by using the override directive:
override CXX := /path-to-your-compiler/g++

Cmake cannot find library

I have a library called Kinova.API.CommLayerUbuntu.so which I want to link against with cmake. So, in my CMakeLists.txt file, I have the line:
target_link_libraries(demo ~/Libraries/Kinova.API.CommLayerUbuntu.so)
However, during compilation, I receive the error:
cannot find -lKinova.API.CommLayerUbuntu
This baffles me because I am telling cmake to look for Kinova.API.CommLayerUbuntu.so, not for -lKinova.API.CommLayerUbuntu. Please could somebody explain what is going on?
In verbose mode, the cmake output gives the following:
Linking CXX executable demo
/usr/bin/cmake -E cmake_link_script CMakeFiles/demo.dir/link.txt --verbose=1
/usr/bin/c++ CMakeFiles/demo.dir/demo.cpp.o -o demo -L/home/karnivaurus/Libraries -rdynamic -lKinova.API.CommLayerUbuntu -Wl,-rpath,/home/karnivaurus/Libraries
/usr/bin/ld: cannot find -lKinova.API.CommLayerUbuntu
The -l is just the flag used to tell the compiler to link a library. Your library name is nonstandard. The compiler will remove the -l and prepend lib to the string you use, and look for a file called libKinova.API.CommLayerUbuntu.so.
This looks like an issue with the distribution of this API. You can try working around it by creating a symbolic link from libKinova.API.CommLayerUbuntu.so to Kinova.API.CommLayerUbuntu.so.

Trouble building tests with Google Test, Clang, and libc++

I tried building Google Test with the following CMake configuration:
$ CMAKE_CXX_COMPILER="clang++" CMAKE_CXX_FLAGS="-std=c++11 -stdlib=libc++ -U__STRICT_ANSI__" cmake ../source
Building shows CMake picked the right compiler, but my compiler flags aren't passing through:
$ VERBOSE=1 make
...
/Users/jfreeman/local/bin/clang++ -I/Users/jfreeman/work/googletest/source/include -I/Users/jfreeman/work/googletest/source -DGTEST_HAS_PTHREAD=1 -o CMakeFiles/gtest.dir/src/gtest-all.cc.o -c /Users/jfreeman/work/googletest/source/src/gtest-all.cc
...
/Users/jfreeman/local/bin/clang++ -I/Users/jfreeman/work/googletest/source/include -I/Users/jfreeman/work/googletest/source -DGTEST_HAS_PTHREAD=1 -o CMakeFiles/gtest_main.dir/src/gtest_main.cc.o -c /Users/jfreeman/work/googletest/source/src/gtest_main.cc
The end goal is that I want my project, which builds with Clang and libc++, to have tests built with Google Test. That means I need Google Test built with libc++ as well.
Using variables on the command line with CMake sometimes requires the -D (for Define) flag.
$ cmake -DCMAKE_CXX_COMPILER="clang++" -DCMAKE_CXX_FLAGS="-std=c++11 -stdlib=libc++ -U__STRICT_ANSI__" ../source