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.
Related
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.
I'm building an application for which I'd like to support a certain proprietary platform. It uses a modified version of ARMCC which CMake doesn't seem to like - no matter what I do, it keeps trying to provide strange flags to armlink where it should not, ignoring attempts to override its behavior.
Is it possible to essentially provide an all-new definition of how CMake should deal with a particular compiler? i.e. specify the binary and flags used for compilation, linking, etc., the expected file extensions, etc. throughout the whole process such that CMake doesn't do anything nasty? CMake seems to make custom compiler definitions an incredibly obscure thing to accomplish.
Edit: I've made it provide most of the right flags, but now I'm realizing I can't change the CMake test program - programs for this platform will fail to build if a certain atypical set of symbols (alternative entry point, not main) don't exist.
Edit 2: I've skipped the test program, but now it's failing while trying to get compiler information (why bother? I've given it everything it needs to know for the situation...) with this error:
Error: C9912E: No --cpu selected
To make it abundantly clear, the command it presents on the line above this clearly has the --cpu flag provided to a valid value:
armcc.exe -xc++ --cpu=MPCore -fpch-preprocess -v -dD -E
Though I'm entirely unsure as to what it's trying to do with the rest of it - I can't seem to override this part.
First, you need a toolchain.cmake file. Here, you provide the paths to your armcc compiler, linker, etc.
SET(CMAKE_SYSTEM_NAME Generic)
SET(CMAKE_SYSTEM_VERSION 1)
# this one is important
SET( CMAKE_SYSTEM_PROCESSOR arm )
SET(MCU_ARCH Cortex-M4) #MCU architecture
SET(TOOLCHAIN_BIN_DIR "C:/Keil_v5/ARM/ARMCC/bin/")
set(LINKER_SCATTER_SCRIPT ${PROJECT_SOURCE_DIR}/Scatter_file.sct)
SET(CMAKE_C_COMPILER ${TOOLCHAIN_BIN_DIR}/armcc.exe CACHE FILEPATH "C compiler")
SET(CMAKE_CXX_COMPILER ${TOOLCHAIN_BIN_DIR}/armcc.exe CACHE FILEPATH "C++ compiler")
SET(CMAKE_LINKER ${TOOLCHAIN_BIN_DIR}/armlink.exe CACHE FILEPATH "linker")
SET(CMAKE_AR ${TOOLCHAIN_BIN_DIR}/armar.exe CACHE FILEPATH "Archiver")
SET(CMAKE_ASM_COMPILER ${TOOLCHAIN_BIN_DIR}/armasm.exe CACHE FILEPATH "Assembler")
SET(CMAKE_FROMELF ${TOOLCHAIN_BIN_DIR}/fromelf.exe CACHE FILEPATH "From ELF tool")
SET(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
SET(CMAKE_EXE_LINKER_FLAGS_INIT "--cpu ${MCU_ARCH} --myFlag1 --myFlag2 ${LINKER_SCATTER_SCRIPT}")
Next, within the CMakeLists.txt file, you can pass the compiler and linker flags:
cmake_minimum_required(VERSION 3.10)
SET(CMAKE_VERBOSE_MAKEFILE ON)
project (MyProject C CXX ASM)
#Source Files -> Let CMake know your source files
SET(SRC_FILES ${CMAKE_SOURCE_DIR}/Dir/main.c)
add_executable(
${PROJECT_NAME}
${SRC_FILES})
#Defining compiler preprocessor directives
target_compile_definitions(${PROJECT_NAME} PRIVATE
$<$<COMPILE_LANGUAGE:C>: DEFINE_1;__DEBUG;>
$<$<COMPILE_LANGUAGE:CXX>:DEFINE_2;__DEBUG;>)
#Defining compiler flags
target_compile_options(${PROJECT_NAME} PRIVATE
$<$<COMPILE_LANGUAGE:C>: --c99 -c -O0 --cpu ${MCU_ARCH};>
$<$<COMPILE_LANGUAGE:CXX>:--cpp11 -c -O0 --cpu ${MCU_ARCH};>
$<$<COMPILE_LANGUAGE:ASM>:--cpu ${MCU_ARCH} -g>)
SET(CMAKE_ASM_FLAGS "--pd \"DEFINE_3"")
target_link_libraries(${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/SomeLibFile.lib --flagForLibFile)
target_link_options(${PROJECT_NAME} PRIVATE --linkerFlags)
target_include_directories(${PROJECT_NAME} PUBLIC
${CMAKE_SOURCE_DIR})
For the necessary flags, have a look at what Keil is using.
Then you can build your application by passing the toolchain.cmake file as -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake
EDIT: Updated based on the feedback from KamilCuk
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)
I am aware of how to suppress compile warnings with CMake by doing (suppose I want to disable compile warning C4819):
set_target_properties(${PROJECT_NAME} PROPERTIES COMPILE_FLAGS "/wd4819")
So how to suppress link warnings with CMake (say LNK4099)?
Try this:
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "/ignore:4099")
It worked perfectly for me with Visual Studio 2015.
Another way to ignore linker warnings for all the targets in the current scope in CMake is by settings CMAKE_EXE_LINKER_FLAGS, CMAKE_SHARED_LINKER_FLAGS, CMAKE_STATIC_LINKER_FLAGS as it follows:
# Ignore warnings about missing pdb
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /ignore:4099")
set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4099")
It also exists a CMAKE_MODULE_LINKER_FLAGS, but it seems not related to C++ projects.
In case your library depends on some another library that doesn't have PDBs, you may want to add ignore flag only once instead of adding it to each executable. Consider the following:
add_library(my_lib my_lib.cpp)
find_library(EXT_LIBRARY no_pdb.lib REQUIRED)
target_link_libraries(my_lib PUBLIC ${EXT_LIBRARY})
add_executable(my_exe1 "src/exe1.cpp")
target_link_libraries(my_exe1 PUBLIC my_lib)
add_executable(my_exe2 "src/exe2.cpp")
target_link_libraries(my_exe2 PUBLIC my_lib)
So now both my_exe1 and my_exe2 cause LNK4099 error. To fix this, instead of doing this on the executable level like this:
set_target_properties(my_exe1 PROPERTIES LINK_FLAGS "/ignore:4099")
set_target_properties(my_exe2 PROPERTIES LINK_FLAGS "/ignore:4099")
You may want to add ignore flag to your library's interface only once:
target_link_options(my_lib INTERFACE "/ignore:4099")
I would like to add -std=c++11 to my
add_compile_options("-std=c++11")
However, this also adds them to compilation of C files, not only C++. I know I can add conditional compile flags depending on the configuration used:
add_compile_options("$<$<CONFIG:DEBUG>:-addMeInDebugOnly>")
How can I add my flag only to c++ files? I'm looking for something like:
add_compile_options("$<$<??:??>:-std=c++11>")
But what do I need to fill in with the question marks?
When you have mixed C and C++ sources, the LINKER_LANGUAGE property might apply the wrong flags for compilation of individual sources. The solution is to use the COMPILE_LANGUAGE generator expression (introduced with CMake 3.3). The simplest example for your original C++1x flag is:
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-std=c++11>)
When you have a string of compile options (for example, for usage with the COMPILE_FLAGS target property), you have to split the flags
set(WARNCFLAGS "-Wall -Wextra -Wfuzzle -Wbar")
# ...
string(REPLACE " " ";" c_flags "${WARNCFLAGS}")
string(REPLACE " " ";" cxx_flags "${WARNCXXFLAGS} ${CXX1XCXXFLAGS}")
add_compile_options(
"$<$<COMPILE_LANGUAGE:C>:${c_flags}>"
"$<$<COMPILE_LANGUAGE:CXX>:${cxx_flags}>"
)
# Two alternative variants for single targets that take strings:
target_compile_options(some-target PRIVATE "${WARNCFLAGS}")
set_target_properties(some-target PROPERTIES
COMPILE_FLAGS "${WARNCFLAGS}")
Use of strings is however deprecated in favor of lists. When lists are in use, you can use:
set(c_flags -Wall -Wextra -Wfuzzle -Wbar)
# ...
add_compile_options(
"$<$<COMPILE_LANGUAGE:C>:${c_flags}>"
"$<$<COMPILE_LANGUAGE:CXX>:${cxx_flags}>"
)
# Two alternative variants for single targets given a list:
target_compile_options(some-target PRIVATE ${f_flags})
set_target_properties(some-target PROPERTIES
COMPILE_OPTIONS "${c_flags}")
Pay attention to the quoting. If a list is not quotes, it is expanded to its items (and is no longer a list). To pass a list between commands, quote it.
You can use LINKER_LANGUAGE target property to add flag only to C++ targets*:
add_compile_options(
"$<$<STREQUAL:$<TARGET_PROPERTY:LINKER_LANGUAGE>,CXX>:-std=c++11>"
)
*Note that this will not work for targets with mixed C/C++ sources
CMAKE_CXX_FLAGS should work fine too:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
Probably you need to add them to cache if it set before project command (e.g. in toolchain):
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" CACHE STRING "" FORCE)
I'd rather do it like this:
set_source_files_properties(
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
PROPERTIES COMPILE_FLAGS "-std=c++11")
where the documentation for set_source_files_properties is at http://www.cmake.org/cmake/help/v3.0/command/set_source_files_properties.html