8

I am trying to port my old CMake to modern CMake (CMake 3.0.2 or above). In the old design I had multiple CMakelists.txt, each directory contained a CMakeLists.txt file.

My current project's directory structure looks like :

.
├── VizSim.cpp
├── algo
├── contacts
│   ├── BoundingVolumeHierarchies
│   │   └── AABBTree.h
│   └── SpatialPartitoning
├── geom
│   └── Geometry.h
├── math
│   ├── Tolerance.h
│   ├── Vector3.cpp
│   └── Vector3.h
├── mesh
│   ├── Edge.h
│   ├── Face.h
│   ├── Mesh.cpp
│   ├── Mesh.h
│   └── Node.h
├── util
|   |__ Defines.h
|   |__ Math.h
|
└── viz
    └── Renderer.h

What I was planning to do was just use a single CMakelists.txt and place all the cpp files in SOURCE and all the headers in HEADER and use add_executable.

set (SOURCE
    ${SOURCE}
    ${CMAKE_CURRENT_SOURCE_DIR}/src/mesh/Mesh.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/src/math/Vector3.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/src/VizSim.cpp
    ....
)

set (HEADER
    ${HEADER}
    ${CMAKE_CURRENT_SOURCE_DIR}/src/mesh/Mesh.h
    ${CMAKE_CURRENT_SOURCE_DIR}/src/math/Vector3.h
    .... 
)

add_library(${PROJECT_NAME} SHARED ${SOURCE})

Doing this I am worried if using a single CMakeLists.txt is good practice. So does single CMakeLists.txt suffice or do I need a CMakeLists.txt for each folder?

I can only think of one good reason to have multiple CMakeLists.txt in my project and that is modularity.

Considering my project will grow eventually.

usr1234567
  • 21,601
  • 16
  • 108
  • 128
