How do I add a linker or compile flag in a CMake file?
Asked Answered
T

8

302

I am using the arm-linux-androideabi-g++ compiler. When I try to compile a simple "Hello, World!" program it compiles fine. When I test it by adding a simple exception handling in that code it works too (after adding -fexceptions .. I guess it is disabled by default).

This is for an Android device, and I only want to use CMake, not ndk-build.

For example - first.cpp

#include <iostream>

using namespace std;

int main()
{
   try
   {
   }
   catch (...)
   {
   }
   return 0;
}

./arm-linux-androideadi-g++ -o first-test first.cpp -fexceptions

It works with no problem...

The problem ... I am trying to compile the file with a CMake file.

I want to add the -fexceptions as a flag. I tried with

set (CMAKE_EXE_LINKER_FLAGS -fexceptions ) or set (CMAKE_EXE_LINKER_FLAGS "fexceptions" )

and

set ( CMAKE_C_FLAGS "fexceptions")

It still displays an error.

Twoseater answered 2/8, 2012 at 18:52 Comment(4)
right now I'm having the same problem, and I'm trying different things. Hang a little and I post an answer. For compile flags, there is an unclean but easy way : add_definitions("-truc")Biblicist
For a more up-to-date discussion on this question (especially if you are using CMake 3.x or newer): What is the modern method for setting general compile flags in CMake?.Storytelling
If the link flags you want aim at configuring rpath then have a look at the specific CMake rpath commands gitlab.kitware.com/cmake/community/wikis/doc/cmake/…Imperforate
see also: https://mcmap.net/q/25272/-cmake-list-append-for-compiler-flags-yields-bogus-resultsForkey
B
303

Please be aware that due to the evolution of CMake since the writing of this answer in 2012, the majority of the recommendations provided here are now obsolete or no longer recommended, with improved alternatives available.


Suppose you want to add those flags (better to declare them in a constant):

SET(GCC_COVERAGE_COMPILE_FLAGS "-fprofile-arcs -ftest-coverage")
SET(GCC_COVERAGE_LINK_FLAGS    "-lgcov")

There are several ways to add them:

  1. The easiest one (not clean, but easy and convenient, and works only for compiler flags, C & C++ at once):

    add_definitions(${GCC_COVERAGE_COMPILE_FLAGS})
    
  2. Appending to corresponding CMake variables:

    SET(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}")
    SET(CMAKE_EXE_LINKER_FLAGS  "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}")
    
  3. Using target properties, cf. doc CMake compile flag target property and need to know the target name.

    get_target_property(TEMP ${THE_TARGET} COMPILE_FLAGS)
    if(TEMP STREQUAL "TEMP-NOTFOUND")
        SET(TEMP "") # Set to empty string
    else()
        SET(TEMP "${TEMP} ") # A space to cleanly separate from existing content
    endif()
    # Append our values
    SET(TEMP "${TEMP}${GCC_COVERAGE_COMPILE_FLAGS}" )
    set_target_properties(${THE_TARGET} PROPERTIES COMPILE_FLAGS ${TEMP} )
    

Right now I use method 2.

Biblicist answered 3/8, 2012 at 14:2 Comment(5)
why is add_definitions() unclean?Swithin
@leinaD_natipaC: The official documentation says: This command can be used to add any flags, but it is intended to add preprocessor definitions. I think that's why.Cosmonautics
While this is the accepted answer this really show very old style CMAKE, refer to the answer by @vitaut for how any new CMAKE code should be structured with regard to compile time parametersKailakaile
string(APPEND CMAKE_EXE_LINKER_FLAGS "new_value") is shorter and cleaner than set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} new_value")Pyroligneous
outdated adviceMaeve
S
252

In newer versions of CMake you can set compiler and linker flags for a single target with target_compile_options and target_link_libraries respectively (yes, the latter sets linker options too):

target_compile_options(first-test PRIVATE -fexceptions)

