6

I want to pass linker flags to all sub-projects (sub-directory CMakeList) in my project.

Before switching to new cmake 3.3, I was using the following code (cmake 3.2) which was working well, adding flags for both compilation and linking :

set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -stdlibc++")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -stdlibc++")

With cmake 3.3 this no longer works and set the flags only for compilation step. I updated the CMakeList to use a more "modern" cmake syntax :

set(MY_DEBUG_OPTIONS -g -stdlib=libstdc++ -Wfatal-errors)
set(MY_RELEASE_OPTIONS -O3 -stdlib=libstdc++ -Wfatal-errors)

add_compile_options(
  "$<$<CONFIG:DEBUG>:${MY_DEBUG_OPTIONS}>"
  "$<$<CONFIG:RELEASE>:${MY_RELEASE_OPTIONS}>")

This set compilation flags for all sub-projects, is there a similar way of doing this for linker flags ? I know one can add linker flags on a target basis with target_link_librariescommand but can't find anything else.

I tried using CMAKE_SHARED_LINKER_FLAGS (and corresponding var for exe, module,..) variable with no success.

Update :

It turns out that this has nothing to do with cmake version, things work correctly with CMAKE_CXX_FLAGS_XXXvariables, except on first make command. If one run make a second time (with a modification in CmakeList), flags are presents.

I think I found a solution while testing with a simple CMakeList : if flags are declared after the project command it just work as expected. I don't know if it's a requirement from cmake itself or just a weird behavior.

cmake_minimum_required (VERSION 3.2)

set(PROJECT Test_Project)

# Not working (on first run)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -stdlib=libstdc++ -Wfatal-errors")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -stdlib=libstdc++ -Wfatal-errors")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -stdlib=libstdc++ -Wfatal-errors")

project(${PROJECT})

# Declare here instead...

add_executable(Test test.cpp)

MESSAGE( STATUS "Config flags : " ${CMAKE_CXX_FLAGS_RELEASE})

Using :

cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release .
rma
  • 115
  • 1
  • 1
  • 10
  • I was wondering why your first approach should have stopped working with `CMake` 3.3. So I checked the changes to the code like `CMAKE_CXX_LINK_EXECUTABLE` and `CMAKE_CXX_CREATE_SHARED_LIBRARY` in [`CMakeCXXInformation.cmake`](https://github.com/Kitware/CMake/blame/master/Modules/CMakeCXXInformation.cmake). But there is nothing obvious, so could you please add some details on your targeted make environment? It looks like `GNU`, but what version/flavor/OS do you use? – Florian Sep 21 '15 at 19:46
  • You are right, I was wrong, the problem is here with cmake 3.2 too. I made a very simple "Hello world" project and the problem is : first time I run `make` after `cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release .`, no flags are present apart from cmake default flags (eg. -O3, -DNDEBUG for release build). Then if I update CMakeList file (eg. add a space in flags string), cmake now add all flags correctly. (on os x yosemite with xcode toolchain) – rma Sep 22 '15 at 06:23
  • Ok, then it's the same issue as with [CMake cross-compiling: C flags from toolchain file ignored](http://stackoverflow.com/questions/11423313/cmake-cross-compiling-c-flags-from-toolchain-file-ignored). See [my answer there](http://stackoverflow.com/questions/11423313/cmake-cross-compiling-c-flags-from-toolchain-file-ignored/30217088#30217088) for some background information. In short, just put your variable `set()` commands after the `project()` command. – Florian Sep 22 '15 at 07:19
  • Yes, this is what I ended up doing. Thanks for the background info ! – rma Sep 22 '15 at 07:39
  • You're welcome. I've added an answer to cover the findings, sample code and background information. – Florian Sep 22 '15 at 11:50

1 Answers1

5

Your problems are/were not related to a specific CMake version.

It's the same for all linker/compiler flag variables in CMake. Because those variables are cached variables and set with the project()/enable_language() command (details see here), you either have to

  1. prefill the cache with set(... CACHE ...) before the project() command
  2. generally use the set(... CACHE ... FORCE) to force/overwrite
  3. move the set() after the project() command to hide or append to the cached variables

Here is an example for CMAKE_EXE_LINKER_FLAGS showing all three variants:

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)

# 1. prefill
#set(CMAKE_EXE_LINKER_FLAGS "-Wl,-Map=output.map" CACHE INTERNAL "")

project(Test_Project CXX)

# 2. force
set(CMAKE_EXE_LINKER_FLAGS "-Wl,-Map=output.map" CACHE INTERNAL "" FORCE)

# 3. hide
#set(CMAKE_EXE_LINKER_FLAGS "-Wl,-Map=output.map")
# 3. or append
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Map=output.map")

# TODO: Remove, this is just for testing
file(WRITE "foo.cpp" "int main() {}") 

add_executable(${PROJECT_NAME} foo.cpp)

Whatever the values of those variables are at the end of your any given CMakeLists.txt file will be applied to all corresponding targets in the same CMakeLists.txt file as defaults (see CMAKE - setting compile flags for libraries and What's the CMake syntax to set and use variables?).

The first variant has the disadvantage that it's really only the initial value. The second and third variant would most likely need an if (CMAKE_COMPILER_IS_GNUCXX) around it, so I prefer the second variant with moving those settings to its own initial-cache file:

MyGNUSettings.cmake

set(CMAKE_CXX_FLAGS "-stdlib=libstdc++ -Wfatal-errors" CACHE INTERNAL "" FORCE)
set(CMAKE_CXX_FLAGS_DEBUG "-g" CACHE INTERNAL "" FORCE)
set(CMAKE_CXX_FLAGS_RELEASE "-O3" CACHE INTERNAL "" FORCE)
set(CMAKE_EXE_LINKER_FLAGS "-Wl,-Map=output.map" CACHE INTERNAL "" FORCE)

Using e.g.

cmake -G "Unix Makefiles" -C MyGNUSettings.cmake -DCMAKE_BUILD_TYPE=Release  .

And yes - for the global and per compiler settings - I prefer the global cached variables over the add_compile_options() command. I think add_compile_options() haven't replaced the global variables, it was mainly introduced to prevent people putting compiler options in add_definitions() commands.

Florian
  • 39,996
  • 9
  • 133
  • 149
  • Thanks, very good explanation, I updated my CMakeList to take this into account. Not sure if I should update the question title. – rma Sep 22 '15 at 12:55
  • Maybe just remove the "CMake 3.3" part from the title? – Florian Sep 22 '15 at 13:25
  • I don't think toolchain files should be (ab)used for setting such flags which should probably go into some CMakeLists.txt file. See https://cmake.org/cmake/help/v3.10/manual/cmake-toolchains.7.html where the purpose of toolchain files is only described for cross compilation scenarios, setting things like `CMAKE_SYSTEM_NAME`, `CMAKE_C_COMPILER` etc. – letmaik Dec 25 '17 at 23:06
  • 1
    @letmaik Toolchain files are not only used in cross-compilation scenarios. It can add support for all compiler toolchains not directly supported/identified by CMake. Cross-compilation is defined by adding [`CMAKE_SYSTEM_NAME`](https://cmake.org/cmake/help/latest/variable/CMAKE_SYSTEM_NAME.html). But you are right, I updated my answer to recommend initial-cache files instead of toolchain files in this scenario. – Florian Dec 30 '17 at 20:47