Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cmake: add_subdirectory() vs include()

Tags:

cmake

Let's say I have a C++ unit-test project using cmake like this:

$ tree . ├── C-API-ConditionVariable-unit-test │   ├── C-API-ConditionVariable-compile-link-test.c │   ├── C-API-ConditionVariable-unit-test-0.cpp │   └── C-API-ConditionVariable-unit-test-1.cpp ├── C-API-Mutex-unit-test │   ├── C-API-Mutex-compile-link-test.c │   ├── C-API-Mutex-unit-test-0.cpp │   └── C-API-Mutex-unit-test-1.cpp ├── some │   └── deeply │       └── nested │           └── path │               └── SomeFeature-unit-test │                   ├── SomeFeature-compile-link-test.c │                   ├── SomeFeature-unit-test-0.cpp │                   └── SomeFeature-unit-test-1.cpp └── CMakeLists.txt 

Each source file from the subfolders creates a separate executable. I would like to have each subfolder in the project to be a non-standalone module - that is each subfolder is (more-or-less) self-contained, but is NOT a standalone cmake project which can be compiled separately. I would like to be able to only build everything or nothing. For example I don't want to make an impression that you could run cmake only from some/deeply/nested/path/SomeFeature-unit-test to build only that.

Which option should I choose?

  1. CMakeLists.txt file in each subfolder + add_subdirectory() in top-level CmakeLists.txt;
  2. some-random-name.cmake in each subfolder + include() in top-level CmakeLists.txt;
  3. ignore my idea for each subfolder to be self-contained and put all relevant build info in the top-level CMakeLists.txt, additional cmake files only as helpers (macros, functions, ...);

The first option is the most convenient, but it suggests that each subfolder is actually a standalone project which could be compiled separately.

The second option seems to clearly suggest that there is only one cmake project here. But then in each some-random-name.cmake in subfolders I have to use full path to source files, which is against my desire for each of them to be self contained. If there's only one level of nesting (like for first two example subfolders) it is ok, but for some/deeply/nested/path/SomeFeature-unit-test this is not so nice. I would also have to prefix the names of output files.

The third option seems like an easy way to quickly create a CMakeLists.txt file with a length of a spaghetti, so it possible I would prefer something else.

I would like know which is the "preferred" way in a "modern" cmake project. All the tutorials I could find for multi-directory project deal with the case when there's a standalone library in one folder, standalone application using that library in another folder and standalone tests using the same library in yet another directory. Looking at projects using cmake I see no consistency, so this doesn't help much.

(I'm a cmake noob, trying to convert a relatively large project to use cmake)

like image 245
Freddie Chopin Avatar asked Jan 29 '18 21:01

Freddie Chopin


People also ask

What does Add_subdirectory do in CMake?

Add a subdirectory to the build. Adds a subdirectory to the build. The source_dir specifies the directory in which the source CMakeLists.

What is include directories in CMake?

The include directories are added to the INCLUDE_DIRECTORIES directory property for the current CMakeLists file. They are also added to the INCLUDE_DIRECTORIES target property for each target in the current CMakeLists file. The target property values are the ones used by the generators.

What is Cmake_current_list_dir?

CMAKE_CURRENT_LIST_DIR: Full directory of the listfile currently being processed. As CMake processes the listfiles in your project this variable will always be set to the directory where the listfile which is currently being processed (CMAKE_CURRENT_LIST_FILE) is located. The value has dynamic scope.

What does CMakeLists TXT do?

CMakeLists. txt file contains a set of directives and instructions describing the project's source files and targets (executable, library, or both). When you create a new project, CLion generates CMakeLists. txt file automatically and places it in the project root directory.


1 Answers

The most commonly used rule is "One CMakeLists.txt per target". So your option No. 1.

To achieve this your project structure may have to adapt, if "each source file from the subfolders creates a separate executable".

And the root CMakeLists.txt is your starting point and contains the project() command.

It's important to note that

  • CMake will mirror your add_subdirectory() and CMakeLists.txt directory structure in its generated build environment
  • CMake will create a new variable scope with each add_subdirectory() call (the big difference to using include() command)

What you should avoid is having to reference source files out of other subdirectories with something like ../some_other_dir/some_other_source.cpp. It shows that your CMake structure is not setup according to the rule of thumb quoted above.

Documentation Extracts

  1. CMake: add_subdirectory() command

    Add a subdirectory to the build. The source_dir specifies the directory in which the source CMakeLists.txt and code files are located.

  2. CLion: CMakeLists File

    When a project has the complex structure and includes one or more subdirectories (project root and subdirectories), you can create subdirectory CMakeList.txt files. Subdirectory CMakeLists.txt files describe the build, contents, and target rules for a subdirectory.

  3. C++Now 2017: Daniel Pfeifer "Effective CMake: a random seletion of best practices

    Directories that contain a CMakeLists.txt are the entry point for the build system generator. Subdirectories may be added with add_subdirectory() and must contain a CMakeLists.txt too.

References

  • Embracing Modern CMake
  • CMake tutorial
  • CMake with subdirectories
  • CMake share library with multiple executables
  • Renaming `CMakeLists.txt`
like image 69
Florian Avatar answered Sep 29 '22 22:09

Florian