Cmake project folder structure - c++

I used qmake but I'd like to experiment with cmake..
I would like to work with a project structure like the following:
Myproject
-- build
-- release
-- debug
-- src
-- mysource1.cpp
-- mysource1.h
-- mysource2.cpp
-- mysource2.h
...
-- main.cpp
myexecutable_release
myexecutable_debug
CMakeLists.txt
How do I set CMakeLists.txt to use the above structure ? How do I associate debug and release configuration using QtCreator ?
Or, as I don't have any particular complex need, should I just forget it and continue using qmake ? (it's a scientific software for linux/windows)
BDW: yes, I have read the CMake tutorial, but instead of starting from basic things like this question they already introduce complex topics like introspection and generators.. maybe different audience ??

project (Myproject)
set(sources src/mysource1.cpp
src/mysource2.cpp
src/mysource1.h
src/mysource2.h
src/main.cpp
)
add_executable(myexecutable ${sources})
In the folder build run
cmake ..
and build. For gcc debug builds, see this answer. For additional options (e.g. different generators) consult cmake documentation.

Related

Use CMake to build dependency (oneTBB as git submodule) as dynamic library?

I have a project that depends on Intel's oneTBB. My project is structured as follows:
external/
| - CMakeLists.txt
| - oneTBB/ (this is a git submodule)
| - ...
include/
lib/
include/
CMakeLists.txt
I currently get things to compile by manually building oneTBB and installing it inside a prefix directory located at external/oneTBB/prefix by running the following (bash) commands:
cd oneTBB
mkdir -p prefix
mkdir -p build
cd build
cmake -DCMAKE_INSTALL_PREFIX=../prefix -DTBB_TEST=OFF ..
cmake --build .
cmake --install .
I then simply include and link using this prefix. (I got this from following the oneTBB READMEs)
While this works without issue, I'm currently trying to clean up my CMake such that its easier to build on Windows as well. Ideally, I'm looking to get to a point where I can simply run:
mkdir build
cd build
cmake ..
cmake --build .
and my project will build itself and all dependencies.
I got this working with other dependencies such as glfw and eigen by simply adding (to the CMakeLists.txt in external/:
add_subdirectory(glfw)
add_subdirectory(eigen)
But adding add_subdirectory(oneTBB) throws a LOT of warnings, starting with:
CMake Warning at external/oneTBB/CMakeLists.txt:116 (message):
You are building oneTBB as a static library. This is highly discouraged
and such configuration is not supported. Consider building a dynamic
library to avoid unforeseen issues.
-- TBBBind build targets are disabled due to unsupported environment
-- Configuring done
CMake Warning (dev) at external/oneTBB/src/tbb/CMakeLists.txt:15 (add_library):
Policy CMP0069 is not set: INTERPROCEDURAL_OPTIMIZATION is enforced when
enabled. Run "cmake --help-policy CMP0069" for policy details. Use the
cmake_policy command to set the policy and suppress this warning.
INTERPROCEDURAL_OPTIMIZATION property will be ignored for target 'tbb'.
This warning is for project developers. Use -Wno-dev to suppress it.
I have no need to build oneTBB as a static library.
Am I doing something wrong in my attempt? Really all I need is for oneTBB to be built as a dynamic library and placed somewhere I can link it to (without installing it on the system overall)
Similar question:
I am also including the METIS library which depends on GKlib. Currently I'm doing this in a similar way to what I did for oneTBB where I manually build each using the following script:
# Setup the GKlib library:
cd GKlib
mkdir -p prefix
mkdir -p build
cd build
cmake -DCMAKE_INSTALL_PREFIX=../prefix ..
cmake --build . -j
cmake --install .
cd ../../
# Setup the METIS library:
cd METIS
mkdir -p prefix
make config prefix=../prefix gklib_path=../GKlib/prefix #(GKLib path is done from root, install path done relative to build)
make install -j
cd ../
When I try to add them using:
add_subdirectory(GKlib)
add_subdirectory(METIS)
it throws errors that METIS cannot find GKlib:
CMake Error at external/METIS/CMakeLists.txt:50 (add_subdirectory):
add_subdirectory given source "build/xinclude" which is not an existing
directory.
While I recognize this is a separate issue, I figured to include it here as it is related to my issues with add_subdirectory()
Many projects expect that you invoke CMake on them separately instead of adding them into an existing project with add_subdirectory. While there might be a way to make add_subdirectory work with oneTBB, there is an easier way.
CMake has a function that allows you to download, build, and install external projects at build time: ExternalProject_Add.
Here's an example for spdlog, taken straight from one of my own projects:
# project_root/thirdparty/spdlog/CMakeLists.txt
string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_UPPER)
ExternalProject_Add(spdlog-project
GIT_REPOSITORY https://github.com/gabime/spdlog
GIT_TAG edc51df1bdad8667b628999394a1e7c4dc6f3658
GIT_SUBMODULES_RECURSE ON
GIT_REMOTE_UPDATE_STRATEGY CHECKOUT
INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/install"
LIST_SEPARATOR |
CMAKE_CACHE_ARGS
"-DCMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}:STRING=${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}"
"-DCMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER}:STRING=${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}"
"-DCMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER}:STRING=${CMAKE_EXE_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}"
"-DCMAKE_SHARED_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER}:STRING=${CMAKE_SHARED_LINKER_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}"
"-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}"
"-DCMAKE_INSTALL_PREFIX:STRING=<INSTALL_DIR>"
"-DSPDLOG_BUILD_EXAMPLE:BOOL=OFF"
)
add_library(ext-spdlog INTERFACE)
add_dependencies(ext-spdlog spdlog-project)
ExternalProject_Get_property(spdlog-project INSTALL_DIR)
target_include_directories(ext-spdlog SYSTEM INTERFACE "${INSTALL_DIR}/include")
target_link_directories(ext-spdlog INTERFACE "${INSTALL_DIR}/lib")
target_link_libraries(ext-spdlog INTERFACE spdlog$<$<CONFIG:Debug>:d>)
After that, my project simply links against the created library target:
target_link_libraries(my_project ext-spdlog)
To adapt this for oneTBB, you have to switch out the repository URL and commit hash, and add your own CMake definitions (i.e. "-DTBB_TEST=OFF"). Depending on how oneTBB has its include and library directories set up, you may also have to change the target_include_directories and/or target_link_directories lines. I haven't looked this up yet, but I'm sure you can figure it out.
This works regardless of whether the external project is built as a static or shared library. You shouldn't use git submodules, though - instead, let CMake do the downloading. (It'll only download and build once; subsequent builds will not re-build the external project if it's already built and up-to-date.)
I have no need to build oneTBB as a static library. Am I doing something wrong in my attempt? Really all I need is for oneTBB to be built as a dynamic library and placed somewhere I can link it to (without installing it on the system overall)
All your diagnostic messages indicate that it's actually being configured to be built as a static library, and additional clues point to the probability that you've set BUILD_SHARED_LIBS to false in the scope where you add_subdirectory(oneTBB).
CMake Warning at external/oneTBB/CMakeLists.txt:116 (message):
You are building oneTBB as a static library. This is highly discouraged
and such configuration is not supported. Consider building a dynamic
library to avoid unforeseen issues.
If you look in oneTBB's CMakeLists.txt file, you'll the following:
if (NOT DEFINED BUILD_SHARED_LIBS)
set(BUILD_SHARED_LIBS ON)
endif()
if (NOT BUILD_SHARED_LIBS)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
message(WARNING "You are building oneTBB as a static library. This is highly discouraged and such configuration is not supported. Consider building a dynamic library to avoid unforeseen issues.")
endif()
And then right after that, you get
-- TBBBind build targets are disabled due to unsupported environment
The corresponding section of oneTBB's CMakeLists.txt file is:
if (TBB_FIND_PACKAGE OR TBB_DIR)
...
else()
if (APPLE OR NOT BUILD_SHARED_LIBS)
message(STATUS "TBBBind build targets are disabled due to unsupported environment")
else()
add_subdirectory(src/tbbbind)
endif()
...
Both of these clues indicate that in the variable scope at which you add_subdirectory(oneTBB), BUILD_SHARED_LIBS is set to a falsy value.
Set BUILD_SHARED_LIBS it to a truthy value (Ex. 1, TRUE, YES, ON, etc.) before doing add_subdirectory(oneTBB) and then restore the previous value afterward.
Ex.
set(BUILD_SHARED_LIBS_TEMP "${BUILD_SHARED_LIBS}")
set(BUILD_SHARED_LIBS YES)
add_subdirectory(oneTBB)
set(BUILD_SHARED_LIBS "${BUILD_SHARED_LIBS_TEMP}")
unset(BUILD_SHARED_LIBS_TEMP)

