0

I am building a shared library in one project and using it in another. They share a prefix, but I'm not building them together (e.g., <prefix>/mylib and <prefix>/myproject). Both mylib and myproject have src and include directories.

The CMakeList.txt for the shared library:

cmake_minimum_required(VERSION 3.5)
project(mylib)

add_library(mylib SHARED
            src/mylib.c
            )

target_include_directories(mylib PRIVATE include)

set_target_properties(mylib PROPERTIES PUBLIC_HEADER include/mylib.h)

install(TARGETS
  mylib
  LIBRARY DESTINATION lib
  PUBLIC_HEADER DESTINATION include
  RUNTIME DESTINATION bin)

This results in mylib.so being installed in install/mylib/lib/mylib.so and mylib.h being installed in install/mylib/include/mylib.h, which is what I intended.

I then want to build a project that uses mylib:

#include "mylib.h"

int main(void)
{
    // use some functions in mylib
}

The associated CMakeList.txt file for main.c:

cmake_minimum_required(VERSION 3.5)
project(myproject)

find_package(mylib REQUIRED)

add_executable(myproject src/main.c)
target_link_libraries(myproject mylib)

install(TARGETS
  myproject
  DESTINATION lib/${PROJECT_NAME})

This produces:

main.c: fatal error: mylib.h: No such file or directory
#include "mylib.h"
         ^~~~~~~~~

If I change CMakeList.txt to include the following:

find_path(MYLIB_INCLUDE_DIR mylib.h)
...
target_include_directories(myproject PUBLIC ${MYLIB_INCLUDE_DIR})

Then it finds the header, but not the library. I get a linker error:

/usr/bin/ld: cannot find -lmylib

If I change CMakeList.txt to include the following:

find_library(MYLIB_LIB mylib)
...
target_link_libraries(myproject ${MYLIB_LIB})

Then it builds.

I (think I) understand why finding the library and include files manually works, but that seems to be the wrong way to go about things...

find_package(mylib) does seem to find the mylib package (I can print cmake cache variables and mylib_FOUND=1), but doesn't find the library and header in such a way that they are built with myproject.

md403
  • 3
  • 1
  • "`find_package(mylib)` does seem to find the `mylib` package" - It finds actually, otherwise CMake would emit an error message during the configuration. But it is **not clear** how do you define this package. It should be a script, either `mylibConfig.cmake` or `Findmylib.cmake` which is executed by `find_package(mylib)` call. CMake is able to generate `mylibConfig.cmake` for your `mylib` project. But your `CMakeLists.txt` for that project doesn't contain `EXPORT` keyword in `install(TARGETS)` call, it doesn't contain `install(EXPORT)` command. – Tsyvarev Apr 28 '20 at 10:34
  • You're right. I needed to use EXPORT. If you change this to an answer, I'll accept it. Perhaps also point to [this answer](https://stackoverflow.com/a/31537603/12519897), which I found immensely helpful after you got me going in the right direction. – md403 Apr 29 '20 at 13:35

1 Answers1

0

You need to specify include directories for both "build" and "install" variants in target_include_directories in your library project:

target_include_directories(mylib PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> # build variant
    $<INSTALL_INTERFACE:include> # install variant
)

BTW, such example is provided in documentation for target_include_directories command.

Additionally, you need to EXPORT the library during installation:

install(
  TARGETS mylib
  EXPORT mylib
  LIBRARY DESTINATION lib
  PUBLIC_HEADER DESTINATION include
  RUNTIME DESTINATION bin)

See this answer for a tutorial on exporting libraries with cmake.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • I tried this, but it didn't install my API header (`mylib.h`) in the install directory. Further, I was already using ```set_target_properties(macaroons PROPERTIES PUBLIC_HEADER include/macaroons.hpp) ```. Which *was* placing `mylib.h` in `/install/mylib/include`. – md403 Apr 28 '20 at 09:49
  • Command `target_include_directories` does not handle public headers installation. It just defines include directory. You still need `install(TARGETS ... PUBLIC_HEADER)` command for installing header files. – Tsyvarev Apr 28 '20 at 09:56
  • Ok. Perhaps I don't understand the scope or intended effect of your answer. I already had a `target_include_directories` command, as well as a `set_target_properties` and `install` command, as shown in the OP. Changing my `target_include_directories` command, as you suggested, didn't have any effect on either the installed location of my API header (`mylib.h`) nor the ability to find either the API header or the library file when building `myproject`. Do I need to make changes to the `CMakeList.txt` file for `myproject` in addition to the change you suggested to the file for `mylib`? – md403 Apr 28 '20 at 10:09