CMake with multiple sub projects building into one directory - c++

I'm not very familiar with CMake and still find it quite confusing. I have a project that has a server and client that I want to be able to run independent of each other but that builds together into the same directory (specifically the top level project build directory kind of like how games have the server launcher and game launcher in the same directory) Currently it just creates a builds directory in each sub project, so one in client, one in server etc.
This is my current project structure
.
├── CMakeLists.txt
├── builds
│   ├── debug
│   └── release
├── client
│   ├── CMakeLists.txt
│   ├── assets
│   └── source
│   └── Main.cpp
├── documentation
├── libraries
│   ├── glfw-3.3.7
│   └── glm
├── server
│   ├── CMakeLists.txt
│   └── source
│   └── Main.cpp
└── shared
├── PlatformDetection.h
├── Utility.h
├── events
└── platform
├── linux
├── macos
└── windows
This is my root CMake file
cmake_minimum_required(VERSION 3.20)
project(Game VERSION 1.0.0)
add_subdirectory(libraries/glfw-3.3.7)
add_subdirectory(client)
add_subdirectory(server)
Client CMake file
cmake_minimum_required(VERSION 3.20)
project(Launcher LANGUAGES CXX VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 23)
set(SOURCE_FILES source/Main.cpp ../shared/events/Event.h ../shared/Utility.h
source/Client.cpp source/Client.h ../shared/PlatformDetection.h ../shared/events/EventManagementSystem.cpp
../shared/events/EventManagementSystem.h)
set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
include_directories(${CMAKE_SOURCE_DIR}/libraries/glm)
include_directories(${CMAKE_SOURCE_DIR}/libraries/glfw-3.3.7/include/GLFW)
include_directories(${CMAKE_SOURCE_DIR}/shared)
add_executable(Launcher ${SOURCE_FILES})
target_link_libraries(Launcher LINK_PUBLIC glfw)
Server CMake file
cmake_minimum_required(VERSION 3.20)
project(ServerLauncher LANGUAGES CXX VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 23)
set(SOURCE_FILES source/Main.cpp ../shared/events/Event.h ../shared/Utility.h
../shared/PlatformDetection.h ../shared/events/EventManagementSystem.cpp
../shared/events/EventManagementSystem.h)
include_directories(${CMAKE_SOURCE_DIR}/libraries/glm)
include_directories(${CMAKE_SOURCE_DIR}/shared)
add_executable(ServerLauncher ${SOURCE_FILES})
How can I make the client and server build into the same directory? And can these cmake file structures be improved at all? They seem quite messy and all over the place to me though that may just be due to my unfamiliarity with CMake.

You cannot have multiple subdirectories use the same build directory, but that doesn't seem what you're trying to achieve.
Assuming you don't set the variable CMAKE_RUNTIME_OUTPUT_DIRECTORY anywhere in your project, and you don't specify the RUNTIME_OUTPUT_DIRECTORY target property for any of your targets by some other means, you could simply set the variable in the toplevel CMakeLists.txt before using add_subdirectory:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
add_subdirectory(...)
...
Note that for distributing the program you should be using install() logic:
Client CMakeLists.txt
...
install(TARGETS Launcher RUNTIME)
Server CMakeLists.txt
...
install(TARGETS ServerLauncher RUNTIME)
Note that you may need to add logic for installing dependencies.
Using those install commands allows you to use
cmake --install <build dir> --prefix <install dir>
to install the programs locally in the default directory for binaries on the system. Furthermore it's the basis for packaging your project using cpack.

Related

Cmake: How to statically link packages to shared library?

