You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CMake自定义命令构建macOS通用静态库失败,求原因及修正方案

问题描述

我维护一个基于CMake的C++项目,依赖第三方Rust库。使用该库前需先编译为静态C库;由于要生成macOS通用二进制文件,需要分别编译arm64和x86_64架构版本,再通过lipo合并为通用静态库。

我曾用三个独立CMake自定义目标完成各步骤(构建两个架构的库、合并)并声明依赖,方案可行,但希望移除可能让用户困惑的中间目标。当前采用自定义命令的方案如下:

set (RESVG_LIB_OUTPUT_DIR ${CMAKE_BINARY_DIR}/resvg)
set (RESVG_LIB_OUTPUT ${RESVG_LIB_OUTPUT_DIR}/libresvg.a)

add_custom_command (
        WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/Ext/resvg/c-api
        COMMAND ${CMAKE_COMMAND} -E env MACOSX_DEPLOYMENT_TARGET=10.7 cargo build --release --target aarch64-apple-darwin --target-dir ${RESVG_LIB_OUTPUT_DIR}
        OUTPUT ${RESVG_LIB_OUTPUT_DIR}/aarch64-apple-darwin/release/libresvg.a)

add_custom_command (
        WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/Ext/resvg/c-api
        COMMAND ${CMAKE_COMMAND} -E env MACOSX_DEPLOYMENT_TARGET=10.7 cargo build --release --target x86_64-apple-darwin --target-dir ${RESVG_LIB_OUTPUT_DIR}
        OUTPUT ${RESVG_LIB_OUTPUT_DIR}/x86_64-apple-darwin/release/libresvg.a)

add_custom_command (
        DEPENDS ${RESVG_LIB_OUTPUT_DIR}/aarch64-apple-darwin/release/libresvg.a ${RESVG_LIB_OUTPUT_DIR}/x86_64-apple-darwin/release/libresvg.a
        WORKING_DIRECTORY ${RESVG_LIB_OUTPUT_DIR}
        COMMAND lipo -create -output libresvg.a aarch64-apple-darwin/release/libresvg.a x86_64-apple-darwin/release/libresvg.a
        OUTPUT ${RESVG_LIB_OUTPUT})

add_library (resvg STATIC IMPORTED)
set_target_properties (resvg PROPERTIES IMPORTED_LOCATION ${RESVG_LIB_OUTPUT})
target_include_directories (resvg INTERFACE ${CMAKE_CURRENT_LIST_DIR}/Ext/resvg/c-api)

当应用目标链接resvg目标时,出现错误:

ninja: error: 'resvg/libresvg.a', needed by 'my_application_target', missing and no known rule to make it

我认为这些自定义命令已定义构建规则,且此前用类似技术自动生成过构建过程中的源文件。另外,我也曾尝试将resvg目标声明为INTERFACE库并链接${RESVG_LIB_OUTPUT},问题依旧。

我的问题:

  1. 该方案为何无效?
  2. 是否可修改该方案,仅通过自定义命令实现?(已有基于自定义目标的可行方案,不接受自定义目标相关的解决方案)

原因分析
  • IMPORTED库目标不触发自定义命令:STATIC IMPORTED库的本质是告知CMake“该库文件已存在”,CMake不会主动为其查找或触发生成该文件的自定义命令。即便你通过add_custom_command定义了生成libresvg.a的规则,CMake也不会将规则与IMPORTED库关联,构建系统(如Ninja)自然不知道需要先执行这些命令生成文件。
  • INTERFACE库的局限性:用INTERFACE库链接${RESVG_LIB_OUTPUT}时,CMake仅会把该路径作为链接项,但同样不会自动关联对应的自定义命令——因为没有目标明确依赖这个生成文件的构建规则,构建系统无法感知到需要先执行命令生成它。

修改方案(仅用自定义命令实现)

核心思路是让应用目标直接依赖最终的通用库文件,确保CMake能追踪到生成该文件的完整依赖链,具体有两种实现方式:

方式一:让应用目标直接依赖生成的库文件

在应用目标(如my_application_target)的定义中,添加对${RESVG_LIB_OUTPUT}的依赖:

add_executable(my_application_target ...)
# 让应用目标明确依赖生成的通用库,触发自定义命令链
add_dependencies(my_application_target ${RESVG_LIB_OUTPUT})
target_link_libraries(my_application_target PRIVATE resvg)

CMake识别到${RESVG_LIB_OUTPUT}是自定义命令的输出后,会自动触发所有前置自定义命令(编译两个架构的库、合并)。

方式二:将自定义命令输出关联到IMPORTED库

通过add_dependencies把生成文件的依赖链绑定到IMPORTED库,这样所有链接resvg的目标都会间接触发自定义命令:

add_library(resvg STATIC IMPORTED)
set_target_properties(resvg PROPERTIES
    IMPORTED_LOCATION ${RESVG_LIB_OUTPUT}
    INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}/Ext/resvg/c-api
)
# 让IMPORTED库目标依赖生成的通用库,间接触发全部自定义命令
add_dependencies(resvg ${RESVG_LIB_OUTPUT})

两种方式的核心都是让CMake的依赖图包含自定义命令的输出节点,确保构建系统能找到生成libresvg.a的规则,按顺序执行编译双架构库、合并的步骤。


内容的提问来源于stack exchange,提问作者PluginPenguin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 10:33:28