3

I am trying to figure out how to write a library that could be consumed by calling find_package() in another project. I am trying to follow the Catch2 repo CMake file.

This is a minimal version that I have come up with: (the git repo is available here)

cmake_minimum_required(VERSION 3.14.4)

set(project_name "hello")
project(${project_name} LANGUAGES CXX)

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")

include(GNUInstallDirs)
include(CMakePackageConfigHelpers)

add_library(${project_name} STATIC)

target_sources(${project_name} PRIVATE "src/hello.cpp")

target_include_directories(${project_name}
    PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/src/>
        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)

install(
    DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/src/
    DESTINATION include
    FILES_MATCHING REGEX "src/.+\.h"
    PERMISSIONS
        OWNER_READ
        GROUP_READ
        WORLD_READ)

set(PROJECT_CMAKE_CONFIG_DESTINATION ${CMAKE_INSTALL_LIBDIR}/../)
configure_package_config_file(
    ${CMAKE_CURRENT_LIST_DIR}/cmake/${project_name}Config.cmake.in
    ${CMAKE_CURRENT_BINARY_DIR}/${project_name}Config.cmake
    INSTALL_DESTINATION
        ${PROJECT_CMAKE_CONFIG_DESTINATION})

install(
    TARGETS
        ${project_name}
    EXPORT
        ${project_name}Targets
    RUNTIME DESTINATION bin
    LIBRARY DESTINATION lib
    ARCHIVE DESTINATION lib)

install(
    EXPORT
        ${project_name}Targets
    NAMESPACE
        ${project_name}::
    DESTINATION
        ${PROJECT_CMAKE_CONFIG_DESTINATION})

install(
    FILES
        "${CMAKE_CURRENT_BINARY_DIR}/${project_name}Config.cmake"
    DESTINATION
        ${PROJECT_CMAKE_CONFIG_DESTINATION})

I then install is using:

- cmake -DCMAKE_INSTALL_PREFIX:PATH="C:\tmp\install"
- cmake --build . --install target

and everything seems to get copied to the right path. However, when I try to use find_package() from another project thats consumes my test library, I get the following error:

CMake Error at C:/tmp/install/helloTargets.cmake:76 (message):
  The imported target "hello::hello" references the file

     "C://lib/hello.lib"

  but this file does not exist.  Possible reasons include:

  * The file was deleted, renamed, or moved to another location.

  * An install or uninstall procedure did not complete successfully.

  * The installation package was faulty and contained

     "C:/tmp/install/helloTargets.cmake"

  but not all the files it references.

Call Stack (most recent call first):
  C:/tmp/install/helloConfig.cmake:33 (include)
  CMakeLists.txt:15 (find_package)

it appears it is missing the path to the cmake install directory. How can I modify the install functions so that the generated config file could be consumed by find_package()?

Ali
  • 1,001
  • 2
  • 14
  • 32

1 Answers1

2

There are few things that are odds at this stage in your CMakeLists.txt :

  1. target_include_directories(${project_name}
        PUBLIC
            $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/src/> 
            # there is no header in this folder so this has no effect
            # maybe create a include/ folder to make it more "legit"
            $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
    
  2. You should specify the header file along the .cpp in target_sources. This has 2 effects :

    • when you do install(TARGETS hello) , you can add PUBLIC_HEADER keyword to install every headers that belong to the target to ${CMAKE_INSTALL_INCLUDEDIR}. That way, you don't need to do a install(DIRECTORY) with the regex
    • IDE will show them
  3. in helloConfig.cmake.in

if(NOT TARGET hello::hello)

I think when you use configure_package_config_file() the export file generated has some logic to protect against multiple inclusion.

Last, I think your main problem is due to the location of PROJECT_CMAKE_CONFIG_DESTINATION, if you remove the last "/" I think it should solve the problem.

EDIT 13.06 : The base path to calculate all the relative paths is wrong due to PROJECT_CMAKE_CONFIG_DESTINATION set to "lib/../" some doc about how CMake computes the relative path

What you try to achieve with "${CMAKE_INSTALL_LIBDIR}/../" is actually "." so the base path is ${CMAKE_INSTALL_PREFIX}. An alternative, is to install in a subfolder like ${CMAKE_INSTALL_LIBDIR}/cmake or just "cmake" work as well

Alkalyne
  • 241
  • 2
  • 7
  • I think you're looking under the wrong src folder, in the [src folder](https://gitlab.com/AliAskari/cmake_conan/tree/master/libs/hello/src) under lib there is a header file. All your other points are valid though but my main issue is with installation. Removing the `/` also did not help. – Ali Jun 12 '19 at 15:23
  • 1
    CMake is lost when you specify _${CMAKE_INSTALL_LIBDIR}/../_ , it is computing 2 folders down instead of the root dir. If _PROJECT_CMAKE_CONFIG_DESTINATION_ is set to ${CMAKE_INSTALL_PREFIX} it works. If you read https://cmake.org/cmake/help/latest/module/CMakePackageConfigHelpers.html: "If the INSTALL_PREFIX argument is passed, this is used as base path to calculate all the relative paths. The argument must be an absolute path. If this argument is not passed, the CMAKE_INSTALL_PREFIX variable will be used instead." You should consider not installing in the root folder. – Alkalyne Jun 13 '19 at 04:40
  • Yeah! That was it. Thank you so much – Ali Jun 14 '19 at 14:34