CMake,第三方,忍者和清洁

时间:2017-01-23 11:16:01

标签: cmake ninja

我正在尝试使用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标记目录。这适用于制作,但不适用于忍者。

尝试#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”的问题,因为没有库/可执行文件是目标的OUTPUT。这很简单。

我现在正试图找到“动态”版本号的解决方案。 (在构建时从Git中提取的那个。)

我的计划是添加一个custom_target,用于触发外部CMake脚本,该脚本将提取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解决我的用例。也许这是不可能的。

0 个答案:

没有答案