21

I currently define a ${SRCS} variable in the CMakeLists.txt file in my projects root directory, listing every source file required for my executable:

SET (SRCS main.cpp
          dir1/file1.cpp
          dir1/file2.cpp
          dir2/file3.cpp
          dir2/file4.cpp)

How can I distribute this list across the CMakeLists.txt files in each of the subdirectories? That is:

CMakeLists.txt
    Adds main.cpp to SRCS and subdirectories dir1 and dir2

dir1/CMakeLists.txt
    Adds file1.cpp, file2.cpp to SRCS

dir2/CMakeLists.txt
    Adds file3.cpp, file4.cpp to SRCS
Rezzie
  • 4,763
  • 6
  • 26
  • 33

3 Answers3

29

It's best to hide all the details of setting up the variable SRCS in a CMake macro. The macro can then be called in all the project CMake list files to add sources.

In the CMakeLists.txt in the project root folder, add the following macro definition:

macro (add_sources)
    file (RELATIVE_PATH _relPath "${PROJECT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
    foreach (_src ${ARGN})
        if (_relPath)
            list (APPEND SRCS "${_relPath}/${_src}")
        else()
            list (APPEND SRCS "${_src}")
        endif()
    endforeach()
    if (_relPath)
        # propagate SRCS to parent directory
        set (SRCS ${SRCS} PARENT_SCOPE)
    endif()
endmacro()

add_sources(main.cpp)
add_subdirectory(dir1)
add_subdirectory(dir2)

message(STATUS "${SRCS}")

The macro first computes the path of the source file relative to the project root for each argument. If the macro is invoked from inside a project sub directory the new value of the variable SRCS needs to be propagated to the parent folder by using the PARENT_SCOPE option.

In the sub directories, you can simply add a macro call, e.g. in dir1/CMakeLists.txt add:

add_sources(file1.cpp file2.cpp)

And in dir2/CMakeLists.txt add:

add_sources(file3.cpp file4.cpp)
Flow
  • 23,572
  • 15
  • 99
  • 156
sakra
  • 62,199
  • 16
  • 168
  • 151
  • Are you sure there's nothing out-of-box to build a library with nested subdirectories?? – Cartesius00 Oct 03 '11 at 22:08
  • @James see the answer to this [question](http://stackoverflow.com/questions/6921695/how-can-i-build-a-c-project-with-multiple-interdependent-subdirectories) – sakra Oct 04 '11 at 19:52
  • Very thorough and helpful answer. Thank you! – dusktreader Oct 16 '12 at 23:37
  • 2
    I've tried your macro and it works as presented if I have a top directory and one level of subdirectories. It however does not work for more levels of nesting, for example with ., ./sub1 and ./sub1/sub2. In that case sub2 sees the SRCS correctly, sub1 also, but the top folder (.) sees only its own sources and the ones in sub1, without sub2... It turns out that I need to first do add_subdirectory() and only then call add_sources() - this way it works. Is there any way to avoid such behaviour? – Freddie Chopin Nov 12 '13 at 11:21
  • If you have more than one level of nested directories, use a [global property](http://www.cmake.org/cmake/help/v2.8.12/cmake.html#command:set_property) instead of a CMake variable. – sakra Nov 12 '13 at 13:52
2

In dir1/CMakeLists.txt put this:

set(SRCS ${SRCS} file1.cpp PARENT_SCOPE)

If that wouldn't work, the only solution is to

set(DIR1_SRCS "file1.cpp" PARENT_SCOPE)

and then in ./CMakeLists.txt

set(SRCS ${DIR1_SRCS} ${DIR2_SRCS})

Philipp Ludwig
  • 3,758
  • 3
  • 30
  • 48
arrowd
  • 33,231
  • 8
  • 79
  • 110
2

A similar macro like the one suggested by sakra was posted on the CMake mailing list by Michael Wild [1]. It uses global properties instead of propagating a variable upwards.

[1] http://www.cmake.org/pipermail/cmake/2010-March/035992.html

Benjamin
  • 144
  • 3