[CMake] cmake and multiple projects, targets and versions

Gints Gailītis gints.gailitis at gmail.com
Thu Oct 13 18:01:22 EDT 2011


Hi cmake list!

I've been playing around with cmake for a couple of weeks now, and am
loving (almost) every minute of it. A thing I really like about cmake
and that I feel will add the greatest value for me, besides the
cross-platform capabilities, is the "build products go outside the
source tree" philosophy - that I can basically choose where I want the
build products (and the OS-specific makefiles/project files) to go,
just by choosing where I run the cmake command. I really want to not
break this freedom while solving the problems that I have.

I'm working on making a suggestion at my company to move to cmake for
our building needs, but I'm stuck on coming up with a cmake-way of
handling some of the aspects of juggling more projects, targets and
versions of them at the same time.

Bear with me, I can't say that I've dug through everything there is to
know about cmake, so if I need to just RTFM, that is a suitable
answer, just point me to some specific command man pages that will
help me.

The scenario I wish to handle in an elegant way, is this:

Suppose a company is working on 4 projects: App_A, App_B, Lib_C and Lib_D.

The directory structure is very simple:

Top
 |-App_A
 |-App_B
 |-Lib_C
 |-Lib_D

Every one of the App/Lib folders has the following structure inside them:

App_X/Lib_X
 |-include (for libs only, this is where the interface they expose to
the outside world goes)
 |-src
 |-tests
 |-CMakeLists.txt

The CMakeLists.txts in each of the App/Lib folders contain their own
project, and add the subdirectories src and tests, which have their
own CMakeLists.txts that just add the actual libraries or executables
that they need. Each of the tests folders have two executables made,
with target names unit_tests and integration_tests.

Building each of these folders individually works fine, but I have the
following dependencies:
Lib_D depends on Lib_C
App_A depends on Lib_C
App_B depends on Lib_D

So far I've been taking care of the dependencies "outside" of the
cmake world - just by manually "including" and linking to hard coded
folders where the outputs of each of the builds go. Which also means
that I have to manually build Lib_C when someone checks in a change
for it, while I'm working on Lib_D, and then build Lib_D.

It would be nice to make cmake aware of these dependencies and
automatically detect that Lib_C needs a rebuild when I build Lib_D.

And it would be even nicer if the build folders did not have to be
hard coded - if I could just run cmake anywhere, and would get the
makefiles/project files for all of the Apps/Libs right there, _and_
they'd be aware of the dependencies they have.

The closest I've gotten to it is by making a top level CMakeLists.txt
(that would reside in the folder "Top" in the diagram above), has a
TOP/ROOT project in it and just includes the subfolders of the
individual Apps/Libs.

If none of the projects and targets in any of the folders shared the
same name, this would be the perfect solution that I'm looking for,
but, alas, they do not - every project has a target called unit_tests
and integration_tests.

I could bite the bullet and work around by naming them
Lib_C_unit_tests and so on, ONCE... but then, what if this happens:

Suppose App_A is "done", that's it, no more new features, we want to
lock it and only apply bug fixes, if needed. But since Lib_C is under
constant development and becomes backwards incompatible every now and
then in one way or another, we also want to lock down the version of
Lib_C that was used when App_A was "done". So I copy the folder of
Lib_C and name it Lib_C_v_1.2.3 or whatever, and add it to the top
level CMakeLists.txt. I can understand having to go into that folder
and rename the project and top level target from Lib_C to
Lib_C_v_1.2.3, and then linking the frozen App_A to that instead of
Lib_C.

But now I have two targets called Lib_C_unit_tests and
Lib_C_integration_tests - I need to rename those as well. OK, there's
only two of them... but what if Lib_C is actually more complicated -
it has many components which in turn have dependencies on each other -
renaming all of those to get rid of the duplicates will be a pain, and
we have to do this every time a lib or app branches (and in real life
that happens a bit more often than in a scenario of a company that
only has two apps and two libs).

So - that's the problem. Can you guys suggest an elegant cmake style solution?

I've thought about something like keeping a variable named ${VS} -
version string, keeping it short since I'd have to use it a lot -
every project and target would get named Lib_C_${VS} and
Lib_C_${VS}_unit_tests and so on, and then each of the Lib_C/App_A
folder CMakeLists.txt would overwrite the value of it, so that the
CMakeLists.txt files in the src and tests folders and subfolders would
name their targets and projects the right way.

This could work, if we control all the libs that we link against,
although it would introduce some clutter in the project and target
names.

But what if some of the libs are not under our control - say gtest or
some other great open source lib that uses cmake (and I'm not even
trying to solve the problem of libs that don't use cmake right now ;)
) - and the need to keep two copies of them arises, since two of our
own projects might want to build and link against two different
versions of gtest or some other lib. Going over their cmake files and
renaming targets and projects would be _painful_ (not to mention wrong
- don't mess with the lib source, just use it).

Is there a way to make target and project names be "nested" instead of
"always top level", with the side effect of cmake creating the
makefiles/projects in a similar hierarchy and setting the dependencies
up right? So that, for example, when I have the need to branch gtest,
I could just make two folders, say, gtest_latest and gtest_1.2.3, in
the Top level folder, stick an "umbrella" CMakeLists.txt in each of
them, that creates the projects gtest_latest and gtest_1.2.3, and
include the CMakeLists.txt gtest provides as a subfolder that creates
a new project, gtest, inside each of those.

If that were the case, I could, in App_A, that needs to use the old
version of gtest, just say something along the lines of "when I say
gtest I mean Top:gtest_1.2.3:gtest", and all the CMakeLists.txt in the
subfolders of App_A, that do target_link_libraries(myTarget gtest)
would select the right version of it. Also, if the names were nested,
each of my projects could have their own target named unit_tests
without fear of colliding.

Now, I've googled plenty of posts online that try to address one or
more sub-problems of the scenario I presented, but nothing that solves
any real part of it in a _clean_ way - most of them are hackish and
require a great deal of manual labor every time a certain event
happens, such as renaming lots projects / targets when branching of a
library happens, etc, which is exactly the problem I'm trying to
eliminate.

I may just be daydreaming of a solution that does not yet exist... in
which case with your help we might be able to formulate a feature
request for cmake. I believe cmake could use a solution (or if it
exists, we need to shed some light on it in the public eye) for
problems like this, since I've encountered way too many posts of
people complaining about not being able to organize dependencies
between projects in an elegant way.

Anyway, looking forward to hearing how you handled similar problems -
the perfect solution that I'm looking for might not exist, but I'll
take a good tradeoff as well.

Thanks,
Gints


More information about the CMake mailing list