How to set compiler flags for groups of files in CMake? - c++

In my CMake project I set up compiler flags like this:
if (MSVC)
# Build cpp files on all cores
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP /W4")
# can't use the "secure" versions as they're Windows specific
add_definitions("/D\"_CRT_SECURE_NO_WARNINGS\"")
add_definitions("/D\"_SCL_SECURE_NO_WARNINGS\"")
add_definitions("/wd4290")
else()
# Enable C++11, you may need to use -std=c++0x if using an older gcc compiler
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
else()
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-unused-parameter -fPIC -Wall -Weffc++ -pedantic")
endif()
endif()
To set the flags for MSVC, Clang and GCC. However in my source files var I have my source code and 3rd party things like gtest and gmock.
How can I set these flags such that they only apply to some subset of my source code?
E.g
# This should use the flags set above
SET(source
mycode/mysrc.cpp)
# This should not use the flags from above
SET(not_my_source
3rdparty/3rdparty.cpp)
# But both end up being part of the same executable
add_executable(Test ${source} ${not_my_source})

You may want to refer to here. You should be able to pass a list of files that you want to change the compiler flags for.

Related

ld cannot find -latomic or -lstdc++

When trying to compile my project using the toolchain provided by the manufacturer of the hardware I'm developing for, compilation succeeds, however linking fails with the following error messages:
/media/xxx/Data/toolchains/sysroots/x86_64-xxx-linux/usr/bin/arm-xxx-linux-gnueabi/../../libexec/arm-xxx-linux-gnueabi/gcc/arm-xxx-linux-gnueabi/4.8.4/ld: cannot find -latomic
/media/xxx/Data/toolchains/sysroots/x86_64-xxx-linux/usr/bin/arm-xxx-linux-gnueabi/../../libexec/arm-xxx-linux-gnueabi/gcc/arm-xxx-linux-gnueabi/4.8.4/ld: cannot find -lstdc++
However, when building a small test program using the same compiler and linker, everything works.
Below is an excerpt of the important parts of my CMake toolchain file:
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
###
# Get $CROSS
###
if (NOT $ENV{CROSS_COMPILE} STREQUAL "")
set(cross $ENV{CROSS_COMPILE}) # WARNING: Ends with a - (minus)
else()
set(cross "arm-xxx-linux-gnueabi-")
endif()
###
# Set CMake sysroot
###
set(CMAKE_SYSROOT $ENV{SDKTARGETSYSROOT})
set(CMAKE_SYSROOT_LINK $ENV{SDKTARGETSYSROOT})
set(CMAKE_SYSROOT_COMPILE $ENV{SDKTARGETSYSROOT})
set(CMAKE_C_COMPILER $ENV{TOOLCHAIN_TOOLSDIR}/${cross}gcc)
set(CMAKE_CXX_COMPILER $ENV{TOOLCHAIN_TOOLSDIR}/${cross}g++)
set(CMAKE_LD $ENV{TOOLCHAIN_TOOLSDIR}/${cross}ld)
set(CMAKE_STRIP $ENV{TOOLCHAIN_TOOLSDIR}/${cross}strip)
set(CMAKE_AR $ENV{TOOLCHAIN_TOOLSDIR}/${cross}ar)
include_directories($ENV{SDKTARGETSYSROOT}/usr/include)
I'm not at liberty to disclose source code, but the source code isn't the issue here as it compiles correctly.
Here's another MWE of the CMakeLists.txt file:
cmake_minimum_required(VERSION 3.10)
project(xxx LANGUAGES CXX VERSION 2.0.0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(TARGET_NAME xxx.bin)
include_directories(
include/
${INCLUDE_DIRS}
)
file(GLOB_RECURSE FILES ${CMAKE_CURRENT_SOURCE_DIR} FOLLOW_SYMLINKS src/*.cpp)
if (xxx_DEBUG)
add_compile_options(
-Wpedantic
-DDDC_STUB
-fabi-version=6
-Wno-format-security
-Wno-reorder
-ggdb3
-O0
-fpermissive # Test
-fPIC
#-static-libstdc++ # with or without, doesn't change the behaviour
#-static-libgcc # with or without, doesn't change the behaviour
)
else()
add_compile_options(
-Wpedantic
-fabi-version=6
-Wno-format-security
-Wno-reorder
-O3
-fpermissive # Test
-fPIC
#-static-libstdc++ # with or without, doesn't change the behaviour
#-static-libgcc # with or without, doesn't change the behaviour
)
endif()
add_executable(${TARGET_NAME} ${FILES} ${MEMWATCH_FILES})
I've tried setting the sysroot-argument for ld, I've tried manually specifying the link (search) path -L$ENV{SDKTARGETSYSROOT}/usr/lib/.debug, and I've also tried hard-wiring the path to the library in question: $ENV{SDKTARGETSYSROOT}/usr/lib/.debug/libatomic.so.1.0.0
Is there another way I can tell ld what libraries to use?
This works perfectly with standard toolchains!
EDIT:
I've now also tried brute-forcing it by creating a symlink from the .debug/libatomic to usr/lib/libatomic. That also hasn't had the desired effect.

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.

Best way to portably set compiler options like -Wall and -pedantic in CMake

Even if I don't want to tie my CMakeLists file to a specific compiler, I still would like to enable certain options like -Wall that I know many compilers support.
Currently I am doing it like this to set the -Wall and -pedantic flags if they are accepted by the current compiler:
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag(-Wall temp)
if(temp)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
endif()
check_cxx_compiler_flag(-pedantic temp)
if(temp)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic")
endif()
Is there a better way? Or at least a nicer way to do essentially the same thing? The above is incredibly verbose and ugly for what it achieves. A much nicer command would be something like:
enable_cxx_compiler_flags_if_supported(-Wall -pedantic)
As suggested in a comment I've tried to write a function myself. I obviously don't know much CMake, but here is my try at a function that checks both that the flag is supported using check_cxx_compiler_flag and also checks that the flag isn't already set (to avoid flooding the list with duplicates).
include(CheckCXXCompilerFlag)
function(enable_cxx_compiler_flag_if_supported flag)
string(FIND "${CMAKE_CXX_FLAGS}" "${flag}" flag_already_set)
if(flag_already_set EQUAL -1)
check_cxx_compiler_flag("${flag}" flag_supported)
if(flag_supported)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}" PARENT_SCOPE)
endif()
unset(flag_supported CACHE)
endif()
endfunction()
# example usage
enable_cxx_compiler_flag_if_supported("-Wall")
enable_cxx_compiler_flag_if_supported("-Wextra")
enable_cxx_compiler_flag_if_supported("-pedantic")

CMake OBJECT file flags not working

I am trying to make a simple cmake file to generate a makefile for my project.
Now, i have to first genereate some .o files and then compile the main script.
I use some std 11 functions so i need to define that flag.
My CMakeList.txt file looks something like this:
cmake_minimum_required(VERSION 2.8)
project(P)
#C++ compiler
set(CMAKE_CXX_COMPILER_INIT g++)
#Compiler flags config
set(CMAKE_CXX_FLAGS "-W")
set(CMAKE_CXX_FLAGS "-Wall")
set(CMAKE_CXX_FLAGS "-I../..")
set(CMAKE_CXX_FLAGS "-lpthread")
set(CMAKE_CXX_FLAGS "-g")
set(CMAKE_CXX_FLAGS "-Iinclude/")
set(CMAKE_CXX_FLAGS "-std=c++11")
set(CMAKE_CXX_FLAGS "-lz")
set(CMAKE_CXX_FLAGS "-lrt")
set(CMAKE_CXX_STANDARD 11)
set_source_files_properties(a.c PROPERTIES LANGUAGE CXX)
add_library(A OBJECT a.h a.c)
set_source_files_properties(b.cpp PROPERTIES LANGUAGE CXX)
add_library(B OBJECT b.h b.cpp)
add_executable(P p.cpp $<TARGET_OBJECTS:A> $<TARGET_OBJECTS:B>)
Now, what happens is that when i run the makefile, the compiler gives me an error with b.cpp.It says that one of the functions is not defined and i know that that error is because is not compiling with standard 11.
How can i tell cmake to add std=c++11 flag when compiling object files?
I know what was wrong. Apparently you can't add the flags in different lines. I should've done something like:
set(CMAKE_CXX_FLAGS "-W -Wall -I../.. -lpthread -g -Iinclude -std=c++11 -lz -lrt")

CMake cross-compile problem with CXX_FLAGS in linker

I have following toolchain for iPhone crosscompilation on mac os x:
# Platform Info
SET (CMAKE_SYSTEM_VERSION 1)
SET (CMAKE_SYSTEM_PROCESSOR arm)
# SDK Info
SET (SDKVER "3.0")
SET (DEVROOT "/Developer/Platforms/iPhoneOS.platform/Developer")
SET (SDKROOT "${DEVROOT}/SDKs/iPhoneOS${SDKVER}.sdk")
SET (CMAKE_OSX_SYSROOT "${SDKROOT}")
SET (CMAKE_OSX_ARCHITECTURES "armv6")
SET_PROPERTY(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS TRUE)
# C Compiler
SET (CMAKE_C_COMPILER "${DEVROOT}/usr/bin/arm-apple-darwin9-gcc-4.2.1")
#SET (LINK_FLAGS "-arch armv6 -arch armv7")
#SET (CMAKE_C_LINK_EXECUTABLE "${DEVROOT}/usr/bin/g++-4.2")
SET (CMAKE_C_FLAGS "-x objective-c")
SET (CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -DDEBUG=1 -ggdb")
SET (CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} -DNDEBUG=1")
SET (CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS} -DNDEBUG=1 -ggdb")
# CXX Compiler
SET (CMAKE_CXX_COMPILER "${DEVROOT}/usr/bin/arm-apple-darwin9-g++-4.2.1")
SET (CMAKE_CXX_FLAGS "-x objective-c++")
SET (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS} -DDEBUG=1 -ggdb")
SET (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS} -DNDEBUG=1")
SET (CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS} -DNDEBUG=1 -ggdb")
# Definitions
#ADD_DEFINITIONS("-arch armv6")
#ADD_DEFINITIONS("-arch armv7")
ADD_DEFINITIONS("-pipe")
ADD_DEFINITIONS("-no-cpp-precomp")
ADD_DEFINITIONS("--sysroot=${SDKROOT}")
ADD_DEFINITIONS("-miphoneos-version-min=${SDKVER}")
# System Includes
INCLUDE_DIRECTORIES(SYSTEM "${SDKROOT}/usr/include")
INCLUDE_DIRECTORIES(SYSTEM "${SDKROOT}/usr/include/c++/4.2.1")
INCLUDE_DIRECTORIES(SYSTEM "${SDKROOT}/usr/include/c++/4.2.1/armv6-apple-darwin9")
INCLUDE_DIRECTORIES(SYSTEM "${SDKROOT}/opt/iphone-${SDKVER}/include")
INCLUDE_DIRECTORIES(SYSTEM "${SDKROOT}/usr/local/iphone-${SDKVER}/include")
# System Libraries
LINK_DIRECTORIES("${SDKROOT}/usr/lib")
LINK_DIRECTORIES("${SDKROOT}/usr/lib/gcc/arm-apple-darwin9/4.2.1/")
#LINK_DIRECTORIES("${SDKROOT}/opt/iphone-${SDKVER}/lib")
#LINK_DIRECTORIES("${SDKROOT}/usr/local/iphone-${SDKVER}/lib")
# Root Paths
SET (CMAKE_FIND_ROOT_PATH "${SDKROOT}" "/opt/iphone-${SDKVER}/" "/usr/local/iphone-${SDKVER}/")
SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH)
SET (CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET (CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# CMake Parameters
SET (iPhone 1)
SET (iPhoneOS 1)
SET (iPhoneOS_VERSION ${SDKVER})
SET (CMAKE_CROSSCOMPILING 1)
#SET_TARGET_PROPERTIES(p3dm PROPERTIES LINK_FLAGS "-arch armv6 -arch armv7")
# HELPERS
#---------
MACRO(ADD_FRAMEWORK appname fwname)
find_library(FRAMEWORK_${fwname}
NAMES ${fwname}
PATHS ${SDKROOT}/System/Library
PATH_SUFFIXES Frameworks
NO_DEFAULT_PATH)
if( ${FRAMEWORK_${fwname}} STREQUAL FRAMEWORK_${fwname}-NOTFOUND)
MESSAGE(ERROR ": Framework ${fwname} not found")
else()
TARGET_LINK_LIBRARIES(${appname} ${FRAMEWORK_${fwname}})
MESSAGE(STATUS "Framework ${fwname} found at ${FRAMEWORK_${fwname}}")
endif()
endmacro(ADD_FRAMEWORK)
And i use following CMakeLists.txt:
# PROJECT PARAMETERS
#--------------------
SET(APP_NAME p3dm)
PROJECT(${APP_NAME})
# SOURCE CODE
#-------------
SET(CMAKE_CXX_FLAGS "-x objective-c++")
INCLUDE_DIRECTORIES(SYSTEM ../inc/ ../xsrc/)
FILE(GLOB headers ../src/*.h ../xsrc/*.h)
FILE(GLOB sources ../src/*.cpp ../xsrc/*.c ../xsrc/*.cpp)
#set_source_files_properties(${sources} PROPERTIES LANGUAGE C )
# EXECUTABLE
#------------
SET(MACOSX_BUNDLE_GUI_IDENTIFIER "org.reversity.${APPNAME}")
SET(APP_TYPE MACOSX_BUNDLE)
ADD_EXECUTABLE(${APP_NAME} ${APP_TYPE} ${headers} ${sources})
SET_TARGET_PROPERTIES(${APP_NAME} PROPERTIES XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer: Mario Hros")
# FRAMEWORKS
#------------
ADD_FRAMEWORK(${APP_NAME} UIKit)
I use following compilation bash script:
#!/bin/bash
unset CPATH
unset C_INCLUDE_PATH
unset CPLUS_INCLUDE_PATH
unset OBJC_INCLUDE_PATH
unset LIBS
unset DYLD_FALLBACK_LIBRARY_PATH
unset DYLD_FALLBACK_FRAMEWORK_PATH
export SDKVER="3.0"
export DEVROOT="/Developer/Platforms/iPhoneOS.platform/Developer"
export SDKROOT="$DEVROOT/SDKs/iPhoneOS$SDKVER.sdk"
export PKG_CONFIG_PATH="$SDROOT/usr/lib/pkgconfig":"/opt/iphone-$SDKVER/lib/pkgconfig":"/usr/local/iphone-$SDKVER/lib/pkgconfig"
export PKG_CONFIG_LIBDIR="$PKG_CONFIG_PATH"
export MAINFOLDER=`pwd`
cmake . \
-DCMAKE_TOOLCHAIN_FILE="$MAINFOLDER/cmake/iphone-$SDKVER.toolchain" \
-DCMAKE_INSTALL_PREFIX="/opt/iphone-$SDKVER" \
"$#"
The problem is, that it uses CMAKE_CXX_FLAGS also in linker. Compilation is fine. Linking looks fine, it adds correct -framework flags, but also adds CMAKE_CXX_FLAGS (which are -x objective-c++) so instead of linking object files compiled previously it behaves like compiling objective c++ (-x objective-c++) and it is impossible to link these objects.
I am getting errors like
ComponentManager.cpp.o:20: error: stray '\341' in program
Don't you know what I am doing wrong?
You can set the LINKER_LANGUAGE to C to force CMake to use the CMAKE_C_FLAGS for the linker. Check it out here.
I've solved this problem by removing CMAKE_CXX_FLAGS and CMAKE_C_FLAGS from the toolchain file and adding ADD_DEFINITIONS("-x objective-c++") to CMakeLists.txt.
That way -x objective-c++ flags get passed only to the compiler (not linker) and only for my source code (not cmake test compilation which happens before building my target).
The link command line is set in Modules/CMake{C,CXX,Fortran}Information.cmake and defaults to using the compiler and its compilation flags (see source code). This can be changed by replacing the rule that builds the link command line, which lives in variables CMAKE_CXX_LINK_EXECUTABLE (and friends). That variable does not give the linker executable; it says how to link an executable!
One solution is to set a modified form of that rule that avoids the use of CXX flags, e.g.
cmake -DCMAKE_CXX_LINK_EXECUTABLE="<CMAKE_CXX_COMPILER> <FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
However, you can avoid the problem by not setting CMAKE_CXX_FLAGS in the first place, but rather adding COMPILE_FLAGS property to the target.