41

I want to set up header-only C++ (or C) library projects, but can't find a clean way.

After some searches I've found that you can't set up a normal library using add_library to do this because it requires a compilable source file. A way to do this would be to use add_custom_target instead, this way:

# Get all headers (using search instead of explicit filenames for the example)
file( GLOB_RECURSE XSD_HEADERS 
    *.hxx
)
add_custom_target( libsxsd SOURCES ${XSD_HEADERS} )

But that doesn't seem to work completely here as I can't see the sources in the project generated in VS2010. I don't know if it's a bug or if I'm doing it wrong or if there is a preferred way to do this.

Boann
  • 48,794
  • 16
  • 117
  • 146
Klaim
  • 67,274
  • 36
  • 133
  • 188
  • 1
    what do you want to do with all your headers ? The only thin you can do is copying them somewhere ... – Joel Falcou May 10 '11 at 23:53
  • 2
    I want: 1) to have them displayed in a project generated by CMake - I've just managed to do that in several different ways just minutes ago. 2) When a project needs those headers, just make the header-only a dependency => implicitely add the include file of the header-only project. At the moment I've tried a lot of different ways to do it but failed. By the way, I've searched for boost projects way to do it but failed. – Klaim May 10 '11 at 23:58
  • dependencies are foudn by cmake by scanning other project headers. If oyu want to know where the fiels live etc, the cmake way of thing is to make a FindFOO that try to find FOO headers and set up appropriate macro to the values of their location. – Joel Falcou May 11 '11 at 01:51
  • how about adding a source file which does nothing apart from including all the headers? – Agnel Kurian May 11 '11 at 04:41
  • @Joel Falcou> Ok I'll look more at this but I'm a bit lost withe the FindFOO function, not sure why I can't grasp it. @Agnel Kurian> I tried that, it allows to create the project (say project K) but there don't seem to be any way to make sure the other projects that depends on headers of K will have access to those headers. I don't understand what I'm doing wrong because I have a normal library (say A) that, like K with a fake cpp file, does use add_library AND include_directories but when I target A I get the includes in the project but not when I target K. I'm lost... – Klaim May 11 '11 at 08:28

3 Answers3

26

Update: CMake will soon include a library target called INTERFACE that is ideal for header-only projects. This feature is currently in the master branch. Reference.

Using the command add_custom_target as you propose works for me (VS2010). The files are neatly listed within my project but it has the drawback that you can't define any "Additional Include Directories" with a custom target. Instead, I now use the following:

add_library(HEADER_ONLY_TARGET STATIC test1.hpp test2.hpp)
set_target_properties(HEADER_ONLY_TARGET PROPERTIES LINKER_LANGUAGE CXX)

This sets up your header-only project as a dummy archive target. Don't worry, no actual binaries will be generated if you should try and build it (at least not in VS2010 and Xcode 4). The command set_target_properties is there because CMake will otherwise complain that it cannot infer the target language from .hpp files only.

Frederik Aalund
  • 1,324
  • 2
  • 14
  • 17
  • 4
    This is not portable and will fail at compile time with make (error `ar: no archive members specified`). – ComicSansMS Aug 11 '13 at 06:59
  • @ComicSansMS Thanks for pointing that out! I didn't test this with make myself. Since you didn't post an answer of your own, I figure that there is no portable alternative. Someone should make a feature request to the CMake team. – Frederik Aalund Aug 27 '13 at 19:42
  • 3
    The best way I could figure out so far is to add a `dummy.cpp` to the library just to keep CMake and the compilers happy. Apart from the noise of creating unnecessarily creating an intermediate static library file for the header-only libs, it works quite well. – ComicSansMS Aug 28 '13 at 06:58
  • That workaround is fine for the header-only library itself, but as soon as you try target_link_libraries(main hdr) it fails because there's no .lib – Trass3r Feb 20 '14 at 12:17
  • Also for the INTERFACE libraries I haven't found a way yet to add files. You can't add include dirs and link to other libs conveniently either. – Trass3r Feb 20 '14 at 12:18
  • Agreeably, it would be nice to have `target_link_libraries(TARGET HEADER_ONLY_TARGET)` link everything together as you propose. It's unfortunate that this approach doesn't work. You can use `include_directories(TARGET HEADER_FILES)` instead as a work-around. Here, `HEADER_FILES` is an array of the header files used to generate the `HEADER_ONLY_TARGET`. – Frederik Aalund Feb 21 '14 at 21:45
  • 1
    The newly released CMake 3.0.0 supports INTERFACE libraries [see add_library documentation](http://www.cmake.org/cmake/help/v3.0/command/add_library.html) Unfortunately I haven't learned how to use them yet. – Erik Sjölund Aug 05 '14 at 15:26
  • 3
    @ErikSjölund old post I know but INTERFACE libraries don't have source dependencies and thus the headers don't show up in an IDE. – IdeaHat Nov 20 '14 at 14:42
  • 2
    [target_sources()](http://www.cmake.org/cmake/help/v3.2/command/target_sources.html) might be helpful to get the headers to show up in an IDE. That CMake command was introduced in CMake 3.1. See also http://stackoverflow.com/a/29218394 – Erik Sjölund May 28 '15 at 21:20
11

You can do this using the recent Interface Library feature:

add_library(mylib INTERFACE)
target_include_directories(mylib INTERFACE my_include_dir1 my_include_dir2)

This creates a library target without any source files, and adds the include directories to the INTERFACE_INCLUDE_DIRECTORIES property of the target. This means that any target that links to this library will get these directories as include paths (-I) when built.

For instance, to use the library with an executable target, just do:

add_executable(myexec ${MY_SOURCES})
target_link_libraries(myexec mylib)
Samuel Peter
  • 4,136
  • 2
  • 34
  • 42
1

I think what you are looking for is just adding an include directory using the "include_directories" command for cmake.

When doing this, if it is a third party tool that you don't have control over, I would also add the "SYSTEM" flag.

So you command would look like something like this:

include_directories(SYSTEM ${GTEST_INCLUDE_DIRS})
Keith P
  • 2,150
  • 1
  • 18
  • 15
  • Thats not exactly what I want: I want to see the files inside the generated solution/project. – Klaim Mar 25 '12 at 11:15
  • 1
    I just started using CMake and unfortunately i am stuck with old version of CMake which does not support INTERFACE and your solution works for me. I have no IDE requirement, working on Linux, VIM. – vk-code Mar 22 '19 at 15:11