The advantage of this method is that you can control propagation of options to other targets that depend on this one via PUBLIC and PRIVATE.

As of CMake 3.13 you can also use target_link_options to add linker options which makes the intent clearer.

Spectral answered 27/2, 2015 at 20:7 Comment(12)
I've tried to use it: target_compile_options(main_static PRIVATE --static) but it doesn't seem to work, any idea why?Quaint
-static is probably a linker, not compiler option. Try passing it to target_link_libraries.Spectral
Oh, target_link_libraries I've missed that part of doc: "Specify libraries or flags to use when linking a given target.". Thanks.Quaint
A recent addition: CMake 3.13 introduces target_link_options as a cleaner way to specify linker flags. You should avoid using target_link_libraries for linker flags in the future and use target_link_options instead.Storytelling
There is also add_compile_options - see #39501981 and more recently add_link_optionsMareld
how do you specify multiple linker options?Gladis
@Gladis you pass multiple arguments to target_link_options.Spectral
@BruceAdams But please, never use them in modern target-based CMake.Kozloski
@val I agree, but qualified with an almost. There are some valid uses for global project settings where cmake doens't have them yet. So not for enabling C++11 but maybe for enabling certain sanitisers when debugging.Mareld
While this is better than the accepted answer, it is still missing a generator expression to prevent that flag from being added to an incompatible compiler. But since this flag is only meant to be used in a specific compiler, it should be added to a tool chain file in the variable CMAKE_CXX_FLAGS_INITBooklover
@AlexReinking I wrote an answer involving toolchains. I wasn't aware of the _INIT variants however. ThanksStumpf
You should really not advise OP to use linker-specific flags without ensuring the linker to be used actually accepts them, or alternatively using generator expressions to only add the flag for relevant linkers.Derby
P
59

Try setting the variable CMAKE_CXX_FLAGS instead of CMAKE_C_FLAGS:

set (CMAKE_CXX_FLAGS "-fexceptions")

The variable CMAKE_C_FLAGS only affects the C compiler, but you are compiling C++ code.

Adding the flag to CMAKE_EXE_LINKER_FLAGS is redundant.