I want to create a .dll library with all its dependencies packed inside the .dll.
However, there seems to be no easy way to achieve that with Cmake. My setup:
cmake_minimum_required(VERSION 3.0.0)
project(Main VERSION 0.1.0)
add_library(Main SHARED Main.cpp)
find_package(libzippp REQUIRED)
target_link_libraries(Main PRIVATE libzippp::libzippp)
This will produce both Main.dll but also libzippp.dll.
I would like to have libzippp.dll packed (statically linked) into Main.dll.
Of course you can't pack one DLL into another. You have to make libzippp a static library in the first place. To do this, build libzippp with BUILD_SHARED_LIBS set to NO at the CMake command line. Then libzippp::libzippp will be a static library when you go to find_package it.
This is easy enough to show steps for:
$ git clone git#github.com:ctabin/libzippp.git
$ cmake -S libzippp -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=NO -DCMAKE_INSTALL_PREFIX=$PWD/local -DLIBZIPPP_BUILD_TESTS=NO
$ cmake --build build --target install
$ tree local
local/
├── include
│   └── libzippp
│   └── libzippp.h
├── lib
│   └── libzippp_static.a
└── share
└── libzippp
├── FindLIBZIP.cmake
├── libzipppConfig.cmake
├── libzipppConfigVersion.cmake
├── libzipppTargets.cmake
└── libzipppTargets-release.cmake

How to make my project find header file from my own separate library imported with FetchContent?

