【问题标题】:Using cmake to create main and sub-projects, and be able to compile them individually使用 cmake 创建主项目和子项目,并能够单独编译它们
【发布时间】:2016-03-01 20:26:40
【问题描述】:

免责声明:我是 CMakeLists.txt 的新手,我有一个可行的实现并希望改进和增强它,问题描述如下:

如果我想将 root/sub-directories/ 作为单独的子项目,可以使用其文件夹中的单个 CMakeLists.txts 进行编译,我发现自己实际上是在复制粘贴几乎整个根文件 CMakeLists.txt 每个子目录。

我想知道是否有更好的方法来拥有一个主项目,然后是从主项目获取共享依赖项的子项目,并且可以在没有 cmake-ing 根 CMakeLists.txt 的情况下进行编译。我的目录结构是;

CMakeLists.txt(根项目)
| __ sub_dir-1
| __ | __ CMakeLists.txt(子项目)
| __ sub_dir-2
| __ | __ CMakeLists.txt(子项目)
| __ sub_dir-3
| __ | __ CMakeLists.txt(子项目)
...

基本上,我希望能够:

  1. cmake root/CMakeLists.txt,它创建了一个包含子项目的整个项目(我已经使用子目录中的单个 CMakeLists.txt 实现了这个。
  2. cmake root/sub-dir/CMakeLists.txtonly 编译子项目,它本质上还找到了必要的依赖项,并可能从 .cmake 包含或 root/CMakeLists.txt 中包含它们。处理这种结构的最佳方法是什么;

    • 同时保留第一个要点,即能够将其编译为整个项目。
    • 还有没有拥挤sub-dir/CMakeLists.txt 有太多冗余代码?

感谢任何建议!谢谢。

【问题讨论】:

  • 减少复制粘贴的方法有很多:include()function()/macro()、变量。您可能知道子项目,无论它们是单独构建还是作为根项目的一部分,通过检查变量/目标/函数并采取适当的措施。试着编程你想要的。
  • 能够单独配置(cmake)项目和能够单独编译(make)它们之间有相当大的区别。我的经验是,您可以通过在顶层目录运行 cmake,然后指定要使用 make 构建的内容:cmake <root-dir>; make sub-project1。什么对你来说最重要?
  • @Emil 实际上两者都很重要,我希望能够使用cmake 配置它们,然后使用make 作为子项目和主项目进行编译。
  • @Tsyvarev 我知道有很多方法,我希望有人能指出一种有效的方法,因为我列出了一些我想满足的限制。你会碰巧有一个我可以效仿的例子吗?
  • 效率一种或另一种方法高度取决于具体情况。对于一种情况最有效的方法在其他情况下会过于复杂。提供一些(简化的)代码,在你的根项目和子项目之间是相似的,我(或其他人)将展示你可以减少代码重复的方法。

标签: cmake


【解决方案1】:

有一些策略可能有效,其中一些可以结合使用。

策略 1:

如果您在 CMake 中使用 Unix Makefiles 生成器,那么只需从与您要构建的子目录对应的构建输出目录中调用 make 即可在很大程度上完成您所描述的工作。除非您正在构建的子目录(或该子目录下面的子目录)的目标需要它们,否则不会构建来自其他目录的目标。因此,当您从根目录运行 CMake 时,您仍然只构建您想要的子目录。

优点:简单,无需更改您的 CMakeLists.txt 文件。

缺点:仅适用于 Unix Makefiles 生成器,无论您是否要构建它们,都需要 CMake 处理源代码树的所有部分。

策略 2:

正如@Emil 在他对您的问题的评论中提到的那样,您可以简单地从构建输出目录的顶层构建您想要的特定目标。这假设您知道要构建的相关目标,或者换句话说,它要求您很好地了解哪些目标由哪些子目录提供。

优点:灵活,适用于任何 CMake 生成器,无需更改您的 CMakeLists.txt 文件。

缺点:需要了解每个子目录提供的内容,无论您是否要构建它们,都需要 CMake 处理源代码树的所有部分。

策略 3:

在顶级CMakeLists.txt 文件中,您可以使每个子目录的add_subdirectory() 调用依赖于一个选项变量。这将允许您单独打开或关闭每个子目录的包含,让您精确控制构建的内容。您可以构建所有内容,只需一个子目录或一组子目录(比您的要求更灵活,但可能有用)。

有条件地包含一个带有选项变量的子目录通常会这样做:

option(BUILD_SUBDIR1 "Enable building subdir1" ON)
option(BUILD_SUBDIR2 "Enable building subdir2" ON)

if(BUILD_SUBDIR1)
    add_subdirectory(subdir1)
endif()
if(BUILD_SUBDIR2)
    add_subdirectory(subdir2)
endif()

如果某些子目录依赖于其他子目录,那么是否添加子目录的逻辑需要考虑到这一点(即实现选项之间的依赖关系)。如果需要,应该不难做到。

优点: 灵活地在一次构建中精确构建您想要的任何子目录集,CMake 只需要处理您感兴趣的那些子目录(为大型项目树节省时间并允许您跳过有问题的部分项目树的) 并且适用于任何 CMake 生成器。

缺点:如果您想单独构建不同的子目录,则需要单独的构建目录。例如,您不能简单地将cd 转到构建输出目录结构的不同部分并从那里运行构建。您仍然只有一个源代码树,但您有多个构建输出树。实际上,这可能不是问题,具体取决于您最终希望能够做什么(我经常为给定的源代码树有多个构建目录,但这通常用于调试和发布构建,而不是不同的 CMake 选项集)。还需要了解子目录之间的依赖关系。需要对顶级 CMakeLists.txt 文件进行轻微更改。


希望上述选项之一或它们的某种组合能够为您提供一些关于如何最终实现目标的想法。我快速浏览了您链接到的 github 项目中的顶级 CMakeLists.txt,它看起来已经使用了策略 3 中的选项,只是不能打开/关闭各个子目录。

【讨论】:

  • 是的 Craig,我认为我们已经在某种程度上实施了选项 3,非常感谢您的回答!那么一个问题是,如果人们只想使用该选项命令构建一个特定的项目,有没有办法通过终端 cmake -option=project2 或类似的东西来做到这一点?而不是每次他们想构建一个单独的项目时都更改CMakeLists.txt
  • option() 命令的重点正是如此。您可以在调用 cmake 时设置选项,如下所示:cmake -DBUILD_SUBDIR1=ON。 CMake 将在其缓存中记住这一点,因此您不必在此后重新运行 cmake 或使用 make 构建时继续指定它。 option() 命令采用默认值,如果您未在 cmake 命令行上设置该选项,则使用该默认值。
  • 有道理。再次感谢@Craig Scott!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-24
  • 1970-01-01
  • 2011-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多