Pah answered 2/8, 2012 at 19:27 Comment(4)
i tried that but it still gives error. Is set(CMAKE_CXX_FLAGS "-fexceptions") the only way to specify compiler flag.Twoseater
i solved the problem but not in a good way its a poor workaround .. i made -DCMAKE_CXX_FLAGS= "-fexceptions" in the command line. for some reason cmake is not reading flags from the .cmake file. :( .. thank you sakra for your answer ..Twoseater
-DCMAKE_CXX_FLAGS= "-fexceptions" There should NOT be a space between the = and "Brigitta
Regarding what you said: "for some reason cmake is not reading flags from the .cmake file." Make sure you clear up the existing cache. This can be done by deleting everything from the build directory before cmake again.Texture
S
9

Edit: My general advice is to avoid adding compiler flags explicitly via CMake altogether. Toolchain files are for configuring novel toolchains, not customising an existing GCC toolchain. A project's CMake is more clean, portable, and future-proof when it is toolchain-agnostic. Separate the concern of describing how your artefacts are constituted (using CMake) from the concern of customising compiler flags. I find environment variables, such as CXXFLAGS are much better at maintaining this separation of concerns. However, in answer to the OP...

The preferred way to specify toolchain-specific options is using CMake's toolchain facility. This ensures that there is a clean division between:

  • instructions on how to organise source files into targets -- expressed in CMakeLists.txt files, entirely toolchain-agnostic; and
  • details of how certain toolchains should be configured -- separated into CMake script files, extensible by future users of your project, scalable.

Ideally, there should be no compiler/linker flags in your CMakeLists.txt files -- even within if/endif blocks. And your program should build for the native platform with the default toolchain (e.g. GCC on GNU/Linux or MSVC on Windows) without any additional flags.

Steps to add a toolchain:

  1. Create a file, e.g. arm-linux-androideadi-gcc.cmake with global toolchain settings:

    set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
    set(CMAKE_CXX_FLAGS_INIT "-fexceptions")
    

    (You can find an example Linux cross-compiling toolchain file here.)

  2. When you want to generate a build system with this toolchain, specify the CMAKE_TOOLCHAIN_FILE parameter on the command line:

    mkdir android-arm-build && cd android-arm-build
    cmake -DCMAKE_TOOLCHAIN_FILE=$(pwd)/../arm-linux-androideadi-gcc.cmake ..
    

    (Note: you cannot use a relative path.)

  3. Build as normal:

    cmake --build .
    

Toolchain files make cross-compilation easier, but they have other uses:

  • Hardened diagnostics for your unit tests.

    set(CMAKE_CXX_FLAGS_INIT "-Werror -Wall -Wextra -Wpedantic")
    
  • Tricky-to-configure development tools.

    # toolchain file for use with gcov
    set(CMAKE_CXX_FLAGS_INIT "--coverage -fno-exceptions -g")
    
  • Enhanced safety checks.

    # toolchain file for use with gdb
    set(CMAKE_CXX_FLAGS_DEBUG_INIT "-fsanitize=address,undefined -fsanitize-undefined-trap-on-error")
    set(CMAKE_EXE_LINKER_FLAGS_INIT "-fsanitize=address,undefined -static-libasan")
    
Stumpf answered 21/8, 2020 at 17:30 Comment(5)
See docs for CMAKE_<LANG>_FLAGS_<CONFIG>_INIT and CMAKE_<LANG>_FLAGS_INITBooklover
In general, in CMake, the variables CMAKE_<LANG>_FLAGS[_<CONFIG>] are meant to be set by the client building your app/library. They should never be programatically set. Appended to, rarely.Booklover
@AlexReinking updated, plus CMAKE_EXE_LINKER_FLAGS_INIT.Stumpf
This was the answer, after much searching thank you @JohnMcFarlane !Servomechanism
@AlexReinking passing -DCMAKE_<LANG>_FLAGS to cmake appears to override CMAKE_<LANG>_FLAGS_INIT. For this reason, I'm tempted to use a custom flag and set it explicitly in CMakeLists.txt.Stumpf
D
4

You can also add linker flags to a specific target using the LINK_FLAGS property:

set_property(TARGET ${target} APPEND_STRING PROPERTY LINK_FLAGS " ${flag}")

If you want to propagate this change to other targets, you can create a dummy target to link to.

Decasyllabic answered 11/4, 2018 at 14:29 Comment(0)
G
4

Since CMake 3.13 there is the add_link_options command.

It applies to all subsequent add_library and add_executable commands in the same scope and sub-scopes.

This can be useful for project-wide settings. For example:

add_link_options("-fexceptions")

add_executable(first-test first.cpp)

For specific targets there is target_link_options:

add_executable(first-test first.cpp)

target_link_options(first-test PUBLIC "-fexceptions")

PUBLIC|PRIVATE|INTERFACE as usual determines how the option is propagated to downstream dependencies.

There's also a BEFORE flag for cases when the option needs to be prepended instead of appended.

Gasbag answered 10/3, 2023 at 17:18 Comment(0)
D
1

This worked for me when I needed a precompile definition named "NO_DEBUG":

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14 -DNO_DEBUG")

Then from code

#ifdef NO_DEBUG
.....
Dunt answered 18/4, 2021 at 23:48 Comment(2)
That's what add_definitions is for.Rosetta
Prefer using add_compile_options("-std=c++14") and add_compile_definitions("NO_DEBUG")Reagent
C
-1

With CMake 3.4+, APPEND can be used with the string command to add flags.

string(APPEND CMAKE_EXE_LINKER_FLAGS " -fexceptions")
Carotid answered 3/7, 2022 at 16:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.