How to use DEBUG macro with cmake?

I'm new to cmake, so up front, my apologies this question is too basic ..
Question is,
I have logs under #ifdef DEBUG which I want to print only for debug build.
Something like this ..
void func () {
// some code here
#ifdef DEBUG
print_log(...) // this portion should execute only for debug builds
#endif
// some code here
}
How can I achieve this with cmake ?
I have already looked at #ifdef DEBUG with CMake independent from platform and cmakelists debug flag not executing code within "ifdef DEBUG", suggestions here don't seem to work for me.
(project is on Linux platform)
Modern CMake uses a target-based approach which allows you to specify settings that are restricted to a target as opposed to being global (and affecting all targets). This then gives you the control to specify how the state from targets propagates transitively to dependent targets so as to reduce the visible scope of the state (include paths, library dependencies, compiler defines, compiler flags, etc) to dependent targets. The approach you decide to go with will depend largely on how complex your application is, for instance, how many targets (executable & libraries) exist in the system. The more complex the system the more benefits you in terms of reduced complexity and compile time that you will get from using the target-based approach. In the simplest case to set this up with the modern target based CMake approach you could use the following (where exe is the name of your executable:
add_executable(exe "")
target_sources(exe
PRIVATE
main.cpp
)
target_compile_definitions(exe
PRIVATE
# If the debug configuration pass the DEBUG define to the compiler
$<$<CONFIG:Debug>:-DDEBUG>>
)
Added set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") to CMakeLists.txt
Created Debug/ directory & cd Debug
cmake -DCMAKE_BUILD_TYPE="Debug" ../
Worked !

CMake Interface dependency with custom build type

So, I've found really strange behaviour in CMake creating dependency on target_link_library..
It's hard to explain in one sentence, so here is a list of requirements (I hope this all will make sence in the end)
your project must have custom build type defined through CMAKE_CONFIGURATION_TYPES ('Tools' in this example)
you must have at least 3 targets:
executable (or simply main target) (test-exe in this example)
interface library which link to main target (this could be something other than INTERFACE library, but the next target must be linked to it via interface property only) (test-env in this example)
static library which links to the interface library with specific generator expression, which is depends on custom build type (something like that 'target_link_libraries(test-env INTERFACE $<$CONFIG:Tools:test-lib>)') (test-lib in this example)
Here is the code of the CMakeLists.txt file to make it little bit clearer:
cmake_minimum_required(VERSION 3.19.0 FATAL_ERROR)
project(multiconfiguration-test LANGUAGES CXX)
set(CMAKE_CONFIGURATION_TYPES Debug Release Tools)
set(CMAKE_CXX_FLAGS_TOOLS ${CMAKE_CXX_FLAGS_DEBUG})
set(CMAKE_EXE_LINKER_FLAGS_TOOLS ${CMAKE_EXE_LINKER_FLAGS_DEBUG})
set(CMAKE_STATIC_LINKER_FLAGS_TOOLS ${CMAKE_STATIC_LINKER_FLAGS_DEBUG})
add_library(test-env INTERFACE)
# EXCLUDE_FROM_ALL used to not build this target by default
add_library(test-lib STATIC EXCLUDE_FROM_ALL "test-lib.cpp")
target_link_libraries(test-env INTERFACE $<$<CONFIG:Tools>:test-lib>)
add_executable(test-exe "test-exe.cpp")
target_link_libraries(test-exe PRIVATE test-env)
(Files test-exe.cpp and test-lib.cpp are trivial, test-lib.cpp has a simple function, and test-exe.cpp declares this function and then calls it from main.)
When you would try to build this project with visual studio 2019/2017 generators (with "Tools" as configuration type of course: cmake --build . --config Tools), you will have next error:
LINK : fatal error LNK1104: cannot open file 'Tools\test-lib.lib' [<none_important_path_to_the_project>\test-exe.vcxproj]
And, what is important, you will not see in the terminal target test-lib being build.
So, what happened is target test-exe knows it must be linked against test-lib, but the build system doesn't know that target test-exe is dependent on target test-lib.
And now the most strange thing! If we will link this library like that: target_link_libraries(test-env INTERFACE $<$<CONFIG:Debug>:test-lib>) (so build type must be Debug), and still build project with Tools as a build type, you will see in the terminal that target test-lib is now building! (yes we have link error because test-exe can't find the function which is defined in test-lib, but this is at least expected)
So, what actually happens, the link flag of the target test-exe is correctly depends on the generator expression BUT, the actual build dependency, somehow, transforms any custom build type in this generator expression to the Debug.
Again this only happens with requirements I pointed up above, so it's not only the fault of generator expression, it's also connected to the interface dependency as well..
If we can't break any of the requirements, one possible solution will be to add direct dependency of test-lib target to test-env (like that: add_dependecies(test-env test-lib)), but this is not perfect, because even if we will use test-lib only then where is Tools as build type, we still will build this library each time, which can be undesired behavior.
I'm not really asking for solution here (but if you have one please share), I'm asking for the explanation why this even happening? Is this a bug or a really strange feature?
EDIT Small update I've found just now:
It must not even be the custom build type. The same bug can be encountered even with Release build type, so the final code can look as simple as this:
cmake_minimum_required(VERSION 3.19.0 FATAL_ERROR)
project(multiconfiguration-test LANGUAGES CXX)
add_library(test-env INTERFACE)
add_library(test-lib STATIC EXCLUDE_FROM_ALL "test-lib.cpp")
target_link_libraries(test-env INTERFACE $<$<CONFIG:Release>:test-lib>)
add_executable(test-exe "test-exe.cpp")
target_link_libraries(test-exe PRIVATE test-env)
and be build with next command: cmake --build . --config Release
Looks like a bug with the Visual Studio generator to me. I've just tested the Ninja Multi-Config generator both on Linux and on Windows and there cmake --build <build-dir> --config Release works just fine.

CMake Nested Projects and Targets

i have a problem on building specific Visual Studio Projects from a CMake source tree. Immagine we have multiple targets in various subdirectories. In one sub-directory we initiate a new project (and solution). The projects in this project targets should be part of ALL_BUILD, but not in the parent ALL_BUILD.
In short:
project(Main)
add_executable(MainApplication ...)
target_link_libraries(MainApplication PRIVATE Library_A)
add_subdirectory(Library_A)
add_library(Library_A STATIC ...)
add_subdirectory(Other_Related_Stuff_Using_MainApplication)
project(OtherRelatedStuff)
add_custom_target(OtherTarget ... DEPENDS MainApplication)
Now with a Visual Studio generator we get two Solution Files. That is right and correct. In both solutions we have these three projects (as OtherReleatedStuff depends on MainApplication beeing built and OtherRelatedStuff is part of the Main-Project).
Now: ALL_BUILD builds all projects.
When i set
set_target_properties(OtherTarget PROPERTIES EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1)
...in both solutions OtherTarget is disabled.
What i would like to do is:
Solution 1:
In Main.Sln there should be not OtherTarget. In OtherRelatedStuff.sln there could be MainApplication, but should not build. Maybe there shouldn't be a MainApplication as well.
Solution 2:
In Main.Sln, OtherTarget should never build (excluded from build). In OtherRelatedStuff.sln, OtherTarget should build, but the dependencies don't or should be even visible.
Is there a solution for it?
All targets found in one CMakeLists.txt source tree will generate a Visual Studio project file. And - as you found out already - EXCLUDE_FROM_ALL and EXCLUDE_FROM_DEFAULT_BUILD are set per target/project or directory.
So you could split/link your projects with the use of ExternalProject_Add(). And since Other_Related_Stuff depends on MainApplication you should call ExternalProject_Add() in Other_Related_Stuff's CMake file.
So my recommendation would look like this:
CMakeLists.txt
project(Main)
add_subdirectory(Library_A)
add_executable(MainApplication ...)
target_link_libraries(MainApplication PRIVATE Library_A)
Other_Related_Stuff/CMakeLists.txt
project(OtherRelatedStuff)
ExternalProject_Add(
MainApplication
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..
PREFIX MainApplication
)
add_custom_target(OtherTarget ... DEPENDS MainApplication)
So now your main CMakeLists.txt is Other_Related_Stuff/CMakeLists.txt and it does generate your second solution file in a MainApplication subdirectory.
Reference
CMake share library with multiple executables

Linking different libraries for Debug and Release builds in Cmake on windows?

So I've got a library I'm compiling and I need to link different third party things in depending on if it's the debug or release build (specifically the release or debug versions of those libraries). Is there an easy way to do this in Cmake?
Edit: I should note I'm using visual studio
According to the CMake documentation:
target_link_libraries(<target> [lib1 [lib2 [...]]] [[debug|optimized|general] <lib>] ...)
A "debug", "optimized", or "general"
keyword indicates that the library
immediately following it is to be used
only for the corresponding build
configuration.
So you should be able to do this:
add_executable( MyEXE ${SOURCES})
target_link_libraries( MyEXE debug 3PDebugLib)
target_link_libraries( MyEXE optimized 3PReleaseLib)
Somehow the answer from #Mike Willekes got CMake linking in the same target both release and debug for me :(
I only got this working by setting both configurations in one line, as suggested by #sakra in a related question - and doing so for every library that needed to be linked:
target_link_libraries ( app
debug ${Boost_FILESYSTEM_LIBRARY_DEBUG}
optimized ${Boost_FILESYSTEM_LIBRARY_RELEASE} )
target_link_libraries ( app
debug ${Boost_LOG_LIBRARY_DEBUG}
optimized ${Boost_LOG_LIBRARY_RELEASE} )
target_link_libraries ( app
debug ${Boost_PROGRAM_OPTIONS_LIBRARY_DEBUG}
optimized ${Boost_PROGRAM_OPTIONS_LIBRARY_RELEASE} )
# ...
I would like to add a few notes to the previous answers.
If you need to create a list of multiple files you want to link and store that in a cache variable then you need to add the optimized or debug specified before each and every library. This can be especially useful for larger makefiles/projects.
So for example you could do something like this:
set( MyFavLib_LIBRARIES
debug debug/module1.lib optimized release/module1.lib
debug debug/module2.lib optimized release/module2.lib )
target_link_libraries( app ${MyFavLib_LIBRARIES} )
What worked for me was to use $(Configuration) macro in a lib path provided to cmake.
So, assuming libs are stored in separate, correctly named folders, e.g.:
C:\boost\lib\Debug\libfoo.lib
C:\boost\lib\Release\libfoo.lib
You can then call cmake with:
cmake -G "Visual Studio 10 2010" -DBOOST_LIBRARYDIR=C:\boost\lib\$(Configuration)\libfoo.lib
That'll generate .vcxproj with Additional Dependencies including C:\boost\lib\$(Configuration)\libfoo.lib, what is evaluated to either C:\boost\lib\Release\libfoo.lib or C:\boost\lib\Debug\libfoo.lib depending on a chosen Configuration.
target_link_libraries with optimize and debug doesn't work for me. I follow the post of Mike Willekes, but release config also import debug library file in visual studio. Then, I use the following cmake code to solving this problem
add_library(BoostLib STATIC IMPORTED)
set_target_properties(BoostLib PROPERTIES
IMPORTED_LOCATION_DEBUG ${BoostLibPath}/debug/module1.lib
IMPORTED_LOCATION_RELEASE ${BoostLibPath}/release/module1.lib)
target_link_libraries(AppTarget BoostLib)