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
Related
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.
I have the Nlhomann JSON library as a sub-project in a subdirectory in my CMake-based parent project. The library is referenced by my CMakeLists.txt which is essentially defined as below. The problem is that when building the whole project this library somehow adds an unwanted -std=gnu++11 compiler flag after my already specified -std=c++2a flag. This addition makes the compilation to fail as my source code requires the latter.
Do you know how to modify my CMakeLists.txt in order to:
remove this specific flag specified in the json sub-project
force the compilation of the sub-project to use the C++ standard I want
An answer to either one is okay, but if you can answer both, that would be much appreciated.
# CMakeLists.txt
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_compile_options(-std=c++2a) # repetitive probably
set(JSON_Install OFF CACHE INTERNAL "") # disable installation
add_subdirectory(json)
add_executable(${PROJECT_NAME}
"main.cpp"
)
target_link_libraries(${PROJECT_NAME} PRIVATE nlohmann_json::nlohmann_json)
I tried this (ugly) solution, but it did not work:
get_target_property(_opt_old nlohmann_json INTERFACE_COMPILE_OPTIONS)
string(REPLACE "gnu++11" "c++2a" _opt_new "${_opt_old}")
set_target_properties(nlohmann_json PROPERTIES INTERFACE_COMPILE_OPTIONS "${_opt_new}")
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()
I'm trying to use cmake to build my little project using protocol buffers.
There's a root directory with a number of subdirectories with a number of libraries and executables. My first thought was to have my .proto-files in a subdirectory, but when I read this answer I made a library out of it instead. But when I try to include a messages header in my executable it can't find it.
Error message:
fatal error: msgs.pb.h: No such file or directory
#include "msgs.pb.h"
^
compilation terminated.
I'm running it by creating a dir "build" and then "cmake .. && make" from inside it.
I've looked and it seems the generated files get put in build/messages, so I could do include_directories(build/messages) but that doesn't seem...proper. Is there a proper way of doing this with protobuf? The reason I want the messages file in their own folder is they they'll be used in a lot of different small executables.
Any other general tips for improvements to my CMake-structure is also appreciated :)
Directories:
root
messages
core
server
root/CMakeLists.txt:
project(lillebror)
cmake_minimum_required(VERSION 2.8)
cmake_policy(SET CMP0015 NEW)
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
find_package(Boost COMPONENTS date_time log thread system)
find_package(Protobuf REQUIRED)
if(Boost_FOUND)
add_definitions(-std=c++11)
add_subdirectory(messages)
add_subdirectory(core)
add_subdirectory(server)
add_subdirectory(testserver)
endif()
messages/CMakeLists.txt:
file(GLOB ProtoFiles "${CMAKE_CURRENT_SOURCE_DIR}/*.proto")
PROTOBUF_GENERATE_CPP(ProtoSources ProtoHeaders ${ProtoFiles})
add_library(messages STATIC ${ProtoSources} ${ProtoHeaders})
target_link_libraries(messages ${Boost_LIBRARIES} ${PROTOBUF_LIBRARY})
core/CMakeLists.txt:
aux_source_directory(src SRC_LIST)
add_library(core STATIC ${SRC_LIST})
target_link_libraries(core messages ${Boost_LIBRARIES})
server/CMakeLists.txt:
aux_source_directory(src SRC_LIST)
include_directories(../messages) <---- I thought this would sove my problem
include_directories(../core/src)
link_directories(../core/build)
add_executable(server ${SRC_LIST})
target_link_libraries(server core ${Boost_LIBRARIES})
server/main.cpp:
#include "msgs.pb.h"
int main()
{
return 0;
}
I think the problem here is that the PROTOBUF_GENERATE_CPP function sets up the .pb.h and .pb.cc files to exist in the build tree, not in the source tree.
This is good practice (not polluting the source tree), but it means that your call include_directories(../messages) is adding the wrong value to the search paths. This is adding the source directory "root/messages", whereas you want "[build root]/messages".
You could probably just replace that line with:
include_directories(${CMAKE_BINARY_DIR}/messages)
However, a more robust, maintainable way might be to set the required include path inside the messages/CMakeLists.txt. To expose this value to the parent scope, this would need to either use set(... PARENT_SCOPE) or:
set(ProtobufIncludePath ${CMAKE_CURRENT_BINARY_DIR}
CACHE INTERNAL "Path to generated protobuf files.")
Then in the top-level CMakeLists.txt, you can do:
include_directories(${ProtobufIncludePath})
If your messages library itself needs to #include the generated protobuf files (this would be normal), then it too should have a similar include_directories call.
Having said all that, if you can specify CMake v2.8.12 as the minimum, you can use the target_include_directories command instead.
In messages/CMakeLists.txt after the add_library call, you'd simply do:
target_include_directories(messages PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
Then any other target which depends on messages automatically has the appropriate "messages" include dirs added to its own - you don't need to explicitly call include_directories at all.
I have a simple project going that uses CMake and gtest. I have a basic CMakeLists.txt file working, but I want to get a better understanding of how to use multiple CMakeLists.txt's and connect them. The code so far for the project is like this:
https://github.com/dmonopoly/writeart/tree/10b62048e6eb6a6ddd0658123d85ce4f5f601178
For quicker reference, the only CMakeLists.txt file (in the project root) that I take advantage of has this inside:
cmake_minimum_required(VERSION 2.8)
# Options
option(TEST "Build all tests." OFF) # makes boolean 'TEST' available
# Make PROJECT_SOURCE_DIR, PROJECT_BINARY_DIR, and PROJECT_NAME available
set(PROJECT_NAME MyProject)
project(${PROJECT_NAME})
set(CMAKE_CXX_FLAGS "-g") # -Wall")
#set(COMMON_INCLUDES ${PROJECT_SOURCE_DIR}/include) if you want your own include/ directory
# then you can do include_directories(${COMMON_INCLUDES}) in other cmakelists.txt files
################################
# Normal Libraries & Executables
################################
add_library(standard_lib Standard.cpp Standard.h)
add_library(converter_lib Converter.cpp Converter.h)
add_executable(main Main.cpp)
target_link_libraries(main standard_lib converter_lib)
################################
# Testing
################################
if (TEST)
# This adds another subdirectory, which has project(gtest)
add_subdirectory(lib/gtest-1.6.0)
enable_testing()
# Include the gtest library
# gtest_SOURCE_DIR is available due to project(gtest) above
include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
##############
# Unit Tests
##############
# Naming
set(UNIT_TESTS runUnitTests)
add_executable(${UNIT_TESTS} ConverterTest.cpp)
# standard linking to gtest stuff
target_link_libraries(${UNIT_TESTS} gtest gtest_main)
# extra linking for the project
target_link_libraries(${UNIT_TESTS} standard_lib converter_lib)
# This is so you can do 'make test' to see all your tests run, instead of manually running the executable runUnitTests to see those specific tests.
add_test(NAME myUnitTests COMMAND runUnitTests)
endif()
My goal is to move Standard.cpp and Standard.h into lib/. The moment I do this, though, I find the ordering of what I do in my CMakeLists.txt complicated. I need the library for my gtest setup, but the library would have to get made in lib/CMakeLists.txt, right? Wouldn't it easily become really complicated to find where all your libraries are and executables are as you would have to look through all your CMakeLists.txt's?
If I am missing something conceptually, or if there is a good example I could use to solve this easily, that would be great.
Help appreciated, and thanks in advance.
If you don't want to use multiple CMakeLists.txt files, don't.
################################
# Normal Libraries & Executables
################################
add_library(standard_lib lib/Standard.cpp lib/Standard.h)
add_library(converter_lib lib/Converter.cpp lib/Converter.h)
# Main.cpp needs to know where "Standard.h" is for the #include,
# so we tell it to search this directory too.
include_directories(lib)
If you do want multiple CMakeLists.txt, you'd move it out:
# Main CMakeLists.txt:
add_subdirectory(lib)
include_directories (${standard_lib_SOURCE_DIR}/standard_lib)
link_directories (${standard_lib_BINARY_DIR}/standard_lib)
and in /lib/CMakeLists.txt:
add_library (standard_lib Standard.cpp)
Here's an example.