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.txt
built when I typemake foo
? - Alternately, how can I cause target
foo_test.passed
to be built as part of updating targetfoo
AND build as part of theALL
target?
Here is an implementation which achieves the requirements.
It's a bit convoluted as it requires several phony targets and dependency chaining.
Step 1:
ALL
so that it is built as part of the global build processfoo/CMakeLists.txt
:Step 2:
libfoo
:foo
tolibfoo
as we've now usedfoo
for our earlier phony target.liblibfoo.a
, which is a bit ugly)foo/CMakeLists.txt
:foo_test
:Making the tests run automatically as part of the build is a bit convoluted.
You have to:
custom_command
which runs the tests and generates a sentinel file (foo_test.passed
) if they passcustom_target
(foo_test.run
) which depends on the sentinel (foo_test.passed
)foo
andfoo_test.run
foo_test/CMakeLists.txt
:As such, target
foo
haslibfoo
andfoo_test.run
as dependencies.As a result, both
make
andmake foo
buildlibfoo
and build and runfoo_test
(viafoo_test.run
andfoo_test.passed
)Perceived clunkiness:
The
foo
->foo_test.run
->foo_test.passed
->foo_test
dependency chain.In
boost-build
you can name the libraryfoo
without causing a clash between thefoo
phony target and thefoo
library (andb2 foo
builds both thefoo
library and its tests)However, it works, and in the absence of a more elegant solution, will give me what I want.