Home > OS >  Is it possible to install different versions of a library in one place?
Is it possible to install different versions of a library in one place?

Time:07-18

The Requirement

I want to have multi versions of a library installed in one place(e.g. the default system prefix path) using CMake to be used like: find_package(Package 1.0.0 REQUIRED).

  • Why just don't set some paths?
    • Because I'm an idiot at remembering paths and flags and other related stuff and I think it's the CMake's find_package job to handle it.

The Problem

The find_package's version parameter is just an ordinary parameter that will be passed to the package *VersionConfig.cmake file for version checking and if that file doesn't exists CMake will treat it as an error. So we couldn't install different version in the same place.

Minimal CMake Configuration

file(WRITE Library.H "void Function();")
file(WRITE Library.C "void Function() {}")

add_library(Library SHARED Library.H Library.C)
target_include_directories(Library 
  PUBLIC 
    $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
    $<INSTALL_INTERFACE:include/Library>
)
set_target_properties(Library PROPERTIES VERSION 1.0.0)

install(TARGETS Library EXPORT LibraryConfig)
install(FILES Library.H DESTINATION include/Library)
install(EXPORT LibraryConfig DESTINATION lib/cmake/Library)

include(CMakePackageConfigHelpers)
write_basic_package_version_file(
  ${PROJECT_BINARY_DIR}/LibraryConfigVersion.cmake
  VERSION 1.0.0
  COMPATIBILITY SameMajorVersion
)
install(
  FILES ${PROJECT_BINARY_DIR}/LibraryConfigVersion.cmake 
  DESTINATION lib/cmake/Library
)

Possible Solution

I just apply version to the target name in form of ${PROJECT_NAME}-${PROJECT_VERSION}:

  1. I changed the export name:
install(TARGETS Library EXPORT Library-1.0.0Config)
install(
  EXPORT Library-1.0.0Config
  DESTINATION lib/cmake/Library-1.0.0Config
)
  1. And the INSTALL_INTERFACE for the include directories:
target_include_directories(Library
  PUBLIC
    $<INSTALL_INTERFACE:include/Library-1.0.0>
)
  1. And the OUTPUT_NAME property of the target:
set_target_properties(Library
  PROPERTIES
    OUTPUT_NAME Library-1.0.0
)

The result:

file(WRITE Library.H "void Function();")
file(WRITE Library.C "void Function() {}")

add_library(Library SHARED Library.H Library.C)
target_include_directories(Library 
  PUBLIC 
    $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
    $<INSTALL_INTERFACE:include/Library-1.0.0>
)
set_target_properties(Library PROPERTIES OUTPUT_NAME Library-1.0.0)

install(TARGETS Library EXPORT Library-1.0.0Config)
install(FILES Library.H DESTINATION include/Library-1.0.0)
install(EXPORT Library-1.0.0Config DESTINATION lib/cmake/Library-1.0.0)

But with this approach, I always have to specify the version: find_package(Target-1.0.0 REQUIRED).

CodePudding user response:

You are working in Config Mode of find_package, so the search mode according to the documents is like:

<prefix>/(lib/<arch>|lib*|share)/cmake/<name>*/                 (U)
<prefix>/(lib/<arch>|lib*|share)/<name>*/                       (U)
<prefix>/(lib/<arch>|lib*|share)/<name>*/(cmake|CMake)/         (U)

So you need to:

  1. Change the INSTALL_INTERFACE to point to a directory tagged with the library version(with whatever policy you prefer for version matching).
  2. Install your package's config files in a directory tagged with the library version(again with your preferred version matching policy).
  3. Provide a version config file and installed it beside the config files. A version config file is required for version matching.
  4. Change the OUTPUT_NAME property of the target to a name tagged with the version.
    • If you instead set VERSION or SOVERSION property, the result will be a non-version tagged symlink to the tagged one. It will be overridden by other versions and It could cause problems if anyone used it without using CMake.

So your sample with above modifications could be like:

file(WRITE Library.H "void Function();")
file(WRITE Library.C "void Function() {}")

add_library(Library SHARED Library.H Library.C)
target_include_directories(Library 
  PUBLIC 
    $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
    $<INSTALL_INTERFACE:include/Library-1.0.0>
)
set_target_properties(Library PROPERTIES OUTPUT_NAME 1.0.0)

install(TARGETS Library EXPORT LibraryConfig)
install(FILES Library.H DESTINATION include/Library-1.0.0)
install(EXPORT LibraryConfig DESTINATION lib/cmake/Library-1.0.0)

include(CMakePackageConfigHelpers)
write_basic_package_version_file(
  ${PROJECT_BINARY_DIR}/LibraryConfigVersion.cmake
  VERSION 1.0.0
  COMPATIBILITY SameMajorVersion
)
install(
  FILES ${PROJECT_BINARY_DIR}/LibraryConfigVersion.cmake 
  DESTINATION lib/cmake/Library-1.0.0
)

With this approach doesn't matter how many version of the library is installed using the same prefix path, the only important thing is the EXACT version name. And the usage could be requesting for a version find_package(Target 1.0.0) or the last available version find_package(Target)(based on the search policy and the version matching policy).

  • Related