71

I have a C++ project where I've used directories as more of an organizational element -- the way one might use packages in Java or directories in PHP. Directories are not intended to be self-sufficient elements, but rather just a way of organizing the whole of the project and keeping me from being overwhelmed by sources. How can I construct my CMakeLists.txt files to deal with this? Making the directories libraries doesn't seem to fit here, since they are all interdependent and not intended to be used that way.

As a related issue, most of the examples I've seen of multiple subdirectories in CMake (and there aren't very many of those) have ignored or glossed over the issue of setting include_directories, which is something I've been having trouble with. Short of combing my source files to determine which file depends on which and in what directory, is there anyway to just set all directories under /src/ as potential include directories and let CMake work out which ones are actually dependent?

Here's an example structure:

--src
  --top1
    --mid1
      --bot1
        --src1.cpp
        --hdr1.h
      --bot2
        --src2.cpp
        --hdr2.h
    --mid2
      --bot3
        --src3.cpp
        --src4.cpp
        --hdr3.h
  --top2
    --mid3
      --src5.cpp
      --hdr4.h

So on and so forth. How can I structure my CMakeLists.txt files to handle this sort of structure?

Daniel Bingham
  • 12,414
  • 18
  • 67
  • 93
  • 2
    Do you absolutely have to use CMake ? – Benoît Aug 03 '11 at 06:37
  • 1.) Are you working with a preferred Generator? 2.) Do you really think CMake should figure out what relies on what? I think it should be possible to define modules (e.g. mid1, mid2, top2) which should be independent from each other. 3.) Are you including #include "hdr2.h" or something like ? – Philipp Aug 03 '11 at 06:51
  • @benoit I suppose not, but that's what I'm currently using. I'd rather not use Automake, seems like a lot of effort to put in to learning a build system when my builds just aren't that complex. – Daniel Bingham Aug 03 '11 at 14:16
  • @Philipp 1) would that not be Cmake? 2) I may be able to definite directories as modules that are --mostly-- independent from each other. But that would beat the point of using an organization, as opposed to modular, directory structure. And in the case of my project where nearly everything is interdependent the modules would be much larger than I'd like. 3) I'm including "hdr2.h" – Daniel Bingham Aug 03 '11 at 14:18
  • 1
    If your folder don't constitute relative-isolated libraries, your organizational approach is merely an illusion - complex dependencies might still creep in. I would suggest keep template and non-template code separate. If possible, try to have implementations on templates on .cpp and instantiate the templates you'll need and nothing else – lurscher Aug 03 '11 at 16:16
  • @Daniel: With Generator I mean e.g. Visual Studio, because there you can put all files into one directory and use SOURCE_GROUP to sort the files. If you're working in the IDE, you don't "see" that they're all in one directory. This also solves the include_directories problem. I found that this approach is a good one in my projects. – Philipp Aug 03 '11 at 16:21
  • @philipp I'm a linux coder and I tend towards Eclipse. – Daniel Bingham Aug 13 '11 at 13:00
  • i can't describe how much i relate to your first two paragraphs and i hope the kitware documentation team sees this question. both of those topics are still difficult to find in the official documentation. – Woodrow Barlow Jun 18 '19 at 23:32

3 Answers3

72

Since the directory structure in your project is just there to keep your files organized, one approach is to have a CMakeLists.txt that automatically finds all sources files in the src directory and also adds all directories as include directories that have a header file in them. The following CMake file may serve as a starting point:

cmake_minimum_required(VERSION 3.12)

project (Foo)

file (GLOB_RECURSE Foo_SOURCES CONFIGURE_DEPENDS "src/*.cpp")
file (GLOB_RECURSE Foo_HEADERS CONFIGURE_DEPENDS "src/*.h")

set (Foo_INCLUDE_DIRS "")
foreach (_headerFile ${Foo_HEADERS})
    get_filename_component(_dir ${_headerFile} PATH)
    list (APPEND Foo_INCLUDE_DIRS ${_dir})
endforeach()
list (REMOVE_DUPLICATES Foo_INCLUDE_DIRS)

add_executable(FooExe ${Foo_SOURCES})
target_include_directories(FooExe PRIVATE ${Foo_INCLUDE_DIRS})

