CMake files to build main and tests separately - c++

I have the following directory structure:
main.cpp
CMakeLists.txt
src/
some_function.h
some_function.cpp
some_class.h
some_class.cpp
CMakeLists.txt
test/
catch.hpp
tests.cpp
CMakeLists.txt
CmakeLists.txt in the project root:
cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_STANDARD 11) # C++11
project(main)
add_subdirectory (src)
add_executable(main main.cpp)
target_link_libraries (main some_class)
CmakeLists.txt in the src/:
add_library (some_class some_class.h some_class.cpp some_function.h some_function.cpp)
The above works to build and run the main target.
Now I want to build and run tests. The file tests.cpp includes some_function.h and some_class.h. However, I am not sure how to add the src/ directory here.
This is what I have so far in test/ (results in a linking error for the function in some_function.h):
cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_STANDARD 11) # C++11
project(tests)
set(CATCH_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
add_library(Catch INTERFACE)
target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR})
add_executable(tests tests.cpp)
target_link_libraries(tests Catch)

Just link the some_class library target to the tests target, like you did with the main executable target.
CmakeLists.txt in the project root:
cmake_minimum_required(VERSION 3.16)
set(CMAKE_CXX_STANDARD 11) # C++11
project(main)
add_subdirectory (src)
# Add the test sub-directory also.
add_subdirectory(test)
add_executable(main main.cpp)
target_link_libraries (main some_class)
CmakeLists.txt in the test directory:
project(tests)
set(CATCH_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
add_library(Catch INTERFACE)
target_include_directories(Catch INTERFACE ${CATCH_INCLUDE_DIR})
add_executable(tests tests.cpp)
# Link 'some_class' here also!
target_link_libraries(tests PRIVATE some_class Catch)

Normal targets created by add_library() or add_executable() have the scope of the project not the directory that's why their name must be unique within the project. So you can "target_link" some_class to your target tests even if target tests is not in a subdirectory of src/...
The <name> corresponds to the logical target name and must be globally unique within a project.
ref: https://cmake.org/cmake/help/latest/command/add_library.html#normal-libraries
This is not the case for imported targets.
The [imported] target name has scope in the directory in which it is created and below, but the GLOBAL option extends visibility. It may be referenced like any target built within the project.
ref: https://cmake.org/cmake/help/latest/command/add_library.html#imported-libraries

Related

How to include properly all files with CMake

I want to build my C++ project with CMake and I want to include automatically every new file on "cmake ." my project structure is:
Application/ Graphics/ CMakeLists.txt CMakeLists.txt.user main.cpp
./Application:
CMakeLists.txt Logger/ Recovery/ application.cpp application.hpp firstclass.cpp firstclass.hpp singleton.hpp
./Application/Logger:
CMakeLists.txt logger.cpp logger.hpp
./Application/Recovery:
CMakeLists.txt recovery.cpp recovery.hpp
./Graphics:
CMakeLists.txt drawableobject.cpp drawableobject.hpp graphics.cpp graphics.hpp
Each folder has own CMakeLists.txt
I did so far this in master CMake:
cmake_minimum_required(VERSION 3.10)
# set the project name and version
project(Asteri VERSION 1.0)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED True)
set(SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
message("Source dir: ${SOURCE_DIR}")
#file(GLOB_RECURSE SRC_FILES ${SOURCE_DIR}/*.cpp)
#file(GLOB_RECURSE HEADER_FILES ${HEADER_DIR}/*.hpp)
set(PROJECT_NAME "Asteri")
macro(SUBDIRLIST result curdir)
file(GLOB children RELATIVE ${curdir} ${curdir}/*)
set(dirlist "")
foreach(child ${children})
if(IS_DIRECTORY ${curdir}/${child})
list(APPEND dirlist ${child})
endif()
endforeach()
set(${result} ${dirlist})
endmacro()
SUBDIRLIST(SUBDIRS ${CMAKE_CURRENT_SOURCE_DIR})
foreach(subdir ${SUBDIRS})
message("Subdirectory: ${subdir}")
add_subdirectory(${subdir})
endforeach()
add_executable(${Asteri} main.cpp)
The question is how to connect all pieces together?
What I need in other CMakeLists.txt?
How to communicate children -> parent or I misunderstood the concept of CMake?
No need for other CMakeLists.
I guess you want something like this in top CMakeLists:
cmake_minimum_required(VERSION 3.12)
project(Asteri VERSION 1.0)
file(GLOB_RECURSE ASTERI_SRC_FILES CONFIGURE_DEPENDS
"${PROJECT_SOURCE_DIR}/Application/*.cpp"
"${PROJECT_SOURCE_DIR}/Graphics/*.cpp"
)
add_executable(Asteri main.cpp ${ASTERI_SRC_FILES})
It's worth noting that GLOB/GLOB_RECURSE -even with CONFIGURE_DEPENDS- is bad practice: it's slow, specially on Windows from my experience, and may not work as expected depending on generator used.
I found out the answer to my problems.
set(SOURCES "${PROJECT_DIR}/main.cpp" CACHE INTERNAL STRINGS)
add_subdirectory(application)
FOREACH(it ${SOURCES})
message("source file: ${it}")
ENDFOREACH()
add_executable(Asteri ${SOURCES})
In this way I store main.cpp path into variable SOURCES
You can see your variables after 'make' in CMakeCache.txt.
After this is done, my variable SOURCES looks like:
/STRINGS
SOURCES:INTERNAL=/home/default/cpp_testing_project/cmake_project/source/main.cpp;
After that in application subdirectory I have CMakeLists.txt:
cmake_minimum_required(VERSION 3.7)
# Get source files
file(GLOB CPP_LIST "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/*.hpp" "${CMAKE_CURRENT_SOURCE_DIR}/*.h" "${CMAKE_CURRENT_SOURCE_DIR}/*.c")
set(SOURCES ${SOURCES} ${CPP_LIST} CACHE INTERNAL STRINGS)
And now SOURCES looks like:
SOURCES:INTERNAL=/home/default/cpp_testing_project/cmake_project/source/main.cpp;/home/default/cpp_testing_project/cmake_project/source/application/application.cpp;/home/default/cpp_testing_project/cmake_project/source/application/drawableobject.cpp;/home/default/cpp_testing_project/cmake_project/source/application/firstclass.cpp;/home/default/cpp_testing_project/cmake_project/source/application/graphics.cpp;/home/default/cpp_testing_project/cmake_project/source/application/application.hpp;/home/default/cpp_testing_project/cmake_project/source/application/drawableobject.hpp;/home/default/cpp_testing_project/cmake_project/source/application/firstclass.hpp;/home/default/cpp_testing_project/cmake_project/source/application/graphics.hpp;

CMake Library issue for header-only library [duplicate]

How to make a project in cmake that collects all c++ files into one header?
I have this project structure.
/
project/
folder1/
file.cpp
file.hpp
folder2/
...etc
CMakeLists.txt
tests/
test.cpp
CMakeLists.txt
CMakeList.txt
root cmakelists.txt
cmake_minimum_required (VERSION 3.8)
project ("CMakeProject"
LANGUAGES C CXX)
set(CMAKE_EXECUTABLE_SUFFIX ".exe")
include(GNUInstallDirs)
add_subdirectory ("project")
option(ENABLE_TESTING OFF)
if (ENABLE_TESTING)
enable_testing()
add_subdirectory("tests")
endif()
CMakeLists.txt in project
cmake_minimum_required (VERSION 3.8)
file(GLOB projectSRC
"*/*.cpp"
"*/*.hpp"
"*.cpp"
"*.hpp"
)
add_library(project INTERFACE)
message(STATUS "CMake inatall directory: " ${CMAKE_INSTALL_INCLUDEDIR})
target_include_directories(project
INTERFACE
$<BUILD_INTERFACE:${PROJECT_INCLUDE_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
and test cmakelist.txt
cmake_minimum_required (VERSION 3.8)
# install Catch2 testing library
# (https://github.com/catchorg/Catch2/blob/master/docs/cmake-integration.md#installing-catch2-from-git-repository or use packet manager)
find_package(Catch2 REQUIRED)
file(GLOB testSRC
"*.cpp"
)
add_executable(tests ${testSRC})
target_link_libraries(tests
Catch2::Catch2
project)
include(CTest)
include(Catch)
catch_discover_tests(tests)
How to generate one header and use it (in tests or other projects) or make this library able to have templates? The first is better.
How to make a header-only library with cmake?
Like this:
add_library(project INTERFACE)
target_include_directories(project INTERFACE .)
Then in the target that uses the library:
target_link_libraries(dependee
PUBLIC/INTERFACE/PRIVATE # pick one
project)
and include the header like this:
#include <project/folder1/file.hpp>

Project Structure for unit testing (qtest) when using target_sources() command in sub directory

First of all, I know there are very similar questions in this forum. However, none really answer my specific case.
I have the following project structure:
|---Project_Root
|---CMakeLists.txt
|---build
|---src
| |---CMakeLists.txt
| |---many .cpp and .h files in multiple subfolders with a different CMakeLists.txt
|---tests
| |---CMakeLists.txt
| |---many .cpp files
In the Project_Root/CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
set(SRC_DIR src)
project(
Project
LANGUAGES CXX
)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(${PROJECT_NAME} "")
target_include_directories(${PROJECT_NAME} PRIVATE ${SRC_DIR})
add_subdirectory(${SRC_DIR})
In the src folder and subfolder CMakeLists.txt files:
cmake_minimum_required(VERSION 3.5)
set(SRC_FILES
Source1.cpp
)
set(HEADER_FILES
Source1.hpp
)
target_sources(${PROJECT_NAME}
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FILES}
${CMAKE_CURRENT_SOURCE_DIR}/${HEADER_FILES}
)
Now in the tests subfolder:
project(Test LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
enable_testing()
add_executable(Test tst_test.cpp)
add_test(NAME Test COMMAND Test)
target_link_libraries(Test PRIVATE Qt5::Test)
In the above CMakeLists.txt I want to include the main project as a static library. I can, for example, add a new library target (say Project_Lib) in the root CMakeLists.txt and use once again the command:
target_sources(Project_Lib
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/${SRC_FILES}
${CMAKE_CURRENT_SOURCE_DIR}/${HEADER_FILES}
)
in all the subfolders.
But is there a more elegant way of doing it without having to modify all the CMakeLists.txt in the subfolders? For example, is there way to extract the source files from the Project target, so that it can be reused to make the Project_Lib target?
As you described, you can make a new static library target Project_Lib. Take advantage of the fact that you parameterized the target name by using the project name (${PROJECT_NAME}), so you actually don't have to change all of the CMakeLists.txt files in the subfolders. Just change the project name.
As I commented, simply exclude the main.cpp file from the static library, and add it to a separate executable target instead.
In Project_Root/CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
set(SRC_DIR src)
# Change the project name, as now the static library is the primary target.
project(
Project_Lib
LANGUAGES CXX
)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Create the static library target, whose sources are populated in subdirectories.
add_library(${PROJECT_NAME} STATIC)
target_include_directories(${PROJECT_NAME} PRIVATE ${SRC_DIR})
# The only modification necessary in the subdirectories is to *exclude* the
# main.cpp file from the target_sources for the static library.
add_subdirectory(${SRC_DIR})
# Add *only* the main.cpp file to the executable target.
add_executable(Project_Exe src/main.cpp)
# Link the static library target to the executable.
target_link_libraries(Project_Exe PRIVATE ${PROJECT_NAME})
In tests/CMakeLists.txt:
...
# Link the static library to your Test executable also.
target_link_libraries(Test PRIVATE Project_Lib Qt5::Test)

CMake include/source paths valid configuration

I have following C++ project structure:
Project
├───inc
├───src
└───tests
└───googletest
The CMakeLists inside root folder has the following contents:
cmake_minimum_required(VERSION 3.16)
project(Project)
add_subdirectory(tests)
set(CMAKE_CXX_STANDARD 17)
add_executable(Project_run main.cpp)
And the CMakeLists inside tests folder:
project(Project_Tests)
set(CMAKE_CXX_STANDARD 17)
add_subdirectory(googletest)
include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
include_directories(${gmock_SOURCE_DIR}/include ${gmock_SOURCE_DIR})
add_executable(Google_Tests_run test.cpp)
target_link_libraries(Google_Tests_run gtest gtest_main gmock)
It compiles fine. Now I would like to have class headers inside inc folder and sources inside src folder. Should I use new CMake file inside src folder? Also the project files should be available inside googletests library.
Just add the following to your CMakeLists.txt file in your "tests" folder:
target_include_directories(Google_Tests_run PRIVATE ${CMAKE_SOURCE_DIR}/inc)
Try to add the above line BELOW your target_link_libraries(Google_Tests_run...) . That should work.
For info: ${CMAKE_SOURCE_DIR} expands to your project root-folder.
Note: Try to always use target_include_directories over include_directories.

How to make a header-only library with cmake?

How to make a project in cmake that collects all c++ files into one header?
I have this project structure.
/
project/
folder1/
file.cpp
file.hpp
folder2/
...etc
CMakeLists.txt
tests/
test.cpp
CMakeLists.txt
CMakeList.txt
root cmakelists.txt
cmake_minimum_required (VERSION 3.8)
project ("CMakeProject"
LANGUAGES C CXX)
set(CMAKE_EXECUTABLE_SUFFIX ".exe")
include(GNUInstallDirs)
add_subdirectory ("project")
option(ENABLE_TESTING OFF)
if (ENABLE_TESTING)
enable_testing()
add_subdirectory("tests")
endif()
CMakeLists.txt in project
cmake_minimum_required (VERSION 3.8)
file(GLOB projectSRC
"*/*.cpp"
"*/*.hpp"
"*.cpp"
"*.hpp"
)
add_library(project INTERFACE)
message(STATUS "CMake inatall directory: " ${CMAKE_INSTALL_INCLUDEDIR})
target_include_directories(project
INTERFACE
$<BUILD_INTERFACE:${PROJECT_INCLUDE_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
and test cmakelist.txt
cmake_minimum_required (VERSION 3.8)
# install Catch2 testing library
# (https://github.com/catchorg/Catch2/blob/master/docs/cmake-integration.md#installing-catch2-from-git-repository or use packet manager)
find_package(Catch2 REQUIRED)
file(GLOB testSRC
"*.cpp"
)
add_executable(tests ${testSRC})
target_link_libraries(tests
Catch2::Catch2
project)
include(CTest)
include(Catch)
catch_discover_tests(tests)
How to generate one header and use it (in tests or other projects) or make this library able to have templates? The first is better.
How to make a header-only library with cmake?
Like this:
add_library(project INTERFACE)
target_include_directories(project INTERFACE .)
Then in the target that uses the library:
target_link_libraries(dependee
PUBLIC/INTERFACE/PRIVATE # pick one
project)
and include the header like this:
#include <project/folder1/file.hpp>