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

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()

Related

pybind11 and another libs in one project [duplicate]

I want to be able to call my C++ code as a python package. To do this I am using pybind11 with CMakelists (following this example https://github.com/pybind/cmake_example). My problem is that I have to include GSL libraries in the compilation of the code, and these need an explicit linker -lgsl .
If I were just to compile and run the C++ without wrapping it with python, the following Cmakelists.txt file does the job
cmake_minimum_required(VERSION 3.0)
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14")
project(myProject)
add_executable(
myexecutable
main.cpp
function1.cpp
)
find_package(GSL REQUIRED)
target_link_libraries(myexecutable GSL::gsl GSL::gslcblas)
but when using pybind11 the template I found doesn't allow the add_executable therefore target_link_libraries doesn't work.
I have trie this
project(myProject)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED YES) # See below (1)
# Set source directory
set(SOURCE_DIR "project")
# Tell CMake that headers are also in SOURCE_DIR
include_directories(${SOURCE_DIR})
set(SOURCES "${SOURCE_DIR}/functions.cpp")
# Generate Python module
add_subdirectory(lib/pybind11)
pybind11_add_module(namr ${SOURCES} "${SOURCE_DIR}/bindings.cpp")
FIND_PACKAGE(GSL REQUIRED)
target_link_libraries(GSL::gsl GSL::gslcblas)
but this produces errors in the building.
Any idea ?
Function pybind11_add_module creates a library target, which can be used for link added module with other libraries:
pybind11_add_module(namr ${SOURCES} "${SOURCE_DIR}/bindings.cpp")
target_link_libraries(namr PUBLIC GSL::gsl GSL::gslcblas)
This is explicitely stated in documentation:
This function behaves very much like CMake’s builtin add_library (in fact, it’s a wrapper function around that command). It will add a library target called <name> to be built from the listed source files. In addition, it will take care of all the Python-specific compiler and linker flags as well as the OS- and Python-version-specific file extension. The produced target <name> can be further manipulated with regular CMake commands.

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

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.

Avoid repetitive CMake code with multiple targets

In CMake projects with multiple targets, I often find myself repeating some code over and over again. See this example:
cmake_minimum_required(VERSION 3.12)
project(rapid_benchmark)
set(CMAKE_CXX_STANDARD 17)
add_executable(benchmark main.cpp)
add_executable(benchmark_parallel main.cpp) # enable parallel processing
add_executable(benchmark_openmp main.cpp) # enable openmp
add_executable(benchmark_static main.cpp) # enable static optimizations
# ... more targets
# Adding basic dependecies
find_package(abc)
if(abc_FOUND)
target_link_libraries(benchmark abc::abc)
target_link_libraries(benchmark_parallel abc::abc)
target_link_libraries(benchmark_openmp abc::abc)
# ... all other targets ...
# The same for the includes etc.
find_package(xyz)
if(xyz_FOUND)
target_link_libraries(benchmark xyz::xyz)
# ... all other targets ...
This is annoying and error prone, especially when adding new targets.
How can I avoid repetitive code with multiple targets in a CMake project?
For example, is there a way to put the targets into a kind of list and call target_link_libraries on that list?
Same as Bernhard said, but in CMake :)
macro(add_benchmark)
set(singleValue NAME)
set(multipleValues SOURCES)
cmake_parse_arguments(local "" "${singleValue}" "${multipleValues}" ${ARGN})
set (libraries)
if (abc_FOUND)
set (libraries ${libraries} abc::abc)
endif()
if (xyz_FOUND)
set (libraries ${libraries} xyz::xyz)
endif()
add_executable(${local_NAME} ${local_SOURCES})
target_link_libraries(${local_NAME} ${libraries})
endmacro()
Then you can simply call it as any other CMake command:
add_benchmark(
NAME benchmark
SOURCES main.cpp
)
add_benchmark(
NAME benchmark_other
SOURCES main.cpp other.cpp
)
I would consider to create a macro, that you can call for each application, which does the both the compilation and the linking
You will probably find that you want to use cmake-parse-arguments
Something like this...
https://cmake.org/cmake/help/latest/command/foreach.html
set(targets a b c d e)
foreach(loop_var IN LISTS targets)
add_executable($loop_var main.cpp)
endforeach(loop_var)
Combined with a macro that does a lot of stuff will keep it manageable.
Further details:
Looping over a string list

Finding Direct3D 12 using CMake