The two file(GLOB_RECURSE ... commands determine the set of source and header files. The foreach loop computes the set of include directories from the list of all header files. The CONFIGURE_DEPENDS flags tells CMake to re-run the glob command at build time.

One drawback with computing the set of source files is that CMake will not automatically detect when new files are added to your source tree. You manually have to re-create your build files then.

sakra
  • 62,199
  • 16
  • 168
  • 151
  • I'd already tried something similar, but with different include_directories logic (that failed miserably). I'll give this a go. – Daniel Bingham Aug 03 '11 at 14:15
  • 22
    Thank you for this! I can't fathom this was so hard to find -- is nobody out there organizing their source files in subdirectories? – Cygon May 24 '12 at 20:30
  • 2
    @Cygon - apparently not. :-) – William Payne Apr 04 '13 at 10:10
  • This won't work for me, the add_executable(FooExe ${Foo_SOURCES}) fails for me saying that there are too many arguements. Does this require a specific version of cmake or something? – Pip Nov 10 '14 at 16:47
  • 4
    This answer is pretty neat. However, the proper solution now days would be to use target_include_directories etc. – Dimitrios Menounos Jan 21 '15 at 09:06
  • 2
    `target_include_directories` is available on cmake versions 2.8.11+ I think. – C-- Nov 20 '15 at 08:30
  • I am also facing the same problem and used your solution. However, when building I get the "undefined reference to" error. Any ideas why this might be happening? – FanFan Apr 14 '17 at 13:51
  • @FanFan same, is it something to do with linking libraries? – A N Syafiq. May 17 '21 at 05:07
5

Though @sakra gave a good answer to this question, I believe it is more proper to approach it more in depth.

We want to separate our code into modules and libraries for many reasons. Like code encapsulation, re usability, easier debugging etc. This idea would propagate in compiling process too.

In other word, we want to divide the compilation process into little compilation steps, each belong to one module. So every module must have its own compilation procedure. This is why we use one CMakeLists.txt file per directory. Hence every directory would have its own compilation commands and there would be one master CMakeLists.txt file in the root directory of your project.

Here is an example. Consider the following structure of a project:

src/
|
- main.cpp
|
_sum/
    |
    - sum.h
    |
    - sum.cpp

We would have one CmakeLists.txt Per directory. First directory is the root directory of the project which src/ folder is in it. here is content for that file:

cmake_minimum_required(VERSION 3.4)
project(multi_file)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "-Wall")

add_subdirectory(src) 

Next CMakeLists.txt would located in src/ directory:

add_subdirectory("sum")

add_executable(out main.cpp)
target_link_libraries(out sum)

And the last one will be in the sum/ directory:

add_library(sum SHARED sum.cpp)

I hope this helps. I created a github repository in case you feel you need to see the code or you need further explanation.

sajed zarrinpour
  • 1,194
  • 10
  • 10
  • 1
    Why does this work? Where are the sourcefile lists implicitly passed to the top level directory? – SiGa Mar 10 '23 at 15:51
4

I'm not an expert on CMake but since there are no other answers I'll take a look at the documentaton and give it a go. Organizing source and include files in different directories is pretty much the norm.

It looks like CMake allows you to give a list of include directories: http://www.cmake.org/cmake/help/cmake-2-8-docs.html#command:include_directories

So something like:

include_directories("src/top1/mid1/bot1" "src/top1/mid1/bot2/" ... )

These are passed to the compiler so it can find the header files and will be passed for each of the source files. So any of your source files should be able to include any of the header files (which I think is what you're asking for).

Similar to that you should be able to list all your source files in the add_executable command:

add_executable(name "src/top1/mid1/bot1/src1.cpp" "src/top1/id1/bot2/src2.cpp" ...)

So this would be a naive way of getting everything to build. Each source file will be compiled and will look for headers in all those directories and then the object files will get linked together. Consider if there is any way of simplifying this such that you don't need so many include folders, maybe there are only a few common header files that need to be referenced by all source files. If things get more complex you can buiild sub-hierarchies into libraries etc. Also consider seperating source files and headers (e.g. in src and include).

Guy Sirton
  • 8,331
  • 2
  • 26
  • 36