CMake: propagate compile options project-wide - c++

Lets say I want to compile all the code with /W4 for a project with three libs/targets.
A -> B -> C
What is the best practice to apply the flag project-wide?
I can think of two approaches:
Set TARGET_COMPILE_OPTIONS(C PUBLIC "\W4") in C's CMake (which is a core library for the whole project) and every other library that depends on C will inherit the flag via: TARGET_LINK_LIBRARIES(B C)
Pro: new libraries will inherit the flag automatically.
Con: compile flags for a project are implicit.
Specify compile options for every target/lib separately.
Pro: the flags are explicitly specified and manageable separately for each lib.
Con: the flags need to be (not forgotten to be) set for a new lib.

Third option, change the compiler flags.
For instance, when I want to activate address sanitizer on the whole project, I do:
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address -static-libasan")
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fsanitize=address -static-libasan")
The current idiomatic way of setting flags for the current folder and subfolder is to use
add_compile_options(-fsanitize=address)

Related

Compiling library with different flags than main code

My project includes an external library (HPTT) that needs to be compiled and linked with the main part of the code. At the moment, I compile both HPTT and my own source code together with the same compiler flags, using the following CMake file:
cmake_minimum_required(VERSION 2.6)
project(custom_tthresh)
# Default settings
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-unknown-pragmas") # -Wno-unknown-pragmas ignores unknown OpenMP pragma's without warnings.
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
file(GLOB SRC "src/*.cpp")
file(GLOB HPTT "src/hptt/*.cpp")
add_executable(experiments ${SRC} ${HPTT})
target_include_directories(experiments PRIVATE /usr/include/eigen3/ src/include/)
add_definitions(-std=c++14)
However, I have two issues with this setup:
HPTT takes a long time to compile, so whenever I change any flags for my own code, I have to wait a lot for it to recompile.
HPTT gives me a bunch of warnings, especially with the -Wall -Wextra -Wno-unknown-pragmas flags, which I'd like to keep for my own code.
How can I set up my CMake file so that it compiles both the library and my own code separately, using different compiler flags, and then links them together? I'd like to stick to some static settings for HPTT (always in release mode, less/no warnings, ...). For full information, these are the current locations of the relevant files:
My own header and source files are in src/
HPTT headers are in src/include/ (this directory needs to be included for the HPTT source files to compile)
HPTT source files are in src/hptt/
Update: Thanks for all the advice. I updated my CMake file now:
cmake_minimum_required(VERSION 3.7)
project(custom_tthresh)
# Always compile external dependencies in Release mode
# We use the custom flag CUSTOM_TTHRESH_BUILD_TYPE to determine the build type for our own library and its related executables
set(CUSTOM_TTHRESH_BUILD_TYPE Release FORCE)
# HPTT
set(HPTT_SRCS src/hptt/hptt.cpp src/hptt/plan.cpp src/hptt/transpose.cpp src/hptt/utils.cpp)
add_library(hptt STATIC ${HPTT_SRCS})
target_include_directories(hptt PRIVATE src/include)
target_compile_options(hptt PRIVATE -w)
# Custom TTHRESH
set(CUSTOM_TTHRESH_SRCS
src/compress.cpp
src/CompressedIO.cpp
src/Compressor.cpp
src/DataBuffer.cpp
src/decompress.cpp
src/quantize.cpp
src/Sizes.cpp
src/Slice.cpp
src/st_hosvd.cpp
)
add_library(custom_tthresh STATIC ${CUSTOM_TTHRESH_SRCS})
target_include_directories(custom_tthresh PRIVATE /usr/include/eigen3/)
target_link_libraries(custom_tthresh hptt)
target_compile_options(custom_tthresh PRIVATE -Wall -Wextra -Wno-unknown-pragmas)
if(CUSTOM_TTHRESH_BUILD_TYPE EQUAL Release)
target_compile_options(custom_tthresh PRIVATE -O3 -DNDEBUG)
else()
target_compile_options(custom_tthresh PRIVATE -g)
endif()
set_target_properties(custom_tthresh PROPERTIES
CXX_STANDARD 14
CXX_STANDARD_REQUIRED ON
)
# Experiments
add_executable(experiments src/experiments.cpp)
target_link_libraries(experiments custom_tthresh)
target_compile_options(experiments PRIVATE -Wall -Wextra -Wno-unknown-pragmas)
if(CUSTOM_TTHRESH_BUILD_TYPE EQUAL Release)
target_compile_options(custom_tthresh PRIVATE -O3 -DNDEBUG)
else()
target_compile_options(custom_tthresh PRIVATE -g)
endif()
This seems to address my problems, avoids some of the bad practices pointed out below and actually reflects the structure of the project. I'm still not proud of the use of CUSTOM_TTHRESH_BUILD_TYPE (based on this question), however I couldn't find a better solution.
Use target_compile_options() to set flags per target:
target_compile_options(experiments PRIVATE "-Wall -Wextra -Wno-unknown-pragmas")
Additionally, don't set flags globally because it sets the flag for everything in the source tree. Don't do this:
set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-unknown-pragmas") # don' do this
set(CMAKE_CXX_FLAGS_DEBUG "-g") # especially this
set(CMAKE_CXX_FLAGS_RELEASE "-O3") # and this
Another bad practice is using file globbing. Read Why is cmake file GLOB evil?
file(GLOB SRC "src/*.cpp")
file(GLOB HPTT "src/hptt/*.cpp") #avoid this
And from the cmake docs:
Note: We do not recommend using GLOB to collect a list of source files from your source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated build system cannot know when to ask CMake to regenerate. The CONFIGURE_DEPENDS flag may not work reliably on all generators, or if a new generator is added in the future that cannot support it, projects using it will be stuck. Even if CONFIGURE_DEPENDS works reliably, there is still a cost to perform the check on every rebuild.
This doesn't do what you think it does. It's certainly not setting the C++ standard:
add_definitions(-std=c++14)
To set the C++ standard, use set_target_properties:
set_target_properties(experiments PROPERTIES
CXX_STANDARD 14 # standard version
CXX_STANDARD_REQUIRED ON # required yes
)
You can set the standard globally using set(CMAKE_CXX_STANDARD 14) if you want to, but it may not work with MSVC.
How can I set up my CMake file so that it compiles both the library and my own code separately, using different compiler flags, and then links them together?
Use target_compile_options and target_link_options separately on targets to specific flags for a specific target.
Your add_definitions(-std=c++14) is doing nothing (because there are no targets after it) and prefer using set_target_properties(target PROPERTIES CXX_STANDARD 14 CXX_STANDARD_REQUIRED YES CXX_EXTENSIONS NO) to portably set C++14.

