How can I actually use the compilation options set during cmake? - c++

This is probably a very simple thing but I can't find an answer for it.
For example I have the following compilation option in CMakeLists.txt
set(CMAKE_CXX_FLAGS_DEBUG "-O1 -g")
How can I use it? Doing
make CMAKE_CXX_FLAGS_DEBUG
fails.
I also can't directly do
make -O1 -g
CMakeLists.txt (shortened a bit and the name of the project anonymized):
cmake_minimum_required(VERSION 3.8)
# Set compiler
set(CMAKE_CXX_COMPILER "clang++")
set(CMAKE_C_COMPILER "clang")
# Set compilation options
set(CMAKE_CXX_FLAGS_DEBUG_INIT "-Wall -O0 -g")
set(CMAKE_CXX_FLAGS_RELEASE_INIT "-Wall")
set(CMAKE_BUILD_TYPE Debug)
# Set path to find additional dependencies
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# Version of the std library
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
# options for compilation
option(BUILD_AAAA "Build ..." ON)
# Set names of libraries
set(LIB_AAAA ${CMAKE_PROJECT_NAME})
if (BUILD_AAAA)
message(STATUS "Building sources.")
add_subdirectory(src)
endif()
# Configure AAAA.pc file and install it
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/AAAA.pc.in ${CMAKE_CURRENT_BINARY_DIR}/AAAA.pc #ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/AAAA.pc
DESTINATION lib/pkgconfig/)