I'm trying to use my own library in a different project using CMake and FetchContent. My library is a static one that's built with CMake in a separate git repo, and it works fine when built with a demo in the same repo. When I use FetchContent to use it in a separate project it can't find the header files and I don't know how to include them.
dynamic-shadow-lib tree:
.
├── CMakeLists.txt
├── demo
│   ├── CMakeLists.txt
│   └── main.cpp
├── include
│   ├── line2D.hpp
│   ├── math.hpp
│   ├── mathplotUtil.hpp
│   ├── shape2D.hpp
│   ├── square2D.hpp
│   ├── triangle2D.hpp
│   └── vec2f.hpp
├── LICENSE
├── README.md
├── src
│   ├── line2D.cpp
│   ├── math.cpp
│   ├── square2D.cpp
│   ├── triangle2D.cpp
│   └── vec2f.cpp
└── tests
├── CMakeLists.txt
├── unit_tests
│   ├── CMakeLists.txt
│   ├── line2DTests.cpp
│   ├── main.cpp
│   ├── mathTests.cpp
│   ├── square2DTests.cpp
│   └── vec2fTests.cpp
└── visual_tests
├── CMakeLists.txt
├── main.cpp
└── visualTests.hpp
dynamic-shadow-lib root CmakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(
dynamic-shadows
VERSION 1.0
LANGUAGES CXX
)
# Build config variables
set(BUILD_DEMO FALSE)
set(BUILD_TESTS FALSE)
# Set C++ to version 14
set(CMAKE_CXX_STANDARD 14)
# Set binary destinations
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# Set header dir
set(HEADERS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
include_directories(${HEADERS_DIR})
# Set source dir
set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
# Set a name for the target
set(TARGET_LIB ${CMAKE_PROJECT_NAME}-lib)
# Make library ${TARGET_LIB}
add_library(
${TARGET_LIB} STATIC
${SOURCE_DIR}/math.cpp
${SOURCE_DIR}/vec2f.cpp
${SOURCE_DIR}/line2D.cpp
${SOURCE_DIR}/square2D.cpp
${SOURCE_DIR}/triangle2D.cpp
)
if (${BUILD_TESTS})
# Enable testing (GoogleTests)
include(CTest)
enable_testing()
add_subdirectory(tests)
endif()
if (${BUILD_DEMO})
# Demo
add_subdirectory(demo)
endif()
Project that I'm trying to include my lib in (tree):
.
├── CMakeLists.txt
├── include
│   ├── Game.hpp
│   └── Primitive.hpp
├── LICENSE
├── README.md
├── src
│   ├── Game.cpp
│   └── main.cpp
└── tests
├── CMakeLists.txt
└── main.cpp
Projects root CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
project(realtime-light-shadow-2D
LANGUAGES CXX
VERSION 1.0
)
# Set extra compiler flags
set(CMAKE_CXX_FLAGS "-W -Wall -std=c++14 -fopenmp")
# Set binary destinations
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# Dependencies
include(FetchContent)
set (FETCHCONTENT_QUIET FALSE)
# SFML
message (STATUS "Adding SFML..")
FetchContent_Declare(
sfml
GIT_REPOSITORY "https://github.com/SFML/SFML"
GIT_TAG 218154cf0098de3f495e31ced489da24b7318638 # Latest
GIT_PROGRESS TRUE
)
# No need to build audio and network modules
set(SFML_BUILD_AUDIO FALSE)
set(SFML_BUILD_NETWORK FALSE)
FetchContent_MakeAvailable(sfml)
# ImGui
message (STATUS "Adding ImGui")
FetchContent_Declare(
imgui
GIT_REPOSITORY https://github.com/ocornut/imgui
GIT_TAG 5c8f8d031166765d2f1e2ac2de27df6d3691c05a # Latest
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(imgui)
# ImGui-SFML
message (STATUS "Adding ImGui-SFML")
FetchContent_Declare(
imgui-sfml
GIT_REPOSITORY https://github.com/eliasdaler/imgui-sfml
GIT_TAG 4129d276d45845581b6ba99ede50db6f761e5089 # Latest
GIT_PROGRESS TRUE
)
set(IMGUI_DIR ${imgui_SOURCE_DIR})
set(IMGUI_SFML_FIND_SFML OFF)
set(IMGUI_SFML_IMGUI_DEMO ON)
FetchContent_MakeAvailable(imgui-sfml)
# dynamic-shadows-lib
message( STATUS "Adding dynamic-shadow-lib")
FetchContent_Declare(
dynamic-shadows-lib
GIT_REPOSITORY https://github.com/JesperGlas/dynamic-shadows-lib/
GIT_TAG 59c7884caac7cc98f902fd880ccb1c9b0f50cca0 # Latest
GIT_PROGRESS TRUE
)
FetchContent_MakeAvailable(dynamic-shadows-lib)
# Set header dir
set(HEADERS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
include_directories(${HEADERS_DIR})
# Set source dir
set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
set(TARGET_BIN ${CMAKE_PROJECT_NAME})
add_executable(
${TARGET_BIN}
${SOURCE_DIR}/main.cpp
${SOURCE_DIR}/Game.cpp
)
target_link_libraries(
${TARGET_BIN}
PUBLIC
sfml-system
sfml-graphics
ImGui-SFML::ImGui-SFML
dynamic-shadows-lib
)
When I try to build the project (sfml-dynamic-light-demo) I get the following error:
[build] /home/jesper/dev/cpp/sfml-dynamic-light-demo/include/Primitive.hpp:4:10: fatal error: vec2f.hpp: No such file or directory
[build] 4 | #include "vec2f.hpp"
Which tells me that the libraries include folder might not be available to the project. I've tried to include it as an install(DIRECTORY ${HEADER_DIR}) in the libs root CMakeLists, but it has no effect. I suspect I'm using it wrong since when I build the project I still get "No install step for 'dynamic-shadows-lib-populate'".
What am I doing wrong? If it's a concept that I'm missing, what should I read up on to make it work?
Thanks to the comments I was able to solve this. In my library (Built with CMake) I used the variable CMAKE_PROJECT_NAME when creating my library (CMAKE_PROJECT_NAME-lib). This variable references the top root CMakeLists.txt which resultet in my library getting the wrong name when I used it in another project. By changing all of those variables to PROJECT_NAME, and other similar ones, everything worked as it should.
TLDR: Make sure to use local variables when you want to use a CMake project as a subproject. (In this case PROJECT_NAME, instead of CMAKE_PROJECT_NAME etc...).

CMake not running tests if tests are defined in subdirectory [duplicate]

I have a project with the following structure:
linalg
├── build
├── CMakeLists.txt
├── docs
│   └── Doxyfile
├── include
│   └── linalg
│   └── vector3.hpp
├── src
│   ├── CMakeLists.txt
│   └── linalg
│   └── vector3.cpp
└── test
├── CMakeLists.txt
└── linalg
└── test_vector3.cpp
The file test_vector3.cpp is a gtest unit test file which provides two simple tests. The top level CMakeLists.txt simply sets up the includes and adds the src and test subdirectories:
cmake_minimum_required(VERSION 2.8)
project(linalg)
include_directories(include)
add_subdirectory(src)
add_subdirectory(test)
The src/CMakeLists.txt file compiles vector3.cpp into a static library:
cmake_minimum_required(VERSION 2.8)
add_library(linalg linalg/vector3.cpp)
The test/CMakeLists.txt file is based on the example provided in /usr/share/cmake-2.8/Modules/FindGTest.cmake:
cmake_minimum_required(VERSION 2.8)
enable_testing()
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})
add_executable(test_vector3 linalg/test_vector3.cpp)
target_link_libraries(test_vector3 linalg ${GTEST_BOTH_LIBRARIES} pthread)
add_test(test_vector3 test_vector3)
I then run the following:
cd build
cmake ..
make
I get the liblinalg.a library compiled correctly in to build/src and I get the test_vector3 executable compiled correctly in to build/test. I can run the test_vector3 executable and I get the output from googletest saying that all tests have passed, however if I run make test I get no output whatsoever and if I run ctest .. I get a message saying:
Test project /home/ryan/GitHub/linalg/build
No tests were found!!!
Is there something I am missing? Or have I just misunderstood how ctest works with gtest?
The crux of the problem is that enable_testing should be called from your top-level CMakeLists.txt in this case. Adding include(CTest) to your top-level CMakeLists.txt should fix this for you.
This would allow you to remove the enable_testing call in test/CMakeLists.txt, since the CTest submodule calls enable_testing internally.
Just to update this.
cmake in version 3.9 added support for GoogleTest integration with CTest.
So you can now get CTest to scrape all of the test macros in your test executable, not just the whole executable.
Example here:
https://gist.github.com/johnb003/65982fdc7a1274fdb023b0c68664ebe4

Error while including Caffe in C++ Project using cmake

I want to include caffe in my project. This is the folder structure of the project:
.
├── AUTHORS
├── ChangeLog
├── cmake
│   ├── FindCaffe.cmake
│   └── FindCUDA.cmake
├── CMakeLists.txt
├── CMakeLists.txt.user
├── data
│   └── info.plist
├── deep-app.pro.user
├── LICENSE.txt
├── README.md
├── [reference]
│   ├── deep-app.pro
│   ├── deep-app.pro.user
│   ├── deployment.pri
│   ├── main.cpp
│   ├── main.qml
│   ├── Page1Form.ui.qml
│   ├── Page1.qml
│   └── qml.qrc
└── src
├── CMakeLists.txt
├── code
│   └── main.cpp
├── icons.yml
└── res
├── assets
│   ├── assets.qrc
│   ├── book-open-page.svg
│   └── book-open.svg
├── icons
│   ├── action_home.svg
│   ├── action_list.svg
│   ├── action_search.svg
│   ├── action_settings.svg
│   ├── file_cloud_done.svg
│   ├── icons.qrc
│   ├── maps_place.svg
│   ├── navigation_check.svg
│   └── social_school.svg
└── qml
├── main.qml
├── Page1Form.ui.qml
├── Page1.qml
└── qml.qrc
Here is the root/CMakeLists.txt:
project(generic-object-detection)
cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
cmake_policy(VERSION 3.4.1)
ENABLE_LANGUAGE(C)
# Find includes in corresponding build directories
set(CMAKE_INCLUDE_CURRENT_DIR ON)
# Instruct CMake to run moc and rrc automatically when needed
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
# Apple-specific configuration
set(APPLE_SUPPRESS_X11_WARNING ON)
# Build flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -fvisibility-inlines-hidden -Werror -Wall -Wextra -Wno-unused-parameter -pedantic -std=c++11")
### Set libraries' locations
# Set Caffe
set(Caffe_DIR "/home/ubuntu/Libraries/caffe")
set(Caffe_INCLUDE_DIRS "/home/cortana/Libraries/caffe/include")
set(Caffe_LIBRARIES "/usr/lib/x86_64-linux-gnu/libcaffe.so")
# Disable debug output for release builds
if(CMAKE_BUILD_TYPE MATCHES "^[Rr]elease$")
add_definitions(-DQT_NO_DEBUG_OUTPUT)
endif()
# Minimum version requirements
set(QT_MIN_VERSION "5.4.0")
# Find Qt5
find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS
Core
Qml
Quick
Concurrent)
# Find OpenCV 3.1
find_package(OpenCV 3.1.0 REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
# Find Boost
FIND_PACKAGE(Boost COMPONENTS program_options REQUIRED)
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIR})
# Find CUDA
FIND_PACKAGE(CUDA REQUIRED)
# Find Caffe
FIND_PACKAGE(Caffe REQUIRED)
if(UNIX)
if(APPLE)
set(MACOSX_BUNDLE_INFO_STRING "generic-object-detection")
set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.mybitchinapp.generic-object-detection")
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME}-${PROJECT_VERSION}")
set(MACOSX_BUNDLE_BUNDLE_NAME "generic-object-detection")
set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION})
set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION})
else()
# Assume linux
# TODO: Install desktop and appdata files
endif()
elseif(WIN32)
# Nothing to do here
endif()
add_subdirectory(src)
The file root/src/CMakeLists.txt:
file(GLOB_RECURSE SOURCES
*.cpp *.h
code/*.cpp code/*.h)
set(SOURCES ${SOURCES}
res/assets/assets.qrc
res/icons/icons.qrc
res/qml/qml.qrc)
add_executable(deep-app ${SOURCES})
target_link_libraries(deep-app
Qt5::Core
Qt5::Qml
Qt5::Quick
Qt5::Concurrent
${OpenCV_LIBS}
${Boost_LIBRARIES}
${CUDA_LIBRARIES})
set_target_properties(deep-app PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/data/Info.plist)
install(TARGETS deep-app
RUNTIME DESTINATION bin
DESTINATION ${CMAKE_INSTALL_BINDIR})
The error is:
CMake Error at CMakeLists.txt:55 (FIND_PACKAGE):
By not providing "FindCaffe.cmake" in CMAKE_MODULE_PATH this project has
asked CMake to find a package configuration file provided by "Caffe", but
CMake did not find one.
Could not find a package configuration file provided by "Caffe" with any of
the following names:
CaffeConfig.cmake
caffe-config.cmake
Add the installation prefix of "Caffe" to CMAKE_PREFIX_PATH or set
"Caffe_DIR" to a directory containing one of the above files. If "Caffe"
provides a separate development package or SDK, be sure it has been
installed.
-- Configuring incomplete, errors occurred!
I have setup Caffe_DIR and other required variables. But it still gives the same errors. I removed the previous [cmake] cache so thats not the problem. I cant figure out how to resolve this problem and I need to use Caffe in the project. Please help.
You want to set your CMAKE_MODULE_PATH to location where cmake module files are located for Caffe project, which would be directory pointed to below:
.
├── AUTHORS
├── ChangeLog
├── cmake <---------Set Caffe_DIR it to this directory
│ ├── FindCaffe.cmake
│ └── FindCUDA.cmake
If that doesn't work then you should set your Caffe_DIR to the above directory and make sure your rename the files under that directory to one of the names mentioned in the following error:
Could not find a package configuration file provided by "Caffe" with any of
the following names:
CaffeConfig.cmake
caffe-config.cmake

CMake 'no rule to make target' with external library

I am trying link one of my programs to libevent. I am using CMake as build system. My project structure is as follows:
my_project
├── CMakeLists.txt
├── README.md
├── build
│  └── Build stuff
└── software
├── README.md
├── CMakeLists.txt
├── include
├── libraries
│   ├── libevent
│ │   └── CMakeLists.txt
│   └── anotherlibrary
│      └── CMakeLists.txt
├── prog1
│   ├── CMakeLists.txt
├── prog2
│   ├── CMakeLists.txt
└── prog3
└── CMakeLists.txt
CMakeList.txt of prog1 (the one that's needs to be linked to libevent)
cmake_minimum_required(VERSION 2.6)
project (prog1)
file(GLOB prog1
"*.h"
"*.cpp"
)
include_directories("${PROJECT_INCLUDE_DIR}/libevent/include")
add_executable(${PROJECT_NAME} ${prog1})
target_link_libraries(${PROJECT_NAME} event_core)
But when I build the project make can't find the library build by libevent. it searched for: libraries/libevent/lib/libevent_core.a this is the wrong path since libevent builds it libs inside: my_project/build/software/libraries/libevent/lib/libevent_core.a
How do I tell CMake to search there for the library? I already added the following lines to my Cmake file but this wasn't working
link_directories(/my_project/build/software/libraries/libevent/lib/)
SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build/lib)
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/build/bin)
Anyone a suggestion?
I fixed the problem myself by removing the content from the build directory and re running cmake .. inside the build directory.
I think CMake was somehow not aware of the changes I made and by rebuilding the project the problem was fixed.