1

I'm new to CMake and I was trying to develop a shared library in Ubuntu. I've managed to create some basic CMakeLists.txt file that allows me to compile a very simple library with just one header and one source file.

In fact, my project tree was:

.
├── CMakesList.txt
├── First.h
|
└── src
    └──First.cpp

And I was able to do the CMakeLists.txt file using this answer.

However, then I tried to add a little bit more complexity to the example, by adding a Second class to the library.

Now, my public header, First.h, also imports the include/Second.h. The project tree is like this:

.
├── CMakesList.txt
├── First.h
|
└── include
|    └──Second.h
|
└── src
    └──First.cpp
    └──Second.cpp

And I've made the necessary changes to the CMakesList file, which looks like this:

cmake_minimum_required(VERSION 3.1...3.15)

project(First VERSION 1.0 
              DESCRIPTION "It does very little"
              LANGUAGES CXX)

include(GNUInstallDirs)

add_library(First SHARED 
    src/First.cpp
    src/Second.cpp)

set_target_properties(First PROPERTIES
    VERSION ${PROJECT_VERSION}
    PUBLIC_HEADER First.h)

configure_file(FIRST.pc.in FIRST.pc @ONLY)

target_include_directories(First PRIVATE .)

install(TARGETS First
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})

install(FILES ${CMAKE_BINARY_DIR}/First.pc
    DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig)

And this file is also run sucessfully. It builds the library with no errors.

However, now, when I'm linking the library and using it in another project, it does not compile, and gives me the following error:

 /usr/local/include/First.h:36: error: include/Second.h: No such file or directory
 #include "include/Second.h"
          ^~~~~~~~~~~~~~

So I guess my question is:

How can I also create a directory (in this case, at /usr/local/include/) that contains all the necessary headers requested by my public header (as well as other headers that can be requested by the headers requested by my public header, etc)? In this case, I would like to include the "Include" directory at the same directory where my public header is.

Sorry for the dumb question, I'm just exploring!

Thanks in advance!

  • "... that contains all the necessary headers requested by my public header" - Just enumerate that headers, e.g., as `PUBLIC_HEADER` property for your target. CMake doesn't provide functionality for scanning headers dependencies and installing them. It is not so simple to perform such things: 1. If your header includes system one, the system header shouldn't be installed (it already installed). 2. What if some `#include` is guared with some macro checking? Should this header be installed or not? It is much simpler and straightforward to provide **exact headers list** for install. – Tsyvarev Sep 17 '19 at 07:44
  • @Tsyvarev `gcc -MM` if able to do it – Jérôme Pouiller Sep 17 '19 at 08:10
  • @Jezz: Yes, I know that is not so difficult to resolve the problem 1 as I have stated it. But it doesn't solve the problem 2 (by "guarded with some macro checking" I meant that the macro could be set in some configuration, but could be unset in some other; `gcc -MM` uses only current macros settings). – Tsyvarev Sep 17 '19 at 10:04

1 Answers1

0

First, you have to declare all your headers. I don't know any way to automatically find headers dependencies with cmake (and GLOB is not an option). You can copy-paste output of ls -1 include if you are lazy. Alternatively, you can create check output of gcc -MM First.h.

If you just want to install Second.h in /usr/local/include/first, I think the code below should work:

install(FILES include/Second.h
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/first)

However, maybe all headers should be placed in /usr/local/include/first and everything is easier:

set_target_properties(First PROPERTIES
    VERSION ${PROJECT_VERSION}
    PUBLIC_HEADER "First.h;include/Second.h")
install(TARGETS First
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
    PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/first)

Notice that PUBLIC_HEADER take one argument and this argument is a list. The syntax PUBLIC_HEADER First.h include/Second.h (without double quotes) will only pass the First.h as argument. See also: CMake: difference between ${} and "${}"

Jérôme Pouiller
  • 9,249
  • 5
  • 39
  • 47
  • Just to add to your answer to someone who may read this in the future, if the compilation stills gives the error that the file was not found, try running: ~$ sudo ldconfig. And try again. – João Franco Sep 17 '19 at 11:07
  • Not working for me... this only installs the first header that is listed with `set_target_properties` (aka, only `First.h` not `include/Second.h`). What am I doing wrong?? – Carlo Wood Jan 20 '20 at 19:57
  • Ah, I figured it out... you can use a semi-colon separated list (possibly in a variable), but have to quote that when you pass it to `set_target_properties`. Aka: `file(GLOB PUBLIC_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "include/libcwd/*.h")` and then `set_target_properties(cwd_r PROPERTIES PUBLIC_HEADER "${PUBLIC_HEADERS}")`. It is NOT working without the quotes and also not when using spaces. I'll edit the answer to reflect this (probably needed for newer versions of cmake). – Carlo Wood Jan 20 '20 at 20:05
  • @CarloWood Indeed. See also https://stackoverflow.com/questions/13582282/cmake-difference-between-and – Jérôme Pouiller Jan 21 '20 at 09:47