[CMake] ADD_SUBDIRECTORY() with a higher level directory

Michael Hertling mhertling at online.de
Mon Nov 7 00:27:25 EST 2011


On 11/06/2011 04:47 PM, Dan Kegel wrote:
> On Sun, Nov 6, 2011 at 7:27 AM, Daniel Dekkers <d.dekkers at cthrough.nl> wrote:
>> This "template" stuff should really be in the documentation, it would have
>> helped me a lot.
>> More than http://www.cmake.org/cmake/help/examples.html. A library called
>> "Hello", really?
> 
> FWIW, I'm gathering more examples at
> http://code.google.com/p/winezeug/source/browse/#svn/trunk/cmake_examples
> 
> I'm also facing questions of how to organize cmake builds for
> existing software, but haven't added any examples about that yet;
> the examples I have are simple things (how to use gtest, how
> to use eclipse, etc.)
> 
> In my case, the existing software consists of a bunch of
> libraries and programs currently living in their own versioned svn
> directories, e.g.
>    libfoo/releases/1.1/src/...
>    libbar/trunk/src/...
>    baz/releases/1.0/src/...
> and all the developers check those directories out into unversioned
> directories on local disk, e.g.
>    libfoo/src/...
>    libbar/src/...
>    baz/src/...
> There's simply no top level directory where one could put an enclosing
> CMakeLists.txt!
> I can't reorganize the source tree on the developers,
> so I'm making do by putting the enclosing CMakeLists.txt next to all
> the projects:
>    toplevel/trunk/CMakeLists.txt
> which is checked out to
>    toplevel/CMakeLists.txt
> It does
>    add_subdirectory(../libfoo libfoo)
>    add_subdirectory(../libbar libbar)
>    add_subdirectory(../baz baz)
> 
> This has the possible advantage that one can have multiple
> "top levels" (say, one for server code, and one for client).
> 
> I don't know how many shops suffer from this svn layout, but I'll probably
> add an example covering it anyway for completeness once I'm sure
> it doesn't explode in practice.
> - Dan

If I understand correctly, your overall project comprises several sub-
projects with source trees not residing beneath the overall project's
PROJECT_SOURCE_DIR, but scattered across the file system instead, as
they stem from different repositories; additionally, this possibly
varies from user to user, right? If so, I'd advise the following:

Provide a top-level project - as you do - with the regular top-down
hierarchy, e.g. core, core/src, examples, examples/example<n> etc.,
use ADD_SUBDIRECTORY() to traverse the hierarchy as usual, and when
you'd enter a sub-project's source tree - which is not present - use
a cached variable pointing at the source tree's location and a local
build directory. E.g., the following overall project X comprises the
sub-projects A and B with B depending on A; suppose that A and B are
*no* descendants of X, thus X/CMakeLists.txt can not process {A,B}/
CMakeLists.txt with ADD_SUBDIRECTORY({A,B}). Additionally, X has an
actual subdirectory "util" with an executable which depends on B:

# A/CMakeLists.txt:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
PROJECT(A C)
SET(CMAKE_VERBOSE_MAKEFILE ON)
FILE(WRITE ${PROJECT_BINARY_DIR}/a.c "void a(void){}\n")
ADD_LIBRARY(a SHARED a.c)

# B/CMakeLists.txt:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
PROJECT(B C)
SET(CMAKE_VERBOSE_MAKEFILE ON)
FILE(WRITE ${PROJECT_BINARY_DIR}/b.c "void b(void){}\n")
ADD_LIBRARY(b SHARED b.c)
TARGET_LINK_LIBRARIES(b a)

# X/util/CMakeLists.txt:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
PROJECT(UTIL C)
SET(CMAKE_VERBOSE_MAKEFILE ON)
FILE(WRITE ${PROJECT_BINARY_DIR}/util.c "int main(void){return 0;}\n")
ADD_EXECUTABLE(util util.c)
TARGET_LINK_LIBRARIES(util b)

# X/CMakeLists.txt:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
PROJECT(X C)
SET(CMAKE_VERBOSE_MAKEFILE ON)
SET(A_EXTERNAL_SOURCE_DIR ${PROJECT_SOURCE_DIR}/A CACHE PATH "A")
SET(B_EXTERNAL_SOURCE_DIR ${PROJECT_SOURCE_DIR}/B CACHE PATH "B")
ADD_SUBDIRECTORY(${A_EXTERNAL_SOURCE_DIR} A)
ADD_SUBDIRECTORY(${B_EXTERNAL_SOURCE_DIR} B)
ADD_SUBDIRECTORY(util)

The A and B projects' locations are provided by the cached variables
{A,B}_EXTERNAL_SOURCE_DIR that are preset as if A,B reside beneath X,
i.e. the, say, regular case. In order to build the project, the user
must just configure it with {A,B}_EXTERNAL_SOURCE_DIR set correctly
on the command line or the GUI, and everything else works as usual.
Note that the overall project X's logical organization is *exactly*
the same as in the regular case with A,B underneath X. Particularly,
there is no need for B to configure A with ADD_SUBDIRECTORY(../A) in
B/CMakeLists.txt or the like, no need to start the configuration with
anything else than the top-level CMakeLists.txt, no need to protect an
ADD_SUBDIRECTORY() command from being processed more than once, and...
no hard-coded assumptions on any part's location. The downside is that
you must provide the external locations, but this could be done rather
easily, e.g. via the -C option, and it could even be done individually
for each developer and also for multiple checked-out working copies of
the same sub-project. IMO, this is a somewhat appropriate solution for
an overall project - call it "logical" - with non-treelike code base.

Regards,

Michael

PS: If A and/or B is one day moved to X, one must change nothing.


More information about the CMake mailing list