admin管理员组

文章数量:1355679

In the CMakeLists.txt of the root-level, I use add_subdirectory to add two directories that have two CMakeLists correspondingly responsible for building different stuff. How can I use the first project into the second one, passing the cmake configuration (i.e. what is to be included for that project) instead of manually copying cmake commands from one CMakeLists.txt to the other?

$ tree

├── libs
│   └── dependency
│       ├── CMakeLists.txt
│       ├── include
│       ├── other_include
│       └── src
│
├── project
│   ├── CMakeLists.txt
│   ├── include
│   └── src
│
└── CMakeLists.txt
...
...
add_subdirectory(libs/dependency)
add_subdirectory(project)
...
...

Pasting the vital content from the one file to another works. But in my case, this content is about a few paths to the header files, some to the implementation files and the linking to the library. However, I am very curious to learn what's the best practice in such circumstances, especially when it comes to more complicated projects. So I am keeping the question as simple and generic as possible, hoping it s not something obvious that I miss.

In the CMakeLists.txt of the root-level, I use add_subdirectory to add two directories that have two CMakeLists correspondingly responsible for building different stuff. How can I use the first project into the second one, passing the cmake configuration (i.e. what is to be included for that project) instead of manually copying cmake commands from one CMakeLists.txt to the other?

$ tree

├── libs
│   └── dependency
│       ├── CMakeLists.txt
│       ├── include
│       ├── other_include
│       └── src
│
├── project
│   ├── CMakeLists.txt
│   ├── include
│   └── src
│
└── CMakeLists.txt
...
...
add_subdirectory(libs/dependency)
add_subdirectory(project)
...
...

Pasting the vital content from the one file to another works. But in my case, this content is about a few paths to the header files, some to the implementation files and the linking to the library. However, I am very curious to learn what's the best practice in such circumstances, especially when it comes to more complicated projects. So I am keeping the question as simple and generic as possible, hoping it s not something obvious that I miss.

Share Improve this question asked Mar 28 at 13:34 cestpasmoicestpasmoi 6758 silver badges19 bronze badges 7
  • Do the sub-CMakeLists define their own CMake project? If not, all targets created in libs/dependency are visible in the root CMake file and the one in project. No need for copy-pasting. – Botje Commented Mar 28 at 13:37
  • @Botje yes they do. – cestpasmoi Commented Mar 28 at 13:40
  • 2 hmm, that should still work. Can you edit your question with a concrete error? You should be able to just reference targets in libs/dependency` instead of copypasting stuff around... – Botje Commented Mar 28 at 13:50
  • 2 that should not happen if you are using target_include_directories(some_dependency PUBLIC ...) to annotate the some_dependency target with an include directory and use target_link_libraries(some_project PUBLIC some_dependency) to link the two. CMake is all about targets and their (transitive or inherited) properties... – Botje Commented Mar 28 at 14:10
  • 1 "I would like to keep the question more generic and not destined to a particular issue" - The more "generic" question you have, the less code you will get in answers. If you want to propagate things from one subproject to another, then you could use targets, CACHE variables, functions and probably some other things. There are many CMake commands which use targets and variables, so if you want more specific solutions (and code), then you need to describe your case in more details. – Tsyvarev Commented Mar 28 at 16:00
 |  Show 2 more comments

1 Answer 1

Reset to default 1

Assuming dependency is a library that is linked by project, you would create a library target using add_library(dependency src/dependency.cpp) and then use target_include_directories(dependency INTERFACE $CMAKE_CURRENT_LIST_DIR/../include) to let whomever uses dependency that -I $CMAKE_CURRENT_LIST_DIR/../include needs to be added to the include path to allow for it to be included by the source files trying to use it.

The order of the creation of targets is irrelevant, as CMake will defer resolving that until later.

FWIW, when I'm putting together a build system I have a convention that directory structure is set up so that the include is to be specified like: #include "mylibrary/mylibrary.h" so that only -I <root>/libraries/ ends up being added to the compile flags (reduces the compilation time because it doesn't need to search so many directories).

So something like:

/
+ libraries/
|           + CMakeLists.txt
|           + lib1/
|           |     + CMakeLists.txt
|           |     + lib1.h
|           |     + src/
|           |          + lib1.cpp
|           + lib2/
|                 + CMakeLists.txt
|                 + lib2.h
|                 + src/
|                      + lib2.cpp
+ projects/
          + project1/
                    + CMakeLists.txt
                    + src/
                         + project1.cpp

So in this case, both lib1 and lib2 will add libraries/ to their interface includes, so when you have target_link_libraries(project1 lib1 lib2) then lib1 will add ./libraries/ to the include path and so will lib2, and it'll collapse to a single -I ./libraries/ and #include "lib1/lib1.h" and #include "lib2/lib2.h" will resolve properly when project1.cpp is being built.

For more info on this, look for "modern cmake" documentation. The idea is "properties" of targets are transitive and you attach whatever property is required to build a target to the target, so when something uses that target the properties will be applied to the user of the target.

The key to controlling this 'transitive' stuff that PUBLIC means add to both the target and the user of the target. PRIVATE means just added to the target when it's being compiled, and INTERFACE properties are only to the user of the target when it is compiling.

本文标签: