How to make a header-only library with cmake? - c++

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>

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;

Can not link static library in CMake

I have a simple project, which consists of several directories with sources and CMakeLists.txt files.
Here is my project structure:
CMakeLists.txt\
main.c
src/source1.c\
src/source1.h\
src/CMakeLists.txt
test/test.cpp\
test/main.cpp\
test/CMakeLists.txt
googletest
Main executable has been built successfully, but test executable not, linker errors instead (undefined references to functions from src directory). Here is my CMake files content:
CMakeLists.txt:
cmake_minimum_required(VERSION 3.22)
project(TIC-TAC-TOE)
set(CMAKE_C_STANDARD 99)
set(CXXMAKE_C_STANDARD 11)
set(POSITION_INDEPENDENT_CODE ON)
enable_testing()
include_directories(src)
add_subdirectory(test)
add_subdirectory(src)
add_subdirectory(googletest)
add_executable(TIC-TAC-TOE main.c)
target_link_libraries(TIC-TAC-TOE tic_tac_toe-lib)
src/CMakeLists.txt:
set(Sources tic-tac-toe.c tic-tac-toe.h)
add_library(tic_tac_toe-lib STATIC ${Sources})
test/CMakeLists.txt:
project(TIC-TAC-TOE-TEST)
set(Sources tic-tac-toe-test.cpp
main.cpp
)
add_executable(TIC-TAC-TOE-TEST ${Sources})
add_test(
NAME TIC-TAC-TOE-TEST
COMMAND TIC-TAC-TOE-TEST
)
target_link_libraries(TIC-TAC-TOE-TEST PUBLIC tic_tac_toe-lib gtest_main)

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>

CMake files to build main and tests separately

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

Creating a header-only library that can be included via add_subdirectory

I'm creating a lightweight header-only library for coroutines in C++20 called Conduit. The structure of the library looks like this, and it has no dependencies other than the C++ standard library:
conduit
|
|---CMakeLists.txt
|
|---tests/tests.cpp
|
|---include/conduit/
|---coroutine.hpp
|---future.hpp
|---[Other header files...]
Question: How do I write the CMakeLists.txt file for my library so that other projects can use Conduit by adding it as a subdirectory via add_subdirectory?
While conduit does provide an installation option, the intended way for someone to use conduit is to add it as a sub-directory inside their project. Inside the CMakeLists.txt folder for example_project, there'd be a line add_subdirectory(conduit), and the directory structure of example_project would look something like this:
example_project
|
|---CMakeLists.txt
|
|---src/...
|
|---include/...
|
|---conduit
|
|---CMakeLists.txt
|
|---tests/tests.cpp
|
|---include/conduit/
|---coroutine.hpp
|---future.hpp
|---[Other header files...]
What I have so far: This is what I'm currently using as the CMakeLists.txt file for conduit. It compiles and runs tests.cpp with a variety of different tests, but when I add conduit as a sub-directory to another project (via git submodule + add_subdirectory in CMakeLists.txt), CMake doesn't add conduit/include/ to the list of include files.
cmake_minimum_required(VERSION 3.0.0)
project(conduit VERSION 0.3.0 LANGUAGES CXX)
option(CONDUIT_Install "Install CMake targets during install step." ON)
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" )
add_compile_options("/std:c++latest" "/await" "/EHsc")
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" )
add_compile_options("-std=c++20")
endif()
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" )
add_compile_options("-std=c++20" "-fcoroutines")
endif()
include_directories(include)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CONDUIT_TARGET_NAME ${PROJECT_NAME})
set(CONDUIT_INCLUDE_BUILD_DIR "${PROJECT_SOURCE_DIR}/include/")
add_library(${CONDUIT_TARGET_NAME} INTERFACE)
target_compile_definitions(
${CONDUIT_TARGET_NAME}
INTERFACE
)
target_include_directories(
${CONDUIT_TARGET_NAME}
INTERFACE
$<BUILD_INTERFACE:${CONDUIT_INCLUDE_BUILD_DIR}>
$<INSTALL_INTERFACE:include>
)
install(
DIRECTORY ${CONDUIT_INCLUDE_BUILD_DIR}
DESTINATION include
)
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
include(CTest)
endif()
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING)
add_executable(run_tests tests/tests.cpp)
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(run_tests PRIVATE Threads::Threads)
foreach(X IN ITEMS
test_coroutine
test_destroy
test_exception_1
test_exception_2
test_future
test_generator
test_on_suspend
test_recursive_generator
test_resume_on_alternate_thread
test_source
test_suspend_invoke
test_task)
add_test(NAME "${X}" COMMAND run_tests "${X}")
endforeach()
endif()