So I've been trying to learn CMake and use it with C++. I'd like to have a go at creating a portable game engine which uses Direct3D 12 on Windows.
Currently, I have the following CMakeLists.txt for my project:
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(PsychoEngineCore)
set(SRCS_CXX ${CMAKE_CURRENT_LIST_DIR})
include(${CMAKE_CURRENT_SOURCE_DIR}/src/dir_src.cmake)
set(LIB_TYPE "STATIC" CACHE STRING "Static or Dynamic Linking")
if(${CMAKE_CXX_COMPILER_ID} STREQUAL GNU OR
${CMAKE_CXX_COMPILER_ID} STREQUAL Clang)
set(warnings "-Wall -Wextra -Werror")
elseif(${CMAKE_CXX_COMPILER_ID} STREQUAL MSVC)
set(warnings "/W4 /WX /EHsc")
endif()
if(NOT CONFIGURED_ONCE)
set(CMAKE_CXX_FLAGS "${warnings}"
CACHE STRING "Flags used by the compiler during all build types." FORCE)
endif()
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
# Do we want static libraries?
# When STATIC_LINKING is TRUE, then cmake looks for binaries ending in ".a".
# THIS IS FOR LINUX ONLY!
if(LIB_TYPE EQUAL STATIC)
if (UNIX AND NOT APPLE)
set(CMAKE_FIND_LIBRARY_SUFFIXES(".a"))
endif(UNIX AND NOT APPLE)
set(CMAKE_EXE_LINKER_FLAGS "-static")
set_target_properties(surface PROPERTIES LINK_SEARCH_END_STATIC 1)
endif(LIB_TYPE EQUAL STATIC)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include)
include(GenerateExportHeader)
add_library(${PROJECT_NAME} ${LIB_TYPE} ${SRCS_CXX})
GENERATE_EXPORT_HEADER(
${PROJECT_NAME}
EXPORT_MACRO_NAME PE_API
EXPORT_FILE_NAME ${CMAKE_CURRENT_SOURCE_DIR}/include/Engine/API/${PROJECT_NAME}Export.hpp
DEPRECATED_MACRO_NAME PE_API_DEP
STATIC_DEFINE PE_STATIC
)
Currently I have my d3d12.lib file in the following location:
C:\Program Files (x86)\Windows Kits\10\Libs\10.0.16299.0\um\x64
Is there a way to keep the path dynamic and preferably "update" if a newer version is available on said system?
Thanks!
Turning my comment into an answer
Generally speaking, just add a
target_link_libraries((${PROJECT_NAME} d3d12.lib)
The Windows SDK (where the Direct3D SDK is now part of) is in the standard search paths e.g. of the linker for libraries. So it's found automatically by the MSVC compiler and linker.
And I don't think that upgrading to a newer (yet unknown) API version automatically is a good idea. You're writing your program for a specific API version.
Working Example
Here is a minimal working example (Tested VS2017 15.5.5, CMake 3.9.0):
cmake_minimum_required(VERSION 3.0)
project(Direct3DExample)
find_package(Git REQUIRED)
set(_path "${CMAKE_BINARY_DIR}/DirectX-Graphics-Samples/Samples/Desktop/D3D12HelloWorld/src/HelloTriangle")
if (NOT EXISTS "${_path}")
execute_process(
COMMAND "${GIT_EXECUTABLE}" clone https://github.com/Microsoft/DirectX-Graphics-Samples.git
)
endif()
file(GLOB _files "${_path}/*")
list(APPEND _shader ${_files})
list(FILTER _files EXCLUDE REGEX "\\.vcxproj|\\.hlsl")
list(FILTER _shader INCLUDE REGEX "\\.hlsl")
get_filename_component(_name "${_path}" NAME)
add_executable(${_name} WIN32 ${_files})
target_compile_definitions(${_name} PRIVATE "UNICODE" "_UNICODE")
target_link_libraries(${_name} PRIVATE "d3d12.lib" "dxgi.lib" "d3dcompiler.lib")
add_custom_command(
TARGET ${_name}
POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy ${_shader} $<TARGET_FILE_DIR:${_name}>
)
If you need more (compilers/linkers that are not finding the Windows SDK automatically), you may want to look the following find_package() config code:
https://github.com/Microsoft/DirectXShaderCompiler/blob/master/cmake/modules/FindD3D12.cmake

Cmake recompiles everything each time I add a new directory in include_directory()

(This question is quite similar to this one I asked but is not related to the GLOB subtleties. So separating the two problems would be clearer I think)
Each time I add or remove a folder in include_directory() (even empty) in my CMakeLists.txt, then typing
cmake .
make
triggers the whole project to be recompiled. Why and what can I do about it?
My actual CMakeLists.txt (note there is no GLOB):
cmake_minimum_required(VERSION 2.8)
cmake_policy(VERSION 2.8.12.2)
# Project
get_filename_component(ProjectName MyProject NAME)
project(${ProjectName})
# Compiler and options
set(CMAKE_CXX_COMPILER /usr/bin/clang++)
set(CLANG_COMPILE_FLAGS "-std=c++1y -stdlib=libc++ ")
# Type of build
set(CMAKE_BUILD_TYPE "Release")
# Build tool
set(CMAKE_GENERATOR "Unix Makefiles")
set(EXECUTABLE_OUTPUT_PATH ../bin/${CMAKE_BUILD_TYPE})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CLANG_COMPILE_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${CLANG_LINK_FLAGS}")
include_directories(
[all folders containing .h files here] # adding anything here triggers the whole project to recompile
)
add_executable(
${ProjectName}
[all .cpp and .h files here]
)
Command include_directories add compiler flags (e.g. -I/path/to/include/dir for gcc). So if you change include_directories => compiler flags changes => target need to be rebuild.
Example
To avoid regular recompilation of whole project you need to not modify include_directories often. As an example you can organize code like this:
CMakeLists.txt
Source/
- A/foo.hpp
- A/foo.cpp
- B/boo.hpp
- B/boo.cpp
Content of CMakeLists.txt:
include_directories(./Source)
add_executable(myexe A/foo.hpp A/foo.cpp B/boo.hpp B/boo.cpp)
Content of C++ files:
#include "A/foo.hpp"
#include "B/boo.hpp"