CMake per file optimizations - cmake

Elsewhere the question has been asked, "How do I turn off optimizations on one file?" The answer is usually something like this:
cmake_minimum_required( VERSION 3.8 )
project( Hello )
add_executable( hello hello.c foo.c bar.c )
set( CMAKE_C_FLAGS_RELEASE "" )
set( CMAKE_CXX_FLAGS_RELEASE "" )
set_source_files_properties( hello.c
PROPERTIES
COMPILE_FLAGS -O0 )
This works unless you invoke cmake like this:
cmake -GNinja -DCMAKE_BUILD_TYPE=Release ../hello
And you get this in your build.ninja
FLAGS = -O3 -DNDEBUG -O0
Checking the documentation on COMPILE_FLAGS
Additional flags to be added when compiling this source file.
This makes sense, it is added to the list of COMPILE_FLAGS, it does not override existing compiler flags.
So, within CMake how can you override the optimisation level on a single file and being able to compile the rest of the project in Release? Otherwise you can force the compile to CMAKE_BUILD_TYPE="" which is the default behavior, but that somewhat defeats a selling point of Cmake.

You can't overwrite compiler options with the makefile CMake generators on source file level. Options are always appended (see my answer at Is Cmake set variable recursive? for the complete formula).
This is - as far as I know - only supported with the Visual Studio solution/project generators. These generators have flag tables to identify flags that are in the same group/that does overwrite a previous defined flag.
So yours is more like a feature request to also add compiler option tables to CMake's makefile generators.
Alternatives
I just wanted to add some crazy CMake magic I came up with as a workaround. Add the following to your main CMakeLists.txt after the project() command:
if (CMAKE_BUILD_TYPE)
define_property(
SOURCE
PROPERTY COMPILE_FLAGS
INHERITED
BRIEF_DOCS "brief-doc"
FULL_DOCS "full-doc"
)
string(TOUPPER ${CMAKE_BUILD_TYPE} _build_type)
set_directory_properties(PROPERTIES COMPILE_FLAGS "${CMAKE_CXX_FLAGS_${_build_type}}")
set(CMAKE_CXX_FLAGS_${_build_type} "")
endif()
This example moves the CMAKE_CXX_FLAGS_<build type> content into an new COMPILE_FLAGS directory property that is then linked to COMPILE_FLAGS source file property via define_property(... INHERITED ...).
Now the build type specific flags are only defined in COMPILE_FLAGS for each source file and you can overwrite/change them e.g. with the code snippet from your example:
set_source_files_properties(
hello.c
PROPERTIES
COMPILE_FLAGS -O0
)
References
Directory properties and subdirectories
CMake: How do I change properties on subdirectory project targets?

Related

change "Requesting program interpreter: /lib64/ld-linux-x86-64.so.2" to a customized one through cmake [duplicate]

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.
Note: Given CMake evolution since this was answer was written in 2012, most of the suggestions here are now outdated/deprecated and have better alternatives.
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:
The easiest one (not clean, but easy and convenient, and works only for compile flags, C & C++ at once):
add_definitions(${GCC_COVERAGE_COMPILE_FLAGS})
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}")
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.
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 more clear.
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.
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:
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.)
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.)
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")
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.
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
.....
With CMake 3.4+, APPEND can be used with the string command to add flags.
string(APPEND CMAKE_EXE_LINKER_FLAGS " -fexceptions")

cmake - building target with custom compile options [duplicate]

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.
Note: Given CMake evolution since this was answer was written in 2012, most of the suggestions here are now outdated/deprecated and have better alternatives.
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:
The easiest one (not clean, but easy and convenient, and works only for compile flags, C & C++ at once):
add_definitions(${GCC_COVERAGE_COMPILE_FLAGS})
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}")
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.
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 more clear.
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.
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:
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.)
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.)
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")
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.
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
.....
With CMake 3.4+, APPEND can be used with the string command to add flags.
string(APPEND CMAKE_EXE_LINKER_FLAGS " -fexceptions")

How to omit debug info for a single source file?

