我正在尝试用 CMake 替换我公司中的(慢速)构建工具。
我们当前的构建工具有一个很好的功能,它可以从一些子存储库中提取标签,然后使用它来下载(使用类似于 rpm 的包管理器,只处理开发包)匹配版本号的第三方。
另一个问题是存储库管理器会将软件包安装到多个用户使用的共享目录中:在执行“make clean”或“ninja clean”时,不得删除安装在该共享目录中的文件。
我一直在尝试用 CMake 复制这种行为。到目前为止,我可以使用 Make 完成这项工作,但不能使用 Ninja。
尝试#1:
add_custom_command(OUTPUT ${SHARED_DIR}/lib/somelib.a
COMMAND ${CMAKE_COMMAND} -DTEST_FILE=${SHARED_DIR}/lib/someLib.a -DRPM_ARG=someLib:${SOMELIB_VERSION} -P ${CMAKE_CURRENT_SOURCE_DIR}/rpm_install.cmake)
add_custom_target(checkSomeLib DEPENDS ${SHARED_DIR}/lib/someLib.a)
add_library(someLib_rpm STATIC IMPORTED)
set_property(TARGET someLib_rpm APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${SHARED_DIR}/include)
set_property(TARGET someLib_rpm APPEND PROPERTY IMPORTED_LOCATION ${SHARED_DIR}/lib/someLib.a)
add_dependencies(someLib_rpm check_SomeLib)
rpm_install.cmake 是:
if (NOT EXISTS ${TEST_FILE})
execute_process(
COMMAND custom_pm install ${RPM_ARG})
endif()
这很好用:如果文件不存在,它就会被安装。问题:“make clean”删除已安装的文件。
解决方案是使用 CLEAN_NO_CUSTOM 标记目录。这适用于 make,但不适用于 Ninja。
尝试#2:
由于 Make 和 Ninja 清理了这些文件,因为它们被声明为 OUTPUT,所以我们不要将它们声明为 OUTPUT:
add_custom_target(checkSomelib
COMMAND ${CMAKE_COMMAND} -DTEST_FILE=${SHARED_DIR}/lib/someLib.a -DRPM_ARG=someLib:${SOMELIB_VERSION} -P ${CMAKE_CURRENT_SOURCE_DIR}/rpm_install.cmake)
add_library(someLib_rpm STATIC IMPORTED)
set_property(TARGET someLib_rpm APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${SHARED_DIR}/include)
set_property(TARGET someLib_rpm APPEND PROPERTY IMPORTED_LOCATION ${SHARED_DIR}/lib/someLib.a)
add_dependencies(someLib_rpm check_SomeLib)
这是尝试#1的轻微变化。它适用于 Make,但不适用于 Ninja,因为 Ninja 一开始就知道没有规则能够创建库!
尝试#3:使用 ExternalProject_Add
include(ExternalProject)
ExternalProject_Add(
someLib
SOURCE_DIR sharedInstalledDir
UPDATE_COMMAND ""
PATCH_COMMAND ""
CONFIGURE_COMMAND ""
BUILD_COMMAND ${CMAKE_COMMAND} -DTEST_FILE=${SHARED_DIR}/lib/someLib.a -DRPM_ARG=someLib:${SOMELIB_VERSION} -P ${CMAKE_CURRENT_SOURCE_DIR}/rpm_install.cmake
BUILD_BYPRODUCTS ${SHARED_DIR}/lib/someLib.a
INSTALL_COMMAND ""
TEST_COMMAND ""
)
add_library(someLib_rpm STATIC IMPORTED)
set_property(TARGET someLib_rpm APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${SHARED_DIR}/include)
set_property(TARGET someLib_rpm APPEND PROPERTY IMPORTED_LOCATION ${SHARED_DIR}/lib/someLib.a)
问题是“make clean”和“ninja clean”仍然清理图书馆......
我想我可以通过添加一个配置步骤来使尝试#2 与 Ninja 一起工作,以便在创建时下载丢失的库。
问题是它将“静态”工作,而不是“动态”工作:如果子存储库中的更新指示库的更新版本,则检查步骤将失败,因为 someLib.a 尚不存在。(在 的命名中有一些版本信息someLib.a
。)
我想我可以像这样放置一些更复杂的东西(我还没有尝试过):
add_custom_command(OUTPUT somelibLnk.a
COMMAND ${CMAKE_COMMAND} -DTEST_FILE=${SHARED_DIR}/lib/someLib.a -DRPM_ARG=someLib:${SOMELIB_VERSION} -P ${CMAKE_CURRENT_SOURCE_DIR}/rpm_install.cmake
COMMAND ln -s ${SHARED_DIR}/lib/someLib.a someLibLnk.a)
add_custom_target(checkSomeLib DEPENDS someLibLnk.a)
add_library(someLib_rpm STATIC IMPORTED)
set_property(TARGET someLib_rpm APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${SHARED_DIR}/include)
set_property(TARGET someLib_rpm APPEND PROPERTY IMPORTED_LOCATION someLibLnk.a)
add_dependencies(someLib_rpm check_SomeLib)
这个想法是创建一个到真实图书馆的链接。链接可以删除没有问题。如果它不存在,那么它会触发下载并创建链接。
但是有没有更简单的解决方案?
更新 1:
我已将所有这些尝试替换为:
execute_process(COMMAND ${CMAKE_COMMAND} -DTEST_FILE=${SHARED_DIR}/lib/someLib.a -DRPM_ARG=someLib:${SOMELIB_VERSION} -P {CMAKE_CURRENT_SOURCE_DIR}/rpm_install.cmake)
add_library(someLib_rpm STATIC IMPORTED)
set_property(TARGET someLib_rpm APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${SHARED_DIR}/include)
set_property(TARGET someLib_rpm APPEND PROPERTY IMPORTED_LOCATION ${SHARED_DIR}/lib/someLib.a)
以便在配置时安装依赖项。
这适用于版本号硬编码到 CMakeLists.txt 中的依赖项。
这也解决了“make clean”和“ninja clean”的问题,因为没有库/可执行文件是目标的输出。这很简单。
我现在正在尝试寻找“动态”版本号的解决方案。(在构建时从 Git 中提取的那个。)
我的计划是添加一个触发外部 CMake 脚本的 custom_target,该脚本将提取 Git 版本信息,然后更新将包含在 CMakeLists.txt 中的 .cmake 文件。我希望这会重新触发 CMake,一切都会好起来的。你认为这可能奏效吗?
更新 2:
这不起作用,尽管我能够做上面描述的事情。这不会触发rebuild_cache。
对于那些感兴趣的人,我做了这样的事情:
if (NOT EXISTS ${CMAKE_BINARY_DIR}/someLib_version.cmake)
execute_process(COMMAND ${CMAKE_COMMAND} -DDIR=${CMAKE_SOURCE_DIR} -DDST=${CMAKE_BINARY_DIR}/someLib_version.cmake -P ${CMAKE_CURRENT_SOURCE_DIR}/extract_version.cmake)
endif()
include(${CMAKE_BINARY_DIR}/someLib_version.cmake)
add_custom_target(extract_version ALL
COMMAND ${CMAKE_COMMAND} -DDIR=${CMAKE_SOURCE_DIR} -DDST=${CMAKE_BINARY_DIR}/someLib_version.cmake -DSRC_DIR=${CMAKE_SOURCE_DIR} -DBIN_DIR=${CMAKE_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/extract_version.cmake)
并且 extract_version.cmake 是:
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --match someLib_v*
COMMAND cut -c6-
WORKING_DIRECTORY ${DIR}
OUTPUT_VARIABLE SOMELIB_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE)
file(WRITE ${DST}.tmp "set(SOMELIB_VERSION ${SOMELIB_VERSION})")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different ${DST}.tmp ${DST})
#execute_process(COMMAND ${CMAKE_COMMAND} -H${SRC_DIR} -B${BIN_DIR})
那时,我不知道如何使用 CMake 解决我的用例。也许这是不可能的。