How to resolve undefined reference when they are not used with GCC & CMAKE

I am trying to port some MSVC projects to Linux with help of CMAKE.
In one of the Library project there are some functions which are just declared and not being defined anywhere or used anywhere.
for ex:
int fun_a();
int fun_unsed() /*This function is never used in project*/
{
fun_a();
}
Now when I try to do make in Linux, I am observing undefined reference to the declared functions. But same code works on MSVC with same CMAKE files.
I tried to use below flags in my CMAKE files(from here), but it doesn't seems to help.
SET(GCC_COVERAGE_COMPILE_FLAGS "-unresolved-symbols=ignore-all")
SET(GCC_COVERAGE_LINK_FLAGS "-unresolved-symbols=ignore-all")
SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}" )
SET( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}" )
am I missing something?
Below is cmake file for executable
#Add Library Projects to the test application
add_subdirectory ("${PROJECT_BINARY_DIR}/../../src/build/vc/" "${PROJECT_BINARY_DIR}/../../src/build/vc/")
#set additional search paths for libraries
#set(CMAKE_LIBRARY_PATH ${PROJECT_BINARY_DIR}/../../lib/Debug)
link_directories(${PROJECT_BINARY_DIR}/../../lib ${OPENCV_BUILD}/lib)
#set ignore undefined & unused functions errors. It seems GCC by defalt looks for them.
SET(GCC_COVERAGE_LINK_FLAGS "-unresolved-symbols=ignore-all")
SET(GCC_COVERAGE_COMPILE_FLAGS "-ffunction-sections")
SET(GCC_COVERAGE_LINK_FLAGS "-Wl,-gc-sections -flto")
SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GCC_COVERAGE_COMPILE_FLAGS}" )
SET( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${GCC_COVERAGE_LINK_FLAGS}" )
#Get the exicutable for source files
add_executable (FaceAnalysis ${sources})
target_link_libraries (FaceAnalysis faceDetect.a libopencv_core.so libopencv_imgproc.so libopencv_imgcodecs.so libopencv_videoio.so libopencv_objdetect.so libopencv_highgui.so libopencv_video.so libopencv_ml.so SDL2)
add_dependencies(FaceAnalysis faceDetect)
If they were indeed unreferenced, you wouldn't get an "undefined reference" error.
The linker error should tell you where the symbol is used.
Edit: revised question
This can be worked around by asking the compiler to put each function into a separate section, so they are kept apart inside the object files until the final link, and then instructing the linker to discard sections that are unreferenced.
Add -ffunction-sections to CFLAGS and/or CXXFLAGS as appropriate, and -Wl,--gc-sections to LDFLAGS.
The link-time optimizer (-flto) can also do this, but AFAIK requires optimization to be enabled, so it would fail in Debug builds.
If you have no implementation for the function in your project, then it must be implemented in another library. In your visual studio project, right-click on the project, select "Properties", then under "Linker" take a look at the linked libraries and link directories. You'll see at least one library defined there. Then in CMAKE you just need to target_link_libraries to the same lib.
The flags which remove death code are, as Simon said in his answer:
-ffunction-sections and -Wl,--gc-sections1
But, GCC does not support them in some targets, like Windows and Linux on X86 and 64, with ELF file format.
My solution was to reduce the visibility of all functions, by also using the following flags:
-fvisibility=hidden and -fvisibility-inlines-hidden 2
All the flags together, removed the link time error.
1 -ffunction-sections puts each function in a different section
-Wl,--gc-sections should remove all the sections that are unused.
2 This flags change the visibility of all functions from default (public) to hidden. So the linker now know that the functions are not needed anymore if they are not used inside the executable or library, so it is free to remove them.

What's the proper way to enable AddressSanitizer in CMake that works in Xcode

I've added AddressSanitizer flag as follow:
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
Everything builds and runs fine when using Unix Makefiles.
The problem comes when generating the Xcode project, it just doesn't want to link because it cannot find the ASan library.
I already found two solutions, but decided not to use them because they cannot be automated using just CMake:
Adding -Wl,-undefined,dynamic_lookup to the linked flags, so it skips linking to dynamic libraries.
Link with libclang_rt.asan_osx_dynamic.dylib directly.
So what's the problem with these two solutions?
When using solution #1, I have to manually open the target scheme in Xcode and add DYLD_INSERT_LIBRARIES environment variable pointing to libclang_rt.asan_osx_dynamic.dylib.
When using solution #2, the path for the ASan library varies between computers.
Additionally as another solution, I tried enabling Address Sanitizer flag from the Xcode target scheme but interestingly it didn't detect the issues I added, so I didn't list this as a solution because it failed my test.
Any help will be much appreciated.
Since the top-voted answer is the wrong way to do it these days and I did not get the proper cmake solution for this reading this thread, I thought I would mention the correct way at the time of writing this so that the next reader does not need to spend much time with this.
The idea of this solution is to pass -fsanitize=address to the compiler and linker flags.
If you would like to enable this for all your targets at the same time, you can use add_compile_options and add_link_options. This makes sense if you have multiple, potentially a large of, targets.
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)
Alternatively, you can also use target_compile_options and target_link_options to set these for a particular target. This might make more sense if you do not want this to apply to all the targets.
target_compile_options(asan-target PRIVATE -fsanitize=address)
target_link_options(asan-target PRIVATE -fsanitize=address)
You need to provide the flag(s) to the linker too. I'm doing it like this:
set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
set (CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
I propose create your own Asan profile.
get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
if(isMultiConfig)
if(NOT "Asan" IN_LIST CMAKE_CONFIGURATION_TYPES)
list(APPEND CMAKE_CONFIGURATION_TYPES Asan)
endif()
else()
set(allowedBuildTypes Asan Debug Release RelWithDebInfo MinSizeRel)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "${allowedBuildTypes}")
if(CMAKE_BUILD_TYPE AND NOT CMAKE_BUILD_TYPE IN_LIST allowedBuildTypes)
message(FATAL_ERROR "Invalid build type: ${CMAKE_BUILD_TYPE}")
endif()
endif()
set(CMAKE_C_FLAGS_ASAN
"${CMAKE_C_FLAGS_DEBUG} -fsanitize=address -fno-omit-frame-pointer" CACHE STRING
"Flags used by the C compiler for Asan build type or configuration." FORCE)
set(CMAKE_CXX_FLAGS_ASAN
"${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=address -fno-omit-frame-pointer" CACHE STRING
"Flags used by the C++ compiler for Asan build type or configuration." FORCE)
set(CMAKE_EXE_LINKER_FLAGS_ASAN
"${CMAKE_EXE_LINKER_FLAGS_DEBUG} -fsanitize=address" CACHE STRING
"Linker flags to be used to create executables for Asan build type." FORCE)
set(CMAKE_SHARED_LINKER_FLAGS_ASAN
"${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fsanitize=address" CACHE STRING
"Linker lags to be used to create shared libraries for Asan build type." FORCE)
Notes:
AddressSanitizer (ASan) for Windows with MSVC is under experimental stage thus I didn't provided the MSVC way here.
CMAKE_BUILD_TYPE isn't used by multi-configuration generators (Xcode, Visual Studio, etc), thus I provided an example to check this first.
The default value for CMAKE_BUILD_TYPE is an empty string. And user can set CMAKE_BUILD_TYPE to any value at the cmake command line. Therefore, we check both cases, and make sure that we are dealing with a known build type (if provided).
There is also CMAKE_MODULE_LINKER_FLAGS you may want to configure.
Usage:
$ cmake \
-DCMAKE_BUILD_TYPE=Asan \
...
...
cmake 3.13
introduce configuration for xcode schema
in CMake
cmake_minimum_required(VERSION 3.13)
set(CMAKE_XCODE_GENERATE_SCHEME ON)
set(CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER ON)
set(CMAKE_XCODE_SCHEME_ADDRESS_SANITIZER_USE_AFTER_RETURN ON)
When Build
xcodebuild -enableAddressSanitizer YES
First ensure with debug info, such as setting CMAKE_BUILD_TYPE to Debug passing -g flag for GCC/Clang.
Then, if your target is an executable or an shared library, then you may set those cmake variables:
CMAKE_EXE_LINKER_FLAGS
CMAKE_EXE_LINKER_FLAGS_DEBUG
CMAKE_SHARED_LINKER_FLAGS
CMAKE_SHARED_LINKER_FLAGS_DEBUG
i.e. When your target is an executable:
set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
When your target is an shared library:
set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
However, when your executable relies on an static library and you'd like to use asan to check your static library, then you have to set like this:
set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} -fno-omit-frame-pointer -fsanitize=address")
The simplest solution that I currently found is
cmake -DCMAKE_BUILD_TYPE=ASAN .
I prefer this option since it expresses the intent (run sanitizers) rather than modifies a number of flags.
I do not know when this option was added. I also cannot find any documentation to it.
I found that the other answers didn't work, you need to set the init variables in your toolchain file:
set(CMAKE_EXE_LINKER_FLAGS_INIT "-fsanitize=address -fno-omit-frame-pointer")

