cmake:复制 boost-build 的 "build everything in Jamfile" 行为?

cmake: replicate boost-build's "build everything in Jamfile" behaviour?

我正在将 boost-build 构建系统转换为 cmake。

boost-build 的一个特点是您可以指定 Jamfile 的路径(相当于 CMakeLists.txt 文件),其中指定的所有目标都将被构建。

例如,具有以下项目结构:

root
|
+--- foo
|    |
|    +--- test
|
+--- bar
|    |
|    +--- test
|
+--- app

如果输入以下命令:

$ b2 foo

root/foo 下的 Jamfile 将被执行,导致构建 foo 库,构建 test 测试和 运行

增强构建示例

这是一个使用 boost-build:

的简单构建配置

Jamroot:

using gcc ;

project proj : requirements
    <link>static
    <include>.
;

build-project foo ;

foo/Jamfile:

lib foo : [ glob *.cpp ] ;

build-project test ;

foo/test/Jamfile:

import testing ;

unit-test foo-tests
:   [ glob *.cpp ]
    ..//foo
;

你会注意到在 foo's Jamfile 中有一个指令 build-project test

这意味着如果我键入 b2 foo 那么 lib/Jamfile 中的所有内容都将被执行,导致 foo foo/test 正在建设中。

此外,在 Jamroot 中有一个指令 build-project foo

这意味着,如果我只键入 b2,那么 Jamroot 中的所有内容都会被执行,从而导致构建 foofoo/test

因此很容易构建整个项目并获得构建的所有源代码和所有测试。

也很容易构建只是一个子目录并获得只有它的源代码和测试构建。

我正试图复制这种行为。

cmake 示例

root/CMakeLists.txt:

cmake_minimum_required(VERSION 3.2.2)
project(proj CXX)

add_subdirectory(foo)

foo/CMakeLists.txt:

file(GLOB src "*.cpp")

add_library(foo STATIC ${src})

add_subdirectory(test)

foo/test/CMakeLists.txt:

file(GLOB src "*.cpp")

add_executable(foo_test ${src})

add_test(foo_test foo_test foo)

# run tests if foo_test.passed is missing or outdated
add_custom_command(
    OUTPUT  foo_test.passed
    COMMAND foo_test
    COMMAND ${CMAKE_COMMAND} -E touch foo_test.passed
    DEPENDS foo_test
    )

# make tests run as part of ALL target
add_custom_target(run_foo_test
    ALL
    DEPENDS foo_test.passed)

上面的 CMakeLists.txt 结构允许我 make 并同时构建 foofoo_test

但是,如果我指定 make foo只会构建 foo,但不会构建 foo_test,并且测试不会 运行.

问题:

这是一个实现要求的实现。

它有点复杂,因为它需要几个虚假目标和依赖链。

第 1 步:

  • 创建一个虚假目标,"module" 的所有其他目标都将收集在
  • 将虚假目标添加到 ALL,以便将其构建为全局构建过程的一部分

foo/CMakeLists.txt:

# create a phony target which all 'foo' related items will be added to
add_custom_target(foo
    ALL
    )

第 2 步:

  • 将 "module" 中的每个目标添加为该虚假目标的依赖项

libfoo:

  • 请注意,我已将 foo 重命名为 libfoo,因为我们现在将 foo 用于我们之前的虚假目标。
  • (这实际上会导致生成的库被调用liblibfoo.a,有点难看)

foo/CMakeLists.txt:

add_library(libfoo STATIC ${src})

# add libfoo as a dependency of foo, so 'make foo' will build libfoo
add_dependencies(foo 
    libfoo)

foo_test:

作为构建的一部分自动进行测试 运行 有点复杂。

你必须:

  • 创建测试可执行文件
  • 添加一个 custom_command 用于 运行 测试并生成标记文件 (foo_test.passed) 如果它们通过
  • 添加一个 custom_target (foo_test.run) 取决于哨兵 (foo_test.passed)
  • foofoo_test.run之间添加依赖关系

foo_test/CMakeLists.txt:

add_executable       (foo_test main.cpp)
target_link_libraries(foo_test libfoo ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})

# create foo_test.passed command which generates the sentinel file when the tests pass
add_custom_command(
    OUTPUT  foo_test.passed
    COMMAND foo_test
    COMMAND ${CMAKE_COMMAND} -E touch foo_test.passed
    DEPENDS foo_test
    )

# create foo_test.run target which depends on foo_test.passed
add_custom_target(foo_test.run
    DEPENDS foo_test.passed
    )

# add foo_test.run as a dependency of foo, so 'make foo' will build foo_test.run
add_dependencies(foo
    foo_test.run
    )

因此,目标 foo 具有 libfoofoo_test.run 作为依赖项。

因此,makemake foo 都构建了 libfoo 并构建了 运行 foo_test(通过 foo_test.runfoo_test.passed)

感知笨拙:

  • foo -> foo_test.run -> foo_test.passed -> foo_test 依赖链。

  • boost-build 中,您可以将库命名为 foo,而不会导致 foo 虚假目标与 foo 库(和 b2 foo 构建 foo 库及其测试)

然而,它有效,并且在没有更优雅的解决方案的情况下,会给我我想要的。