How to config cmake for strip file - c++

when I use cmake in Release mode I have the following binary:
64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=485ac09b0a3aa879f88b7f5db6c00ea8d8e1eaf6, not stripped
I want the binary to be stripped.
How can I say to cmake in a clean way to add the -s option to my compiler to make it stripped?
Why did the Default Release mode not strip my binary?

Cleanest possible way is to modify CFLAGS or CXXFLAGS (depending on C or C++ code)
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -s")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")
But there is one more hack if you do not want to change your build system (figuring out exact place where to put above lines might be tricky). You may just use strip as standalone application, like:
strip -s a.out
and do this after executable is ready to release as a post-build step. I found this way cleaner, then disturbing compiler flags.

You can try
set_target_properties(TARGET_NAME PROPERTIES LINK_FLAGS_RELEASE -s)

Using add_link_options() or set_target_properties() to add -s should work fine, additionally, CMake creates an install/strip target which also could be used for striping the binary if you have at least one install() command for your target (reference).
Example:
$ cmake --build . --config Release --target install/strip

This works fine:
add_link_options($<$<CONFIG:RELEASE>:-s>)

Related

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.

How to generate preprocess and assmeble code by cmake?

I am trying to get intermediate .i .s file by CMake when compiling .cpp file, but cmake default only output .o file. Is there any command to manipulate cmake to keep these intermediate file, thanks a lot!
If you are using gcc, try adding this line.
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -save-temps=obj")
Which flag to use depends on the compiler you are using. Also, you should strongly prefer to inject such compiler-and-scenario-specific flags into the build externally, rather than set()-ing them inside the build.
For g++ or clang++, the following invocation would be appropriate:
$ cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_FLAGS="-save-temps=obj"
For MSVC it would be:
> cmake -S . -B build "-DCMAKE_CXX_FLAGS=/FA"

Failed to use gdb for an executive file generated by cmake

To do debug for C++ codes with cmake, I have a trick to add the following lines before the project (myProjectYY) line of the CMakeLists.txt file in the root directory of the source code.
set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall")
So, after cmake and make, I have obtained the executable file mainYY, and I can simply run gdb mainYY to do the debug as I should be able to see the source codes of mainYY.cpp.
I have 2 separated projects that were already cmake and make by the others, namely a simple project (i.e. myProject1) and a complicated project (i.e. myProject2). For myProject1, the above trick works for me, i.e. after I added the above 3 lines and re-do the cmake and make again to obtain main1, I can see the source code of main1.cpp by simply executing l in gdb.
But for myProject2, I do same, i.e. I added the above 3 lines and re-do the cmake and make again to obtain main2, but in gdb there is no source code for main2.cpp. That is, gdb main2 firstly gave me Reading symbols from main2...(no debugging symbols found)...done. And then if I run l in gdb, I have No symbol table is loaded. Use the "file" command.
What are the most probable reasons for such differences between myProject1 and myProject2? How can I find out those probable reasons, and how can I do the fix such that I can debug for myProject2?
Thanks.
As you mentioned, your myProject2 is a complicated project. There could be SET( CMAKE_BUILD_TYPE Release ... FORCE ) somewhere. Check using fgrep -R "CMAKE_BUILD_TYPE" and even better, remove every definition of this everywhere.
Use cmake -DCMAKE_BUILD_TYPE=Debug instead.

DistCC and CMake - select between local and distributed build when running make

My project is build using CMake and is compiled with DistCC + GCC.
I configure the compiler as follows:
SET(CMAKE_C_COMPILER "distcc variation-of-gcc")
To build the project, I simply run 'cmake' and then 'make -jXX'.
Although distcc really speeds up things, I sometimes want to build without distribution - I want it to build locally on the machine.
I know I can modify DISTCC_HOSTS to include only localhost - but this still has the overhead of distcc networking, although it is faster than the overhead for other machines...
I can also do that by rerunning cmake again and modifying the CMAKE_C_COMPILER using customization flags.
But I am looking for a way to do that by just adding a flag directly to 'make'.
I.e.
# This will use distcc:
make -jXX ...
# This will run locally:
make LOCAL_BUILD=1 -jX ...
Is there a CMake trick I can use?
We use the following to allow make time (rather than cmake time) switching on and off of the -Werror flag.
if(CMAKE_GENERATOR STREQUAL "Unix Makefiles")
# TODO: this approach for the WERROR only works with makefiles not Ninja
set(CMAKE_CXX_COMPILE_OBJECT "<CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> $(WERROR) -o <OBJECT> -c <SOURCE>")
endif()
Then we run
make WERROR=-Werror
to turn on warnings as error.
I expect you could do something similar to have whether to use distcc come from a make variable. Like this:
set(CMAKE_CXX_COMPILE_OBJECT "$(USE_DISTCC) <CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -o <OBJECT> -c <SOURCE>")
And then run either
make USE_DISTCC=distcc
or just
make
The simplest thing to do (IMO) is write a little script in your project that invokes the compiler, and change your CMake files to run that script instead of containing the name of the compiler directly:
SET(CMAKE_C_COMPILER "my-gcc-script")
Now you can have that script normally run distcc, but (based on an environment variable or something) also run without distcc. There isn't any need to change anything in your CMake files.

Strange behaviour of ExternalProject_Add with a command containing a space

I need to compile boost libraries with ExternalProject_Add, and the build command needs
c++11 flags on MacOS platform with Clang.
The command should look like this:
./bin/b2 debug release cxxflags="-std=c++11 -stdlib=libc++" linkflags=-stdlib=libc++
But I have a problem with the quotes and space.
set(BOOST_CXX_FLAGS cxxflags="-std=c++11 -stdlib=libc++")
set(BOOST_TOOL_SET toolset=clang ${BOOST_CXX_FLAGS}
linkflags=-stdlib=libc++)
ExternalProject_Add(boost
....
BUILD_COMMAND ./bin/b2 debug release
${BOOST_TOOL_SET}
....
)
The ${BOOST_TOOL_SET} value is a list, and cxxflags="-std=c++11
-stdlib=libc++" is one item in it. The generated command line becomes strange:
./bin/b2 debug release "cxxflags=\"-std=c++11 -stdlib=libc++\""
linkflags=-stdlib=libc++
It seems the flag is translated by CMake when it detected the space inside the argument and wrapped it with quote marks, but it's not what I want.
I searched on the Internet, but haven't found any help. Is there any tip about this issue?
This should work:
set(BOOST_CXX_FLAGS "cxxflags=-std=c++11 -stdlib=libc++")
This should produce
./bin/b2 debug release "cxxflags=-std=c++11 -stdlib=libc++"
Under normal shell parsing rules, this is equivalent to what works for you:
./bin/b2 debug release cxxflags="-std=c++11 -stdlib=libc++"
This doesn't answer your question exactly, as in I can't see a way to get your requested command line to the b2 exe with the cxxflags="-std=c++11 -stdlib=libc++" portion correctly formatted.
However, I believe you can accomplish the desired effect by calling cxxflags= twice. Each argument is appended to the compiler flags eventually invoked by b2.
So you should be able to do:
set(BOOST_CXX_FLAGS cxxflags=-std=c++11 cxxflags=-stdlib=libc++)
and the eventual command invoked by b2 will be something like
"clang++" ... -std=c++11 -stdlib=libc++ ...
To verify this, you can add -d+2 to your command:
BUILD_COMMAND ./bin/b2 debug release ${BOOST_TOOL_SET} -d+2
This causes the full commands to be written to the boost-build-out.log file in your boost-stamp directory.