Search code examples
ccmakevcpkg

Configuration file for the vcpkg


As an author of a library, what configuration do I need to provide to make it easy for the consumers to install the library.

CMakeLists.txt

cmake_minimum_required(VERSION 3.26)
project(mylib C)

set(CMAKE_C_STANDARD 11)

add_library(mylib src/foo.c src/bar.c)
target_compile_features(mylib PRIVATE c_std_11)

target_include_directories(mylib PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
        $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)

include(GNUInstallDirs)
include(CMakePackageConfigHelpers)

# Config
configure_package_config_file(build/config.cmake
        ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake # cmake-build-debug/
        INSTALL_DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME} NO_SET_AND_CHECK_MACRO) # share/

# Version
write_basic_package_version_file(
        ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake
        VERSION 1.0.0
        COMPATIBILITY SameMajorVersion)

# Install Config & Version
install(FILES
        ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake # cmake-build-debug/
        ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake # cmake_build-debug/
        DESTINATION
        ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}) # share/

# Create export set to enable import in other CMake Projects
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME}-targets # create export set
        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} # lib/
        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # lib/
        INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} # include/
)
# Install Export Set
install(EXPORT ${PROJECT_NAME}-targets
        NAMESPACE mylib::
        DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME})

# Install Headers
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}) # include/

My config.cmake

@PACKAGE_INIT@

include(${CMAKE_CURRENT_LIST_DIR}/mylib-targets.cmake)

check_required_components(mylib)

Please review and advise on what needs to be changed for the config or the CMakeLists file, for a seamless consumer experience.

Edit Consumer

cmake_minimum_required(VERSION 3.26)
project(consumer C)

set(CMAKE_C_STANDARD 11)

add_executable(consumer main.c)

find_package(mylib CONFIG REQUIRED)
target_link_libraries(consumer PRIVATE mylib::mylib)

Directory structure

mylib/
|-- CMakeLists.txt (top-level)
|-- src/
|   |-- CMakeLists.txt
|   |-- foo.c
|   |-- bar.c
|-- example/
|   |-- CMakeLists.txt
|   |-- example1.c
|   |-- example2.c

example/CMakeLists.txt

add_executable(example1 example1.c)
target_link_libraries(example1 PRIVATE mylib)
add_test(NAME TestExample1 COMMAND example1)

add_executable(example2 example2.c)
target_link_libraries(example2 PRIVATE mylib)
add_test(NAME TestExample2 COMMAND example2)

Solution

  • It seems you've done an excellent job already. It's guaranteed to be clean and x-platform.

    One thing you haven't done according to Daniel Pfeifer's awesome presentation:

    When you export Foo in namespace Foo::, also create an alias Foo::Foo.

    add_library(Foo::Foo ALIAS Foo)

    This way, when you and your consumer would like to use your lib directly from SOURCE CODE, they can still use in the canonical form with namespace:

    find_package(mylib REQUIRED)
    target_link_library(... PRIVATE mylib::mylib)
    

    provided they override find_package API like page 34 from the pdf above, sth like this:

    ### NOTE!!! Hand written && NOT tested
    set(as_subproj mylib)
    
    macro(find_package)
        if(NOT "${ARG0}" IN_LIST as_subproj)
            _find_package(${ARGV})
        else()
            add_subdirectory(${ARG0})
        endif()
    endmacro()