If you define a target in cmake after setting variable, cmake initializes the properties of the target with the value of the variable, if you specify the right build type. No make options necessary; (I recommend using cmake --build . instead anyways to be build system independent.)
...
set(CMAKE_CXX_FLAGS_DEBUG "-O1 -g")
# with the correct configuration the flags are automatically applied to the following executable
add_executable(HelloWorld main.cpp)
Command line (assuming the working directory containing CMakeLists.txt and are on Linux):
mkdir buildDebug
cmake -D CMAKE_BUILD_TYPE=Debug -S . -B buildDebug
cmake --build buildDebug
The second command sets up the project inside the buildDebug directory with the debug configuration and the third command builds the project (equivalent to make all run from inside buildDebug.
Edit after question update
You're setting the build type to Debug in the CMakeLists.txt file. I don't recommend doing this, since this prevents you from setting up the project with another configuration. If you want Debug to be the default configuration, I'd use a cache variable. There's a similar issue with setting the compiler in the CMakeLists.txt file; for setting the compiler you may be better of using a toolchain file.
As an alternative create a cmake script initializing cache variables with the desired values which allows you to pass multiple values with just passing the file name via -C option to cmake which may be a conventient way of providing a set of possible configurations.
The only thing that changes in the answer though is the fact that passing the build type via command line is not possible in the current state of the cmake project, i.e. -D CMAKE_BUILD_TYPE=Debug could be removed from the second command line command.

Related

How to properly set CMake flags in CMakeLists.txt

I'm having trouble setting up options and CMake variables and getting them propagated to the CMake GUI.
For example, the following lines are in my CMakeLists.txt file:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -tR -DPOCO_DLL")
message("CMake flags: " ${CMAKE_CXX_FLAGS})
When running configure in the CMake GUI it prints "CMake flags: -tM -tR -DPOCO_DLL" indicating that setting the CMAKE_CXX_FLAGS "works".
The GUI however don't get updated and only shows "-tM" on the CMAKE_CXX_FLAGS line.
What is the proper way of setting up these CMAKE options in the CMakeLists file so they get propagated to the CMake GUI?
The trick is to set CMAKE_CXX_FLAGS before project(...) statement. But this will not be enough; you will also need to put it into the cache.
Also, if you plan to support setting it initially from the command line interface, and/or -C cmake option (locad initial cache), you will need to force put it into the cache, as shown:
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -tR -DPOCO_DLL" CACHE STRING "Default CXX options" FORCE)
...
...
project(MyProject)
....
On the other hand, be very careful, because you are setting command line options at the moment you know nothing about the compiler, that is by default determined during execution of project statement.
Implicitly, this renders your CMakeLists.txt compiler dependant.
At the end, here is documentation about set cmake command

How to construct CMake files to be OS generic but specific too?

Actually, my project has this files:
/ cmake / CMakeLists.txt
/ sources / {my cpp headers ...}
And I construct the "project" (makefile or .sln...) the same way under every OS, at the root of this repository with the command:
cmake ./cmake/
But it needs some optional settings like the CMAKE_CXX_FLAGS different on each OS.
And I have chosen to put this variables in a /cmake/CMakeCache.txt but to not commit this file (since it is different between OSs). The dev has to generate (edit) this file on each machine. I only gave some instructions in a readme about this CMakeCache file.
How could I make cmake files generic again, but have this differences in a commited content too?
Use code like this in your CMakeLists.txt:
if(UNIX AND NOT APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread")
elseif(WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWIN32_LEAN_AND_MEAN")
endif()
Notes:
Append your options to ${CMAKE_CXX_FLAGS} so that user-specified settings are also included.
You cannot use list(APPEND ...) with compiler flags because it uses semicolons to separate parameters (this is why the code above uses set(...) with quotes)
APPLE is also UNIX, so we need NOT APPLE when we actually mean Linux
You can use ifs for different platforms directly in your CMakeLists.txt files like following:
if (APPLE)
# ...
elseif (UNIX)
# ...
elseif (WIN32)
# ...
endif()

CMake and Make need to be run twice in order to build code successfully

I am using CMake 3.8.2, GNU make 4.2.1 and GCC 6.4.0 for my C++14 project and I noticed a strange behavior when building. I am using CMake for an out-of-source build in a sub-folder called "build" where I run cmake .. followed by make.
CMake runs fine without any errors and make will build all source files like I expect until it is done compiling and starts linking them. It will then fail with an error
[ 83%] ...
[100%] Linking CXX executable myproject
/usr/bin/ld: some-source-file.cc.o: undefined reference to symbol '_ZNKSt7__cxx1118basic_stringstreamIcSt11char_traitsIcESaIcEE3strEv##GLIBCXX_3.4.21'
Interestingly it doesn't show any compiler warnings up to this point and only shows the above mentioned linker error.
Now when I ignore the error and simply run cmake .. and then make again (just like I did before) I get all the compiler warnings that my code should produce and everything links perfectly fine, even though I didn't change any code or CMake-related files in the meantime.
I can reproduce this behavior by deleting all files in the build dir by running rm -r *.
Here is my CMakeLists.txt file:
# Define minimum required CMake version
cmake_minimum_required(VERSION 3.8.2)
# Setting compiler related settings
set(CMAKE_CXX_COMPILER "${CMAKE_SOURCE_DIR}/toolchain/binary/gcc-6.4.0/bin/gcc")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wconversion -O2 -lstdc++")
set(CMAKE_CXX_STANDARD 14)
# Define project name
project(MyProject)
# Find source files
file(GLOB_RECURSE SOURCES application/*.cc)
# Adding third-party sources
set(SOURCES ${SOURCES} "third-party/cpp-base64/base64.cpp")
# Executable to be built from which source files
add_executable(myproject ${SOURCES})
# Find and include and link Botan
find_library(BOTAN botan-2 "third-party/botan/build/lib")
include_directories("third-party/botan/build/include/botan-2")
# Includes that are part of the project
include_directories("${CMAKE_SOURCE_DIR}/application/include")
# Include nlohmann/json
include_directories("third-party/json/src")
# Include cpp-base64 by René Nyffenegger
include_directories("third-party/cpp-base64")
find_package(Boost REQUIRED COMPONENTS program_options)
if(Boost_FOUND)
include_directories(${Boost_INCLUDE_DIRS})
endif()
# Link third-party libraries
target_link_libraries(myproject ${Boost_LIBRARIES} ${BOTAN})
Note: I am required to check-in the compiler and libraries I am using, which is why I specified them in the CMake file.
If it only works the second time it has to do with cached variables.
So I'm pretty sure that it will work the first time if you modify CMAKE_CXX_COMPILER setting by adding set(... CACHE INTERNAL "") to:
set(CMAKE_CXX_COMPILER "${CMAKE_SOURCE_DIR}/toolchain/binary/gcc-6.4.0/bin/gcc" CACHE INTERNAL "")
And move set(CMAKE_CXX_FLAGS ...) after the project() command.
But please also be noted that you shouldn't put the compiler into your CMakeLists.txt.
References
CMake: In which Order are Files parsed (Cache, Toolchain, …)?
Passing compiler options cmake
CMake Error at CMakeLists.txt:30 (project): No CMAKE_C_COMPILER could be found

cmake - Global linker flag setting (for all targets in directory)

I want to pass linker flags to all sub-projects (sub-directory CMakeList) in my project.
Before switching to new cmake 3.3, I was using the following code (cmake 3.2) which was working well, adding flags for both compilation and linking :
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -stdlibc++")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -stdlibc++")
With cmake 3.3 this no longer works and set the flags only for compilation step. I updated the CMakeList to use a more "modern" cmake syntax :
set(MY_DEBUG_OPTIONS -g -stdlib=libstdc++ -Wfatal-errors)
set(MY_RELEASE_OPTIONS -O3 -stdlib=libstdc++ -Wfatal-errors)
add_compile_options(
"$<$<CONFIG:DEBUG>:${MY_DEBUG_OPTIONS}>"
"$<$<CONFIG:RELEASE>:${MY_RELEASE_OPTIONS}>")
This set compilation flags for all sub-projects, is there a similar way of doing this for linker flags ? I know one can add linker flags on a target basis with target_link_librariescommand but can't find anything else.
I tried using CMAKE_SHARED_LINKER_FLAGS (and corresponding var for exe, module,..) variable with no success.
Update :
It turns out that this has nothing to do with cmake version, things work correctly with CMAKE_CXX_FLAGS_XXXvariables, except on first make command. If one run make a second time (with a modification in CmakeList), flags are presents.
I think I found a solution while testing with a simple CMakeList : if flags are declared after the project command it just work as expected. I don't know if it's a requirement from cmake itself or just a weird behavior.
cmake_minimum_required (VERSION 3.2)
set(PROJECT Test_Project)
# Not working (on first run)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -stdlib=libstdc++ -Wfatal-errors")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -g -stdlib=libstdc++ -Wfatal-errors")
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -stdlib=libstdc++ -Wfatal-errors")
project(${PROJECT})
# Declare here instead...
add_executable(Test test.cpp)
MESSAGE( STATUS "Config flags : " ${CMAKE_CXX_FLAGS_RELEASE})
Using :
cmake -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release .
Your problems are/were not related to a specific CMake version.
It's the same for all linker/compiler flag variables in CMake. Because those variables are cached variables and set with the project()/enable_language() command (details see here), you either have to
prefill the cache with set(... CACHE ...) before the project() command
generally use the set(... CACHE ... FORCE) to force/overwrite
move the set() after the project() command to hide or append to the cached variables
Here is an example for CMAKE_EXE_LINKER_FLAGS showing all three variants:
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
# 1. prefill
#set(CMAKE_EXE_LINKER_FLAGS "-Wl,-Map=output.map" CACHE INTERNAL "")
project(Test_Project CXX)
# 2. force
set(CMAKE_EXE_LINKER_FLAGS "-Wl,-Map=output.map" CACHE INTERNAL "" FORCE)
# 3. hide
#set(CMAKE_EXE_LINKER_FLAGS "-Wl,-Map=output.map")
# 3. or append
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-Map=output.map")
# TODO: Remove, this is just for testing
file(WRITE "foo.cpp" "int main() {}")
add_executable(${PROJECT_NAME} foo.cpp)
Whatever the values of those variables are at the end of your any given CMakeLists.txt file will be applied to all corresponding targets in the same CMakeLists.txt file as defaults (see CMAKE - setting compile flags for libraries and What's the CMake syntax to set and use variables?).
The first variant has the disadvantage that it's really only the initial value. The second and third variant would most likely need an if (CMAKE_COMPILER_IS_GNUCXX) around it, so I prefer the second variant with moving those settings to its own initial-cache file:
MyGNUSettings.cmake
set(CMAKE_CXX_FLAGS "-stdlib=libstdc++ -Wfatal-errors" CACHE INTERNAL "" FORCE)
set(CMAKE_CXX_FLAGS_DEBUG "-g" CACHE INTERNAL "" FORCE)
set(CMAKE_CXX_FLAGS_RELEASE "-O3" CACHE INTERNAL "" FORCE)
set(CMAKE_EXE_LINKER_FLAGS "-Wl,-Map=output.map" CACHE INTERNAL "" FORCE)
Using e.g.
cmake -G "Unix Makefiles" -C MyGNUSettings.cmake -DCMAKE_BUILD_TYPE=Release .
And yes - for the global and per compiler settings - I prefer the global cached variables over the add_compile_options() command. I think add_compile_options() haven't replaced the global variables, it was mainly introduced to prevent people putting compiler options in add_definitions() commands.

"cmake -g "XCode" " Creates a library, not executable

When I set up an XCode project with CMake using cmake -G "Xcode", and open it in Xcode, the option to run is greyed out, and I can only build the project. Looking around revealed that this is probably because XCode sees it as a library and not an executable. How do I use cmake so that I can run the file after building it?
EDIT: Here's my CMakeLists.txt
# Get the exercise name from the current directory
get_filename_component(exercise ${CMAKE_CURRENT_SOURCE_DIR} NAME)
# Basic CMake project
cmake_minimum_required(VERSION 2.8.11)
# Name the project after the exercise
project(${exercise} CXX)
# Locate Boost libraries: unit_test_framework, date_time and regex
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
find_package(Boost 1.55 REQUIRED COMPONENTS unit_test_framework date_time regex)
# gcc/clang won't enable C++11 features without this flag
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(GNU|Clang)")
set(CMAKE_CXX_FLAGS "-std=c++11")
endif()
# Configure to run all the tests?
if(${EXERCISM_RUN_ALL_TESTS})
add_definitions(-DEXERCISM_RUN_ALL_TESTS)
endif()
# Make an executable that runs the exercism unit tests against the implementation
function(exercism)
# Replace -'s with _'s to get a filename from the exercise name
string(REPLACE "-" "_" file ${exercise})
# Implementation could be only a header
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${file}.cpp)
set(exercise_cpp ${file}.cpp)
else()
set(exercise_cpp "")
endif()
# Build executable from sources and headers
add_executable(${exercise} ${file}_test.cpp ${exercise_cpp} ${file}.h)
# We need boost includes
target_include_directories(${exercise} PRIVATE ${Boost_INCLUDE_DIRS})
# We need boost libraries
target_link_libraries(${exercise} ${Boost_LIBRARIES})
# Run the tests on every build
add_custom_command(TARGET ${exercise} POST_BUILD COMMAND ${exercise})
endfunction()
exercism()
This was included with a set of exercises that ask you to write files that pass the tests thrown at it using Boost. I'm not familiar with cmake but I need the Xcode project that it creates to be runnable.
I have been working on a similar project, and I find that my "Products" in XCode are all red and the "Run" command is greyed out.
If you look at the "Identity and Type" pane for your test executable product, you'll probably find that the path listed is not the actual output path.
I have made this match and the run command be enabled by tinkering with the build settings. The "Build Products Path" for the top-level project seems to determine what path is expected in the "Products" list. The "Build Products Path" for each individual target seems to determine where the product output actually goes. So by manually changing these until the expected and actual output match, you can enable your "Run" command.
There's probably an adjustment to the CMake file that will automate this, but I haven't figured out what it is yet.