pokche
  • 1,141
  • 12
  • 36
  • AFAK, regardeless modularity, i cannot think any reason to use multiple CMakeLists.txt – Wei Guo Dec 14 '18 at 05:43
  • I agree with @WeiGuo: Usually, I have one `CMakeLists.txt` per library (project) with sources in one directory. Some of these libraries have that many files that I separated some of them in sub-directories. In this case, I use a similar approach like your (with the one exception that I introduced multiple variables for the different directories and added statements to let cmake generate multiple sub-folder for VS). – Scheff's Cat Dec 14 '18 at 06:53
  • @Scheff Thank you for the answer. So I should use one CMakeLists.txt. But what do mean when you say " I introduced multiple variables for the different directories and added statements to let cmake generate multiple sub-folder for VS" How do you tell cmake to generate multiple sub-folder. Can you give a little hint on how I can do that. Is this what you meant https://stackoverflow.com/questions/7787823/cmake-how-to-get-the-name-of-all-subdirectories-of-a-directory – pokche Dec 14 '18 at 12:01
  • _Is this what you meant [stackoverflow.com/questions/7787823/…](https://stackoverflow.com/questions/7787823/cmake-how-to-get-the-name-of-all-subdirectories-of-a-directory)_ Similar. ;-) – Scheff's Cat Dec 14 '18 at 12:14
  • _How do you tell cmake to generate multiple sub-folder._ Sorry, you got me wrong. I meant sub-folders in the visual project tree of VisualStudio. The separation of source files into directories is done by me (and I wouldn't want that cmake changes anything about this). ;-) – Scheff's Cat Dec 14 '18 at 12:18

2 Answers2

2

This is a bit long for a comment – so I make it an answer:

In one of my projects (a library), I have that many sources that I started to move some of them in a sub-directory util.

For this, I made separate variables:

file(GLOB headers *.h)
file(GLOB sources *.cc)
file(GLOB utilHeaders
  RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
  ${CMAKE_CURRENT_SOURCE_DIR}/util/*.h)
file(GLOB utilSources
  RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
  ${CMAKE_CURRENT_SOURCE_DIR}/util/*.cc)

To make it nice looking / more convenient in VisualStudio, I inserted source_groups which generates appropriate sub-folders in the VS project. I believe they are called "Filters".

source_group("Header Files\\Utilities" FILES ${utilHeaders})
source_group("Source Files\\Utilities" FILES ${utilSources})

Of course, I have to consider the variables utilHeaders and utilSources as well where the sources have to be provided:

add_library(libName
  ${sources} ${headers}
  ${utilSources} ${utilHeaders})

That's it.


Fred reminded in his comment that I shouldn't forget to mention that file(GLOB has a certain weakness (although I find it very valuable in our daily work). This is even mentioned in the CMake doc.:

Note: We do not recommend using GLOB to collect a list of source files from your source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated build system cannot know when to ask CMake to regenerate. The CONFIGURE_DEPENDS flag may not work reliably on all generators, or if a new generator is added in the future that cannot support it, projects using it will be stuck. Even if CONFIGURE_DEPENDS works reliably, there is still a cost to perform the check on every rebuild.

So, using file(GLOB, you shouldn't never forget to re-run CMake once files have been added, moved, or removed. An alternative could be as well, to add, move, remove the files directly in the generated built-scripts (e.g. VS project files) and rely on the fact that the next re-run of CMake will those files cover as well. Last but not least, a git pull is something else that it's worth to consider a re-run of CMake.

Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56
  • 1
    So what is the point of placing files in variable based on the name of the sub directory they are present in. Is this just for VS or does it have any other reason. I asked because I use vim as my IDE. – pokche Dec 14 '18 at 13:04
  • @pokche The extra variables `utilHeaders`/`utilSources` are there because the resp. lists are used two times (once in `source_group`, once in `add_library`). – Scheff's Cat Dec 14 '18 at 14:35
  • @pokche I just thought about how to fill one variable with `file(GLOB` out of multiple directories. After a short look into CMake doc. I saw this should work. So, this is not another reason why multiple variables could help. May be, it's a bit easier to read but I'm not sure. ;-) – Scheff's Cat Dec 14 '18 at 14:41
  • 1
    Regarding using GLOB: https://cmake.org/cmake/help/latest/command/file.html Note We do not recommend using GLOB to collect a list of source files from your source tree. If no CMakeLists.txt file changes when a source is added or removed then the generated build system cannot know when to ask CMake to regenerate. The CONFIGURE_DEPENDS flag may not work reliably on all generators, or if a new generator is added in the future that cannot support it, projects using it will be stuck. Even if CONFIGURE_DEPENDS works reliably, there is still a cost to perform the check on every rebuild. – fdk1342 Dec 22 '18 at 00:03
  • @Fred We are aware of the weaknesses of GLOB. (Although, it couldn't hurt to edit your link into answer.) Practically, we use CMake currently exclusively with VS. (All our efforts in portable development are in the moment just for fun as we have no customers for this. This may change in the future when we expand into cloud services where a Linux environment is more imaginable and would be transparent for users.) However, GLOB works fine in daily business. Whenever a I or a colleague faces link issues after `git pull`, re-running CMake is the first thing to try. – Scheff's Cat Dec 22 '18 at 08:46
  • @Fred Beside of this, when I add new files to a project I do this directly in the VS project without caring about CMakeLists.txt. The next run of CMake will cover this file as well thanks to `GLOB`, and (I believe) it even won't change the VS project file. This is said with the fact in mind that re-loading the VS solution could last multiple minutes which is sometimes really anoying. (Our application VS solution became a bit large over the last three years of development.) TLDR; in daily work, `GLOB` is very valuable and damn convenient beside of the little weaknesses you have to be aware of. – Scheff's Cat Dec 22 '18 at 08:52
  • 1
    @Scheff I understand what you are saying. But it assumes that every file in a folder is actually used for the target which may not be the case (certainly not multi-platform projects that don't want to have separate folders for every platform specific file for a library). I don't doubt you find it useful "in daily work", but to me it's just a broken way of using CMake that causes too many problems with the generators I use. I added my comment not because of your awareness but to inform others about the problems using this method. – fdk1342 Dec 22 '18 at 20:39
  • @Fred Concerning the multi-platform behavior, I must admit I'm still lacking experience. Our platform-specific layer is rather thin. The few things, which are not covered by C++ standard libraries or Qt (so that I had to do it with own source code) are wrapped into `#ifdef`s (even in cases where the whole file is for one specific platform only). `list(REMOVE` (or similar) is another CMake option to remove files from variables which shall not be considered in build but usually I remove files physically if they become obsolete. – Scheff's Cat Dec 23 '18 at 11:25
2

I would always recommend a CMakeList.txt file per directory. My reasons:

  • locality: keep everything in the same folder that belongs together. This includes the relevant parts of the build system. I would hate it to navigate to the root folder to see how a library or target was invoked.
  • separation of build artifacts and related build code: Tests belong below test, libraries below lib, binaries below bin, documentation below doc, and utilities below utils. This may vary from project to project. When I have to make a change to the documentation, why should I wade through dozens of unrelated CMake code? Just have a look into the right CMakeLists.txt.
  • avoid handling of paths: In most cases relative or absolute paths including stuff like ${CMAKE_CURRENT_SOURCE_DIR} can be avoided. That leads to maintainable build code and reduces errors from wrong paths. Especially with out-of-source build, which should be used anyway.
  • localization of errors: If a CMake error occurs it is easier to locate the problem. Often a sub-directory can be excluded as a first workaround.
usr1234567
  • 21,601
  • 16
  • 108
  • 128
  • Does the locality applies to sub-folders of a project like algo,contacts, geom .. etc? – mato Dec 17 '18 at 22:19
  • @mato. I'd question whether so many sub-folders make sense in the first place. Having one or two file doesn't help with structure, it just adds clutter. – usr1234567 Dec 17 '18 at 22:49
  • I agree. Last question, Let say mesh sub-folder does makes sense since it will grow and some one from out side could use it as a library. So mesh folder contain the CMakeLists.txt? Because with this logic any folder that could be potentially used by external user as library could endup having CMakeLists.txt. – mato Dec 17 '18 at 23:06
  • @mato: Depends on your opinion. I would have a folder for the single file, only if the single file ends up in a library (even a CMake object library). – usr1234567 Dec 18 '18 at 06:20
  • my programme is small, i want a single cmakelists.txt for all subdirs – Dee May 18 '19 at 01:36