cmake - 如何从 cmake 目标自动生成 pkgconfig 文件

标签 cmake pkg-config

我想从目标在 cmake 中生成 pkgconfig 文件。我盯着写这样的东西:

function(auto_pkgconfig TARGET)

    get_target_property(INCLUDE_DIRS ${TARGET} INTERFACE_INCLUDE_DIRECTORIES)
    string(REPLACE "$<BUILD_INTERFACE:" "$<0:" INCLUDE_DIRS "${INCLUDE_DIRS}")
    string(REPLACE "$<INSTALL_INTERFACE:" "$<1:" INCLUDE_DIRS "${INCLUDE_DIRS}")
    string(REPLACE "$<INSTALL_PREFIX>" "${CMAKE_INSTALL_PREFIX}" INCLUDE_DIRS "${INCLUDE_DIRS}")


    file(GENERATE OUTPUT ${TARGET}.pc CONTENT "
Name: ${TARGET}
Cflags: -I$<JOIN:${INCLUDE_DIRS}, -I>
Libs: -L${CMAKE_INSTALL_PREFIX}/lib -l${TARGET}
")
    install(FILES ${TARGET}.pc DESTINATION lib/pkgconfig)
endfunction()

这是一个简化版本,但它基本上是 INTERFACE_INCLUDE_DIRECTORIES属性和处理INSTALL_INTERFACE的生成器表达式。
只要在调用 auto_pkgconfig 之前设置了包含目录,就可以很好地工作。 , 像这样:
add_library(foo foo.cpp)

target_include_directories(foo PUBLIC 
    $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>
    ${OTHER_INCLUDE_DIRS}
)

auto_pkgconfig(foo)

但是,有时属性是在调用 auto_pkgconfig 之后设置的。 , 像这样:
add_library(foo foo.cpp)
auto_pkgconfig(foo)

target_include_directories(foo PUBLIC 
    $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>
    ${OTHER_INCLUDE_DIRS}
)

但是,这将不再正确读取包含目录。我要 auto_pkgconfig在设置所有目标属性后运行。我可以为此使用生成器表达式,方法是更改​​ auto_pkgconfig对此:
function(auto_pkgconfig TARGET)

    file(GENERATE OUTPUT ${TARGET}.pc CONTENT "
Name: ${TARGET}
Cflags: -I$<JOIN:$<TARGET_PROPERTY:${TARGET},INTERFACE_INCLUDE_DIRECTORIES>, -I>
Libs: -L$<TARGET_FILE_DIR:${TARGET}> -l${TARGET}
")
    install(FILES ${TARGET}.pc DESTINATION lib/pkgconfig)
endfunction() 

但是,这将读取 BUILD_INTERFACE而不是 INSTALL_INTERFACE .那么有没有另一种方法可以在设置目标属性后读取它们?

最佳答案

根据CMake documentation ,INSTALL_INTERFACE的内容仅在调用 install(EXPORT) 时可用.除非他们扩展 CMake,否则最好执行其他操作来生成 PkgConfig 文件。理想情况下,您将对安装布局有足够的控制权,以使这变得容易。

然而,这并不意味着你不能做你所要求的;只是"Tony the Pony"邪恶的层次。 我真的很犹豫要不要发布这个。请不要将此作为建议。
这个想法是使用 install(EXPORT)让 CMake 生成适当的脚本。然后生成一个使用 file(GENERATE OUTPUT ...) 的虚拟 CMake 项目您在上面给出的代码;虚拟项目将看到导出的,即。 INSTALL_INTERFACE特性。
我最初尝试使用 install(CODE [[ ... ]])这样做,但它也会看到 $<BUILD_INTERFACE:...>看法。我在 CMake Discourse 上问过这个问题。 .

cmake_minimum_required(VERSION 3.16)
project(example)

# Dummy library for demo

add_library(example SHARED example.cpp)

target_compile_definitions(example
  PUBLIC $<BUILD_INTERFACE:BUILD>
         $<INSTALL_INTERFACE:INSTALL>)

target_include_directories(example
  PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
         $<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>)

# Here be dragons...

function(auto_pc TARGET)
  file(CONFIGURE OUTPUT "pc.${TARGET}/CMakeLists.txt"
       CONTENT [[
cmake_minimum_required(VERSION 3.16)
project(pc_@TARGET@)

find_package(pc_@TARGET@ REQUIRED CONFIG)

file(GENERATE OUTPUT @TARGET@.pc
     CONTENT [=[
Name: @TARGET@
Cflags: -I$<JOIN:$<TARGET_PROPERTY:INTERFACE_INCLUDE_DIRECTORIES>, -I> -D$<JOIN:$<TARGET_PROPERTY:INTERFACE_COMPILE_DEFINITIONS>, -D>
Libs: -L$<TARGET_FILE_DIR:@TARGET@> -l@TARGET@
]=]   TARGET "@TARGET@")
]] @ONLY NEWLINE_STYLE LF)

  install(TARGETS ${TARGET} EXPORT pc_${TARGET})
  install(EXPORT pc_${TARGET} DESTINATION "_auto_pc" FILE pc_${TARGET}-config.cmake)

  file(CONFIGURE OUTPUT "pc.${TARGET}/post-install.cmake"
       CONTENT [[
file(REAL_PATH "${CMAKE_INSTALL_PREFIX}" prefix)
set(proj "@CMAKE_CURRENT_BINARY_DIR@/pc.@TARGET@")
execute_process(COMMAND "@CMAKE_COMMAND@" "-Dpc_@TARGET@_DIR=${prefix}/_auto_pc" -S "${proj}" -B "${proj}/build")
file(COPY "${proj}/build/@TARGET@.pc" DESTINATION "${prefix}")
]] @ONLY NEWLINE_STYLE LF)

  install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/pc.${TARGET}/post-install.cmake")
endfunction()

auto_pc(example)

# Clean up install path
install(CODE [[ file(REMOVE_RECURSE "${CMAKE_INSTALL_PREFIX}/_auto_pc") ]])
这导致以下结果:
alex@Alex-Desktop:~/test$ cmake -S . -B build
...
-- Configuring done
-- Generating done
-- Build files have been written to: /home/alex/test/build
alex@Alex-Desktop:~/test$ cmake --build build/
...
alex@Alex-Desktop:~/test$ cmake --install build --prefix install
-- Install configuration: ""
-- Installing: /home/alex/test/install/lib/libexample.so
-- Installing: /home/alex/test/install/_auto_pc/pc_example-config.cmake
-- Installing: /home/alex/test/install/_auto_pc/pc_example-config-noconfig.cmake
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/alex/test/build/pc.example/build
alex@Alex-Desktop:~/test$ ls install/
example.pc  lib
alex@Alex-Desktop:~/test$ cat install/example.pc
Name: example
Cflags: -I/home/alex/test/install/include -DINSTALL
Libs: -L/home/alex/test/install/lib -lexample
这应该让你感到难过。这让我很难过。

关于cmake - 如何从 cmake 目标自动生成 pkgconfig 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44292462/

相关文章:

c++ - CMake 不生成依赖目标文件

c# - Visual Studio 2019 的 CSC 警告 CS1668

visual-studio - 在 cmake 中使用现有的 Visual Studio 项目

windows - 如何在 Windows 上通过 Python 以编程方式为 CMake 配置编译器?

windows - 如何在 Windows 上为 pkg-config 编写 *.pc 文件?

c++ - 我如何通过 CMake(qt5_wrap_ui) 指定 Qt Designer Form 类的文件名

shared-libraries - 在 pkg-config 中使用 Requires 与 Requires.private 时,链接过程有何不同?

autotools - 将库与 autotools 链接的正确方法

node.js - npm 安装期间 mac 10.5.8 上的 node-gyp 'pkg-config: command not found'

python - 构建 Lupa 时出错(可能是 pkg-config 的问题)