问题
I am converting a boost-build build system to cmake.
One of the features of boost-build is that you can specify a path to a Jamfile (the equivalent of a CMakeLists.txt file) and all the targets specified therein will be built.
For example, with the following project structure:
root
|
+--- foo
| |
| +--- test
|
+--- bar
| |
| +--- test
|
+--- app
If you enter the following command:
$ b2 foo
The Jamfile under root/foo will be executed, resulting in the foo library being built, and the test tests being built and run
boost-build example
Here is a simple build configuration using 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
;
You will notice that within foo's Jamfile there is a directive build-project test
This means that if I type b2 foo then everything in lib/Jamfile will be executed, resulting in foo and foo/test being built.
Also, within the Jamroot there is a directive build-project foo
This means that if I just type b2 then everything in Jamroot will be executed, resulting in foo and foo/test being built.
It is thus easy to build the whole project and get all sources and all tests built.
It is also easy to build just a subdirectory and get only it's sources and tests build.
It is this behaviour I'm trying to replicate.
cmake example
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)
The above CMakeLists.txt structure allows me to make and have both foo and foo_test built.
However, if I specify make foo, only foo will be built, but foo_test won't be, and the tests won't be run.
Question:
- How can I have everything within
foo/CMakeLists.txtbuilt when I typemake foo? - Alternately, how can I cause target
foo_test.passedto be built as part of updating targetfooAND build as part of theALLtarget?
回答1:
Here is an implementation which achieves the requirements.
It's a bit convoluted as it requires several phony targets and dependency chaining.
Step 1:
- create a phony target which all other targets for a "module" will be collected under
- add the phony target to
ALLso that it is built as part of the global build process
foo/CMakeLists.txt:
# create a phony target which all 'foo' related items will be added to
add_custom_target(foo
ALL
)
Step 2:
- add every target within the "module" as a dependency to this phony target
libfoo:
- Note here I have renamed
footolibfooas we've now usedfoofor our earlier phony target. - (This will in fact result in the generated library being called
liblibfoo.a, which is a bit ugly)
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:
Making the tests run automatically as part of the build is a bit convoluted.
You have to:
- create the test executable
- add a
custom_commandwhich runs the tests and generates a sentinel file (foo_test.passed) if they pass - add a
custom_target(foo_test.run) which depends on the sentinel (foo_test.passed) - add a dependency between
fooandfoo_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
)
As such, target foo has libfoo and foo_test.run as dependencies.
As a result, both make and make foo build libfoo and build and run foo_test (via foo_test.run and foo_test.passed)
Perceived clunkiness:
The
foo->foo_test.run->foo_test.passed->foo_testdependency chain.In
boost-buildyou can name the libraryfoowithout causing a clash between thefoophony target and thefoolibrary (andb2 foobuilds both thefoolibrary and its tests)
However, it works, and in the absence of a more elegant solution, will give me what I want.
来源:https://stackoverflow.com/questions/36701286/cmake-replicate-boost-builds-build-everything-in-jamfile-behaviour