27

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.

Wootiae
  • 728
  • 1
  • 7
  • 17
  • 2
    If you want everything in headers, why are you putting things into .cpp files in the first place?? – Jesper Juhl Mar 09 '20 at 16:03
  • 1
    This might not be possible. You can define variables that are local to single cpp file and if you've done that then there is a potential you'll be redefining something if multiple cpp file define the same variable. – NathanOliver Mar 09 '20 at 16:03
  • If it is important I can move everything in headers but not into one. – Wootiae Mar 09 '20 at 16:07
  • I never personally tried doing this. However, you can check open source projects on github that do this. You might get good pointers from playing with their projects. I know of 2, there is pybind11 and catch2 that are header only libraries. – user3389943 Mar 09 '20 at 16:11
  • 1
    A good reference with line by line explanation can be found here: https://dominikberner.ch/cmake-interface-lib/ – Rufus Aug 14 '20 at 07:36

1 Answers1

50

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>
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • 1
    I found that in the target_link_libraries if the header only library is INTERFACE then when building the project cmake will not detect the header files of the library. – Satrapes May 19 '21 at 21:49
  • Strictly speaking, shouldn't the scope of '.' in target_include_directories be PUBLIC, vs. INTERFACE? Technically the files in that directory include other files in that directory. I understand that, practically speaking, it makes no difference, since you will never actually build this project. I suppose it would allow you to make one less change if you ever add an implementation file and change the library to PUBLIC (non-header-only). – ToddR Jun 07 '21 at 15:52
  • @ToddR If the library is an "INTERFACE", that keyword can only be "INTERFACE". – X.Arthur Jun 11 '21 at 14:58
  • 9
    Worth noting: Since CMake 3.19 interface libraries can be created with source files, i.e. `add_library(project INTERFACE file1.hpp file2.hpp)`. If you are working with Visual Studio, those headers will now show up under the generated project. – sebrockm Sep 13 '21 at 16:09
  • I can confirm, @sebrockm's suggested approach works, is most idiomatic and also necessary to properly get CMake to register the dependency between a target and a generated header file... – saxbophone Nov 09 '22 at 17:05
  • I tried this with gcc and clang, had to use `#include ` – Berik Nov 14 '22 at 22:30