I'm writing a test for a tool that parses debug info, and I would like to omit debug info for a single compilation unit (source file) in my debug target. Is there any way to accomplish this with cmake?
Obviously, I could just hardcode the compiler flags, but I'd like to keep my build portable across different OSes and toolchains (say gnu and msvc).
Changing CMAKE_BUILD_TYPE on the fly doesn't seem to work.
To do this you have to modify the default flags because with multi-configuration generators you can't force a specific build type. This is a just a quick example to show how to remove the debug flag from the defaults and then add it back in. You can then update this example to use a variable to collect all the source files and some if statements to check which compiler is being used to test for either -g or /Zi.
cmake_minimum_required(VERSION 3.12)
project(sample)
message( "\${CMAKE_CXX_FLAGS_RELEASE} = ${CMAKE_CXX_FLAGS_RELEASE}")
message( "\${CMAKE_CXX_FLAGS_DEBUG} = ${CMAKE_CXX_FLAGS_DEBUG}")
string(REPLACE "-g" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
string(REPLACE "/Zi" "" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG})
string(REPLACE "-g" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO})
string(REPLACE "/Zi" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO})
message( "\${CMAKE_CXX_FLAGS_DEBUG} = ${CMAKE_CXX_FLAGS_DEBUG}")
message( "\${CMAKE_CXX_FLAGS_RELWITHDEBINFO} = ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
set_source_files_properties(file1.cpp PROPERTIES COMPILE_FLAGS "/Zi")
add_executable( sample file1.cpp file2.cpp )
You can access the default compile flags that cmake uses per configuration (see this answer). You can also set the COMPILE_FLAGS property on any source file. Thus, after you have created a target, it should be as simple as querying the default compile options for your language, and applying the appropriate defaults using set_source_files_properties.
For example, here's a CMakeLists.txt for an extremely simple project:
cmake_minimum_required(VERSION 3.6.1)
project(MyProj)
set_source_files_properties(file1.cpp PROPERTIES COMPILE_FLAGS ${CMAKE_CXX_FLAGS_RELEASE})
get_source_file_property(FILE1_FLAGS file1.cpp COMPILE_FLAGS)
get_source_file_property(FILE2_FLAGS file2.cpp COMPILE_FLAGS)
message( "\${FILE1_FLAGS} = ${FILE1_FLAGS}")
message( "\${FILE2_FLAGS} = ${FILE2_FLAGS}")
add_executable( MyExe file1.cpp file2.cpp )
Which outputs (for MSVC):
${FILE1_FLAGS} = /MD /O2 /Ob2 /DNDEBUG
${FILE2_FLAGS} = NOTFOUND
If you inspect the generated MyExe.vcxproj, you'll see that defaults are used to compile file2.cpp, whereas for file1.cpp, all the configurations use release settings:
<ClCompile Include="C:\temp\cmake\file1.cpp">
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AnySuitable</InlineFunctionExpansion>
<Optimization Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">MaxSpeed</Optimization>
<RuntimeLibrary Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">MultiThreadedDLL</RuntimeLibrary>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AnySuitable</InlineFunctionExpansion>
<Optimization Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">MaxSpeed</Optimization>
<RuntimeLibrary Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">MultiThreadedDLL</RuntimeLibrary>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|Win32'">AnySuitable</InlineFunctionExpansion>
<Optimization Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|Win32'">MaxSpeed</Optimization>
<RuntimeLibrary Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|Win32'">MultiThreadedDLL</RuntimeLibrary>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|Win32'">NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<InlineFunctionExpansion Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|Win32'">AnySuitable</InlineFunctionExpansion>
<Optimization Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|Win32'">MaxSpeed</Optimization>
<RuntimeLibrary Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|Win32'">MultiThreadedDLL</RuntimeLibrary>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|Win32'">NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<ClCompile Include="C:\temp\cmake\file2.cpp" />
I haven't tried this on other generators, but it should work identically.

Get CMake to not be silent about sources it doesn't understand?

Suppose you have a very simple CMakeLists.txt
add_executable(silent T.cpp A.asm)
CMake will happily generate a C++ target for building silent, with T.cpp in it, but will silently drop any and all reference to A.asm, because it doesn't know what to do with the suffix.
Is there any way to get CMake to loudly complain about this source file it doesn't understand (to aid in porting a Makefile to CMake).
Ignoring unknown file extensions is - unfortunately for your case - by design.
If I look at the code of cmGeneratorTarget::ComputeKindedSources() anything unknown ends up to be classified as SourceKindExtra (to be added as such to generated IDE files).
So I tested a little and came up with the following script that evaluates your executable target source files for valid file extensions by overwriting add_executable() itself:
cmake_minimum_required(VERSION 3.3)
project(silent CXX)
file(WRITE T.cpp "int main() { return 0; }")
file(WRITE T.h "")
file(WRITE A.asm "")
function(add_executable _target)
_add_executable(${_target} ${ARGN})
get_property(_langs GLOBAL PROPERTY ENABLED_LANGUAGES)
foreach(_lang IN LISTS _langs)
list(APPEND _ignore "${CMAKE_${_lang}_IGNORE_EXTENSIONS}")
endforeach()
get_target_property(_srcs ${_target} SOURCES)
foreach(_src IN LISTS _srcs)
get_source_file_property(_lang "${_src}" LANGUAGE)
get_filename_component(_ext "${_src}" EXT)
string(SUBSTRING "${_ext}" 1 -1 _ext) # remove leading dot
if (NOT _lang AND NOT _ext IN_LIST _ignore)
message(FATAL_ERROR "Target ${_target}: Unknown source file type '${_src}'")
endif()
endforeach()
endfunction()
add_executable(silent T.cpp T.h A.asm)
Since you wanted a rather loudly complain by CMake I declared it an FATAL_ERROR in this example implementation.
CMake doesn't just drop unknown files in add_executable().
If alongside with
add_executable(silent T.cpp A.asm)
you have
add_custom_command(OUTPUT A.asm COMMAND <...>
DEPENDS <dependees>)
then whenever <dependees> changed CMake will rerun command for create A.asm before compiling the executable.
Note, that automatical headers scanning doesn't provide such functionality: if your executable includes foo.h then executable will be rebuilt only when foo.h itself is changed. Any custom command creating this header will be ignored.
However, you may change behavior of add_executable by redefining it. See #Florian's answer for example of such redefinition.

How to not add Release or Debug to output path?

Here is my current settings for output :
set( EXECUTABLE_OUTPUT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/bin")
set( LIBRARY_OUTPUT_PATH "${CMAKE_CURRENT_SOURCE_DIR}/bin")
set( RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin")
But for some reason I do not want (MSVS) put out files to bin/Release or Debug folders in my bin folder. Can I realize it using CMake somehow?
thank you
A similar question was asked a few months ago, where I advised the use of target properties and also referred to another answer. For MSVC you can completely specify the locations of executables, libraries, archives, etc. on a per-configuration basis.
E.g. using something like:
if ( MSVC )
set_target_properties( ${targetname} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${youroutputdirectory} )
set_target_properties( ${targetname} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_DEBUG ${youroutputdirectory} )
set_target_properties( ${targetname} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_RELEASE ${youroutputdirectory} )
# etc for the other available configuration types (MinSizeRel, RelWithDebInfo)
endif ( MSVC )
which will put all your libraries in a single output-directory ${youroutputdirectory}, whether it is in Debug or Release config.
use cmake -G 'Ninja' in msvc 2015+