Optimize in CMake by default

I have a C++ project which uses CMake as its build system. I'd like the following behavior:
If cmake is invoked as cmake .., then CMAKE_CXX_FLAGS is -O3 -Wall -Wextra
If cmake is invoked as cmake .. -DCMAKE_BUILD_TYPE=Debug, then CMAKE_CXX_FLAGS is -g -Wall -Wextra
I tried the following
message(STATUS "Build type: ${CMAKE_BUILD_TYPE}")
set(CMAKE_CXX_FLAGS "-O3 -Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -Wextra")
But this has a big problem. First of all, if the second invocation is used, then both -O3 and -g flags are passed to the compiler. Besides, if I use the second invocation and the first thereafter, CMAKE_BUILD_TYPE stays Debug although not explicitly ordered so - so I get a Debug build although I want an optimized build.
Why? What can I do to get the desired behavior?
First off: recommended usage of CMake is to always specify CMAKE_BUILD_TYPE explicitly on the command line (if and only if using a single-configuration generator). Your use case deviates from this best practice, so treat this answer as "how you can do it," not necessarily as "how you should do it."
To address the first issue, you should be able to do this early in your CMakeList:
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE Release)
endif()
set(CMAKE_CXX_FLAGS "-Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
This will make sure that if you do not specify a build type at all, it will default to "Release" and thus CMAKE_CXX_FLAGS_RELEASE will be used.
The second one is harder to tackle. Variables passed from the command line (such as CMAKE_BUILD_TYPE=Debug) are cached by CMake and thus re-used in subsequent invocations (that is necessary, since CMake can re-trigger itself if you modify its inputs between builds).
The only solution is to make the user switch the build type explicitly again, using cmake .. -DCMAKE_BUILD_TYPE=Release.
Consider why this is necessary: as I said, CMake can re-trigger itself as part of a build if CMake's input (CMakeLists.txt files or their dependencies) has changed since last CMake ran. In such case, it will also be run without command-line arguments such as -DCMAKE_BUILD_TYPE=whatever, and will rely on the cache to supply the same value as last time. This scenario is indistinguishable from you manually running cmake .. without additional arguments.
I could provide a hacky solution to always reset CMAKE_BUILD_TYPE to Release if not specified explicitly on the command line. However, it would also mean that a buildsystem generated as Debug would get re-generated as Release if automatic re-generation happened. I am pretty sure that's not what you want.
For CXX flags specific for Release target, you should set
CMAKE_CXX_FLAGS_RELEASE
instead of
CMAKE_CXX_FLAGS
In your case you can use:
set(CMAKE_CXX_FLAGS "-Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")
A more modern CMake approach (which I suggest, if you are using CMake version 2.8.12 or newer), is well described in this StackOverflow answer and involves the use of target_compile_options.
The default optimization level for various release modes is O3, which often isn't the best choice. Within CMakeLists.txt file, these can be modified to O2:
# Modify compile flags to change optimization level from O3 to O2
string(REGEX REPLACE "([\\/\\-]O)3" "\\12"
CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REGEX REPLACE "([\\/\\-]O)3" "\\12"
CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
string(REGEX REPLACE "([\\/\\-]O)3" "\\12"
CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL}")
string(REGEX REPLACE "([\\/\\-]O)3" "\\12"
CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
These regular expressions will be modified (e.g.):
-O3 to -O2 usually for Linux-based compilers
/O3 to /O2 usually for Windows-based compilers

Does set_target_properties in CMake override CMAKE_CXX_FLAGS?

At the beginning of my CMake project, I'm setting general compilation flags in the variable CMAKE_CXX_FLAGS, like
set(CMAKE_CXX_FLAGS "-W -Wall ${CMAKE_CXX_FLAGS}")
Later on, I need to append additional configuration-specific compilation flags (stored in BUILD_FLAGS). Can I use the following command for this:
set_target_properties(${TARGET} PROPERTIES COMPILE_FLAGS ${BUILD_FLAGS})
or do I have to add the CMAKE_CXX_FLAGS manually:
set_target_properties(${TARGET} PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS} ${BUILD_FLAGS}")
to prevent CMAKE_CXX_FLAGS being overriden by BUILD_FLAGS?
The accepted answer is still working but outdated since 2013.
This answer is based and new functions from CMake v2.8.12, v3.3 and v3.13.
Since CMake-2.8.12 (2013)
Two new commands to set CMAKE_CXX_FLAGS:
target_compile_options() (for one single target)
add_compile_options() (for all targets)
The documentation of last version has not changed a lot since cmake-2.8.12:
target_compile_options()
add_compile_options()
In you case you can use:
target_compile_options(${TARGET} PRIVATE ${BUILD_FLAGS})
Or simply if you have a single target:
add_compile_options(${BUILD_FLAGS})
More examples
target_compile_options(mylib PRIVATE -O2) # only internal
target_compile_options(mylib INTERFACE -gl) # only external
target_compile_options(mylib PUBLIC -g) # same as PRIVATE + INTERFACE
# multiple targets and flags
target_compile_options(mylib1 mylib2 PRIVATE -Wall -Wextra)
target_compile_options( mylib PUBLIC -DUSEXX) # Bad
target_compile_definitions(mylib PUBLIC -DUSEXX) # OK
add_compile_options(-Wall -Wextra) # for all targets in current directory
add_compile_options(-DUSEXX) # Bad
add_definitions(-DUSEXX) # OK
Deprecated COMPILE_FLAGS
cmake-3.0 documentation flags COMPILE_FLAGS as deprecated:
COMPILE_FLAGS
Additional flags to use when compiling this target’s sources.
The COMPILE_FLAGS property sets additional compiler flags used to
build sources within the target. Use COMPILE_DEFINITIONS to pass
additional preprocessor definitions.
This property is deprecated. Use the COMPILE_OPTIONS property or the
target_compile_options command instead.
If you still want to use set_target_properties() you may use COMPILE_OPTIONS instead of COMPILE_FLAGS:
set_target_properties(${TARGET} PROPERTIES COMPILE_OPTIONS ${BUILD_FLAGS})
Since CMake-3.3 (2015)
Anton Petrov suggests to use generator expressions as presented in an answer of ar31.
The CMake generator expressions applies your ${BUILD_FLAGS} to:
C++ language using $<COMPILE_LANGUAGE:CXX> (can also be C, CUDA...)
Clang compiler using $<CXX_COMPILER_ID:Clang>
(can also be GNU for gcc, or MSVCfor Visual C++... see full list)
(use $<C_COMPILER_ID:Clang> instead if language is C)
and more as supported C++ feature or compiler version... (see documentation)
In you case you can use:
target_compile_options(${TARGET} PRIVATE
$<$<COMPILE_LANGUAGE:CXX>:${BUILD_FLAGS_FOR_CXX}>
$<$<COMPILE_LANGUAGE:C>:${BUILD_FLAGS_FOR_C}>)
or about compilers:
target_compile_options(${TARGET} PRIVATE
$<$<CXX_COMPILER_ID:Clang>:${BUILD_FLAGS_FOR_CLANG}>
$<$<CXX_COMPILER_ID:GNU>:${BUILD_FLAGS_FOR_GCC}>
$<$<CXX_COMPILER_ID:MSVC>:${BUILD_FLAGS_FOR_VISUAL}>)
Since CMake-3.13 (2018)
A new function target_link_options() allow to pass options to the linker, as mentioned by Craig Scott.
Different options for C and C++ files
The best way is to distinguish C files and C++ files using two different targets.
Use the first one:
set_target_properties(${TARGET} PROPERTIES COMPILE_FLAGS ${BUILD_FLAGS})
The flags stored in BUILD_FLAGS are appended after CMAKE_CXX_FLAGS when compiling the sources of TARGET. The documentation hints at this, but I've just tried it to make sure.
COMPILE_FLAGS
Additional flags to use when compiling this target's sources.
The COMPILE_FLAGS property sets additional compiler flags used to
build sources within the target. Use COMPILE_DEFINITIONS to
pass additional preprocessor definitions.
The full command line will be the equivalent of:
${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} ${COMPILE_FLAGS} -o foo.o -c foo.cc
And as Ramon said, you can always check with make VERBOSE=1.