Ali
Ali

Reputation: 1041

CMake install targets with relative paths

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()?

Upvotes: 3

Views: 3874

Answers (1)

Alkalyne
Alkalyne

Reputation: 251

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

Upvotes: 2

Related Questions