分层CMake项目,在构建“;“内部”;(非根)项目

分层CMake项目,在构建“;“内部”;(非根)项目,cmake,build,Cmake,Build,假设我有一个分层的CMake项目,由不同的项目组成: CMakeLists.txt proj-1/CMakeLists.txt proj-2/CMakeLists.txt (...) proj-n/CMakeLists.txt 显然,每个项目都会有源文件 我会确保所有感兴趣的命令都添加到根CMakeLists.txt文件中,比如说,CMAKE\u CXX\u STANDARD,enable\u testing(),add\u compile\u options(),等等。如果我理解正确,根CM

假设我有一个分层的CMake项目,由不同的项目组成:

CMakeLists.txt
proj-1/CMakeLists.txt
proj-2/CMakeLists.txt
(...)
proj-n/CMakeLists.txt
显然,每个项目都会有源文件

我会确保所有感兴趣的命令都添加到根
CMakeLists.txt
文件中,比如说,
CMAKE\u CXX\u STANDARD
enable\u testing()
add\u compile\u options()
,等等。如果我理解正确,根
CMakeLists.txt
文件中包含的任何选项也将应用于所有子
CMakeLists.txt
文件--如果我错了,请纠正我,因为我指望这种行为。对于每个
X
=1,…,n,根
CMakeLists.txt
还包含一个
add_子目录(proj-X)
语句

无论如何。假设出于某种原因,我只想构建一个
proj-X
文件夹,比如
proj-1
。可能构建在其他项目中被破坏,或者可能我需要修复
proj-1
上的一个bug,它不依赖于其他项目,而且构建所有项目将花费永远的时间

要点是:我想在
proj-1/CMakeLists.txt上运行
cmake
,而不是在根
CMakeLists.txt
文件上运行
cmake
,但是我想确保
proj-1
的构建方式与在根
CMakeLists.txt
文件上运行
cmake
完全相同。这是一个问题,因为根
CMakeLists.txt
包含子
CMakeLists.txt
应该在从根构建的常规情况下“继承”的语句,而在这个场景中,我直接从
proj-1/CMakeLists.txt
(在这种情况下,根
CMakeLists.txt
文件不会出现在图片中。)

据我所知,一种可能是将所有选项从根
CMakeLists.txt
文件复制到其他每个
proj-X/CMakeLists.txt
文件。当然,这是一个黑客和维护噩梦,但我想它会起作用


还有其他可能的解决方案吗?比如说,我可以创建一个包含所有公共选项的文件并将其保存到根目录下,然后在每个
proj-X/CMakeLists.txt
文件中执行类似于
#include
的CMake>操作吗?如果运行相同的命令两次,是否会出现问题(从根目录开始构建时,在根目录下
CMakeLists.txt
proj-X/CMakeLists.txt
文件上分别打开一次)?

您可能需要修改一些CMakeLists.txt文件

我建议看(幻灯片可用)

其要点是,您的所有项目都应该提供构建或编译所需的一切,本质上是构建需求和使用需求。要以可维护和可扩展的方式实现这一点,您必须远离变量并设置全局选项(
add_compile_options
include_directories
,等等),而是关注目标(
target_compile_options
target_include_directories
,等等)

因此,在您的例子中,
proj-1/CMakeLists.txt
将提供一个目标(我们称之为
proj::proj1
),用于设置适当的
PUBLIC
INTERFACE
选项(我指的选项是所需的编译器功能、依赖项、包括目录等)

一个抽象的例子:

project(proj1)

add_library(proj1 src.cpp)
# This are private include files, whoever uses this library does not need them
target_include_directories(proj1 PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
# These are public, needed both by this target and by whoever uses it.
target_include_directories(proj1 PUBLIC
    # This is used when building the target
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/public/include>
    # This is used when the target is installed
    $<INSTALL_INTERFACE:include>)
# Instead of asking directly for a language standard we ask for a compiler feature. We make this public so whoever depends on this target knows they also need this feature.
target_compile_features(proj1 PUBLIC cxx_strong_enums)
# As above, but this is needed only by this target during the build.
target_compile_features(proe1 PRIVATE cxx_lambdas)

# Add an alias, users can use target_link_libraries(target PRIVATE|PUBLIC proj::proj1) to add this target as a dependency (this will propagate all the PUBLIC include paths, compile options, compile features, dependencies, etc.
add_library(proj::proj1 ALIAS proj1)
项目(proj1)
添加库(proj1 src.cpp)
#这是私有的包含文件,任何使用此库的人都不需要它们
目标目录(proj1 PRIVATE${CMAKE\u CURRENT\u SOURCE\u DIR}/include)
#这些都是公共的,这个目标和使用它的人都需要。
目标目录(proj1公共目录)
#这在构建目标时使用
$
#这在安装目标时使用
$)
#我们没有直接要求语言标准,而是要求编译器功能。我们将其公开,以便依赖此目标的人知道他们也需要此功能。
目标编译功能(proj1公共cxx强枚举)
#如上所述,但这仅是该目标在构建期间需要的。
目标功能(proe1专用cxx\u lambdas)
#如果添加别名,用户可以使用target|link_库(target PRIVATE | PUBLIC proj::proj1)将此目标添加为依赖项(这将传播所有公共包含路径、编译选项、编译功能、依赖项等)。
添加库(proj::proj1别名proj1)
这是高度抽象的,这取决于您在构建脚本中实际执行的操作,很难给出比Daniel Pfeifer更好的解释,因此我建议您观看他的演讲或至少阅读幻灯片。这将使您的构建脚本更易于编写、阅读和使用


另一个很好的资源是。

非常感谢,我将回顾这些幻灯片。不过:这是否意味着每个选项都应该放在自己的
proj-X/CMakeLists.txt
文件中?如果我有10个不同的选项,它们都是通用的呢?在每个选项中重复这10个不同的选项似乎不是正确的方法文件。这确实有点问题。首先想到的方法是在根
CMakeLists.txt
中的变量中添加公共选项,然后使用
target.*
将它们添加到特定目标。但是,如果您想将一个项目编译为独立项目,您将没有这些选项。您可以使用interfa捆绑这些选项并与之链接的ce库,但问题是您将该库放在何处以及如何找到它?我不知道一种处理此问题的通用方法。一种替代方法是始终从根项目生成,但具有禁用您不需要的目标的选项。但如果您愿意,这将不起作用提供仅包含一个子项目的包。