cmake 后处理静态库目标 [英] cmake post-process static library target

查看:75
本文介绍了cmake 后处理静态库目标的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 cmake 来创建我的静态库,其中的内容类似于

I am using cmake to create my static libraries with something along the lines of

add_library(library library.cpp)
install(TARGETS library DESTINATION lib)

它创建了我想要的 liblibrary.a.但是我想把它和一个库捆绑在一起,让我们说 vendor/proprietary.a 做一些像

which creates liblibrary.a which is what I want. However I would like to bundle that with a library, let's say vendor/proprietary.a by doing something custom like

tmp=$(mktemp -d)
cd $tmp
ar -x $<TARGET_FILE:library>
ar -x vendor/proprietary.a
ar -qc $<TARGET_FILE:library> *
rm -rf $tmp

我可以用 cmake 做到这一点而不忘记目标 library 实际上是一个库(例如,通过使用 add_custom_command/add_custom_target).

Can I do that with cmake without it forgetting that the target library is actually a library (eg by using add_custom_command/add_custom_target).

推荐答案

令人失望的是,这非常困难.我们设法在 Halide 团队中做到了,但这仅仅是因为这是企业客户的一项硬性要求.对于没有这种限制的其他读者,我这样说:这里有龙.使用 CMake 的常用目标和依赖项,让它将所有静态库放在最终产品的链接线上.

This is, disappointingly, quite hard. We managed to do it on the Halide team, but only because it was a hard requirement from a corporate client. To other readers without such constraints, I say this: here be dragons. Use CMake's usual targets and dependencies and let it put all the static libraries on the end-product's link line.

对 OP,我说,试试这个:

To OP, I say, try this:

首先,在您的 vendor 目录中创建一个 CMakeLists.txt,内容如下:

First, create a CMakeLists.txt in your vendor directory with the following content:

# 0. Convenience variable
set(proprietary_lib "${CMAKE_CURRENT_SOURCE_DIR}/proprietary.a")

# 1. Get list of objects inside static lib
execute_process(COMMAND "${CMAKE_AR}" -t "${proprietary_lib}"
                WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
                OUTPUT_VARIABLE proprietary_objects)

string(STRIP "${proprietary_objects}" proprietary_objects)
string(REPLACE "\n" ";" proprietary_objects "${proprietary_objects}")

# 2. Attach configure dependency to the static lib
set_property(DIRECTORY . APPEND PROPERTY
             CMAKE_CONFIGURE_DEPENDS proprietary.a)

# 3. Extract the lib at build time
add_custom_command(
    OUTPUT ${proprietary_objects}
    COMMAND "${CMAKE_AR}" -x "${proprietary_lib}"
    DEPENDS "${proprietary_lib}")

# 4. Get absolute paths to the extracted objects
list(TRANSFORM proprietary_objects
     PREPEND "${CMAKE_CURRENT_BINARY_DIR}/")

# 5. Attach the objects to a driver target so the
# custom command doesn't race
add_custom_target(proprietary.extract DEPENDS ${proprietary_objects})

# 6. Add a target to encapsulate this
add_library(proprietary OBJECT IMPORTED GLOBAL)
set_target_properties(proprietary PROPERTIES
                      IMPORTED_OBJECTS "${proprietary_objects}")
# TODO: add usage requirements
# target_include_directories(proprietary INTERFACE ...)

# 7. Force proprietary to run completely after extraction
add_dependencies(proprietary proprietary.extract)

这里有很多很多,但最终步骤很简单,复杂之处在于解释对 CMake 的依赖关系.此外,这附带了一个警告,即它仅适用于 Linux(或至少仅适用于与 GNU-ar 兼容的归档程序).可以为 MSVC 做类似的事情,但对于这个答案来说太多了.

There's a lot going on here, but ultimately the steps are straightforward and the complications are with explaining the dependencies to CMake. Also, this comes with the caveat that it is Linux-only (or at least GNU-ar-compatible archiver only). It is possible to do something similar for MSVC, but it would be too much for this answer.

因此,首先我们询问存档器哪些对象在库中,然后我们将其每行一个对象的输出轻松处理到 CMake 列表中.这是上面的第 1 步.

So first we ask the archiver which objects are in the library and we lightly process its one-object-per-line output into a CMake list. That's step 1 above.

第 2 步告诉 CMake,如果 proprietary.a 上的时间戳被更新,那么它将需要重新运行 CMake(从而获得一个新的对象列表).

Step 2 tells CMake that if the timestamp on proprietary.a is ever updated, then it will need to re-run CMake (and thereby get a new list of objects).

第 3 步创建一个自定义命令,它将在构建时运行归档工具以将对象提取到 vendor 构建目录中.

Step 3 creates a custom command which will, at build time, run the archiver tool to extract the objects into the vendor build directory.

在自定义命令运行后,步骤 4 将对象的(相对)列表转换为这些对象的绝对路径列表.这是为了 add_custom_target 的好处,它需要绝对路径(或者更确切地说,如果启用了某些策略,会使用相对路径做一些奇怪的事情).

Step 4 turns the (relative) list of objects into a list of absolute paths to those objects after the custom command runs. This is for the benefit of add_custom_target which expects absolute paths (or rather, does weird things with relative paths if certain policies are enabled).

第 5 步创建一个自定义目标来驱动存档提取.

Step 5 creates a custom target to drive the archive extraction.

步骤 6 创建一个导入对象库来封装提取的库.它必须是全局的,因为默认情况下导入的目标是目录范围的,这是对导入库功能的滥用.您可以在此处添加其他使用要求.

Step 6 creates an imported object library to encapsulate the extracted library. It has to be global because imported targets are directory-scoped by default and this is an abuse of the imported-library feature. You can add additional usage requirements here.

最后,第 7 步将驱动程序目标依赖于对象库.

Finally, step 7 puts a dependency to the driver target on the object library.

然后可以透明地使用它.举个例子:

This can then be used transparently. Here's an example:

cmake_minimum_required(VERSION 3.16)
project(example)

add_subdirectory(vendor)

add_library(library library.cpp)
target_link_libraries(library PRIVATE proprietary)

这篇关于cmake 后处理静态库目标的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