Daniel
Daniel

Reputation: 778

Install prebuilt static library dependency with the parent library

My project structure looks like this:

Parent/
    CMakeLists.txt
    file.cpp
    third_party/
        dependency/
            CMakeLists.txt
            lib/
                dependency.lib

Note that dependency.lib is a precompiled/prebuilt library: I don't even have sources for it.

When I install Parent, I want it to go into a dist folder like this (within the previous tree structure but omitted for clarity):

Parent/
    dist/
        Parent/
            include/
            bin/
            lib/
                cmake/
                    ParentTargets.cmake
                Parent.lib
                dependency.lib

The idea here is to just get both the generated as well as the dependency libraries all in one place for easier install.

Relevant parts of Parent's CMakeLists.txt look like this (omitting a lot of stuff like includes, etc.):

cmake_minimum_required(VERSION 3.22)
project(Parent VERSION 0.1.0 LANGUAGES C CXX)
add_library(Parent)

target_sources(Parent PRIVATE file.cpp)

add_subdirectory("${CMAKE_SOURCE_DIR}/third_party/dependency")
target_link_libraries(Parent PRIVATE dependency)

# Manual copy of dependency.lib to install location
install(DIRECTORY "${CMAKE_SOURCE_DIR}/third_party/dependency/lib/"
    DESTINATION lib
    FILES_MATCHING
    PATTERN "dependency.lib"
)

# Install all targets to ParentTargets export set.
# (omitting install dir variables, components, etc. for clarity)
install(
    TARGETS
        Parent dependency
    EXPORT
        ParentTargets
    LIBRARY
        DESTINATION lib
    ARCHIVE
        DESTINATION lib
)

# Write ParentTargets export set to targets file directly in install folder
install(EXPORT ParentTargets
    FILE ParentTargets.cmake
    NAMESPACE Parent::
    DESTINATION lib/cmake
)

And this is what I have for the dependency's CMakeLists.txt:

add_library(dependency INTERFACE)
target_link_libraries(dependency
    INTERFACE
        $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/lib/dependency.lib>
        $<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/lib/dependency.lib>
)

This almost sort of works, the issue is that if you look at the generated ParentTargets.cmake, this is what CMake generates for the dependency target:

# Create imported target Parent::dependency
add_library(Parent::dependency INTERFACE IMPORTED)

set_target_properties(Parent::dependency PROPERTIES
  INTERFACE_LINK_LIBRARIES "E:/absolute/path/Parent/dist/Parent/lib/dependency.lib"
)

...its a hard-coded absolute path to my installation directory. This is not great, and usually for built libraries CMake would have generated a path like ${_IMPORT_PREFIX}/lib/dependency.lib, which is what I wanted and important to have the install relocatable.

Here are my questions:

  1. Is this the right way of doing this? I could never find a good reference on how to package dependency static libraries alongside your project, and CMake doesn't seem to do this "on its own" too eagerly (I even had to manually copy the .lib there as you can see);
  2. How do I get CMake to use ${_IMPORT_PREFIX} there? I can't just use that on my CMakeLists.txt as it would evaluate to the empty string during generation, and I can't escape it like \${_IMPORT_PREFIX} as it still places the escaped version in ParentTargets.cmake...

Upvotes: 3

Views: 355

Answers (1)

Alex Reinking
Alex Reinking

Reputation: 19916

Is this the right way of doing this? I could never find a good reference on how to package dependency static libraries alongside your project, and CMake doesn't seem to do this "on its own" too eagerly (I even had to manually copy the .lib there as you can see);

I think what you're doing is fine. If it were me, I would create and link to an IMPORTED STATIC target named Parent::ThirdParty::dependency for dependency.lib in both the main build and in the package config file, and set its IMPORTED_LOCATION property appropriately in each.

The reason being that CMake will validate that anything you link to that contains a :: in its name is a CMake target. This tends to lead to fewer surprises on the link line and lets you attach things like include paths to the library.

How do I get CMake to use ${_IMPORT_PREFIX} there? [...]

Your issue is here:

$<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}/lib/dependency.lib>

CMAKE_INSTALL_PREFIX is set to E:/absolute/path/Parent/dist/Parent immediately and CMake has no chance to replace it with something like ${_IMPORT_PREFIX}. Fortunately, there is a generator expression for this.

$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/lib/dependency.lib>

Try that instead.

Upvotes: 1

Related Questions