【问题标题】:CMake shared project subdirectories avoid rebuildingCMake 共享项目子目录避免重建
【发布时间】:2018-05-27 15:12:53
【问题描述】:

对于一个项目,我使用带有 CMake 的 Android gradle 脚本,gradle 插件是 3:0:0 版本,CMake 版本是 3.6。 gradle 和 CMake 文件都非常简单且无趣(只需定义使用的文件 - 我仍然可以根据需要复制粘贴它们)。

我有以下项目结构;基本上是一个代码库,产生了几十个 .so 文件(被打包到 apk 中的 Android 包的本机部分,因此称为“可执行文件”),它们都依赖于相同的共享库代码(静态库,因此称为“库” ')。库代码仍然(相对)易变,所以我希望可执行文件对它们具有项目级别的依赖关系,这样每当构建可执行文件时,每次更改代码时都会按需重建库。 结构如下:

+ LibProjects/
---Bin/ (Originally empty)
---Lib1/CMakeLists.txt (+sources files, same level as the CMakeLists.txt)
...
---Lib10/CMakeLists.txt (same)
+ Executables/
---Executable1/CMakeLists.txt (source files here)
--------------/AndroidFiles/build.gradle (and other android project files)(points to the CMakeLists.txt)
...
---Executable40/CMakeLists.txt

库的 CMakeLists 使用

将其输出重定向到 Bin 文件夹
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY {CMAKE_CURRENT_SOURCE_DIR}/../Bin/${ANDROID_ABI}/${LibraryName})

可执行文件的项目“正常”添加对库的依赖项

add_subdirectory(${PROJECTS_ROOT}/LibProjects/${LibraryName} ${PROJECTS_ROOT}/Framework/Bin/Android/${ANDROID_ABI}/${LibraryName})...

几乎一切正常,因为我可以获得合理的可执行文件并且可执行文件触发库的构建。

问题在于,在按顺序构建可执行文件时,每个都不会重用其他库项目的输出:当我构建 Executable1 时,它将构建所有库(正常),然后它会自行构建。之后,当我构建 Executable2 时,它不会重用已经为 Executable1 构建的库,依此类推——这有效地将我的构建时间增加了约 10 倍。

我可以按预期在 /Bin 文件夹中找到每个库的构建输出,但它们不会跨可执行文件重用 - bin 文件夹中没有 CMake“项目文件”(这是正确的术语),它们都在可执行的构建目录中生成。

我要解决的问题是构建时间源于每个库都为每个可执行文件重新构建的事实。

目前我正在考虑的解决方案是以某种方式指示 CMake 使用 Bin 文件夹(或另一个文件夹)作为其自己文件夹中每个库的工作文件夹,而不是使用可执行文件,希望 gradle android 插件将足够聪明,然后发现 cmakefile 和目标文件都不需要重新生成,并避免重新生成。

我的限制是我不能重组代码库本身,并且每个可执行文件必须与其他可执行文件分开构建 - 绝对没有顶级 CMake 的可能性 - 每个可执行文件都应该能够被触发自己的。

【问题讨论】:

  • AndroidFiles 你只有在Executable1下吗?或者你有 40 个?
  • 看起来像一个依赖问题,你能检查你的目标是否明确列出了每个目标的依赖关系吗?另外,如果您可以为库和可执行文件发布CMakeLists.txt
  • AndroidFiles 我有 40 个。我可以发布 CMakeLists,但它们不会告诉你任何东西——依赖关系就是我发布的内容。这不是“依赖问题”,因为行为是预期的,这不是我想要的 - 请参阅下面 Oliv 的回答。

标签: android c++ cmake android-ndk android-gradle-plugin


【解决方案1】:

CMake 可以通过从当前构建目录读取信息来猜测构建是否是最新的。

当您在Executables/<x> 目录中手动运行CMake 时,cmake 会从与Executable/<x> 目录关联的构建目录中检索信息。然后它检查构建文件的时间戳是否对应于在此构建目录中执行的最后一次构建。如果没有,它会重建。发生的情况是:Lib1 库文件是在你构建 Executable1 之后构建的,然后你在 Executalbe2 中运行 cmake,它比较 Lib1 目标文件的时间戳,看到这个文件不是由这个实例生成的cmake构建然后重建lib。以此类推。

所以你有两个选择:

1- 构建库并将其目标文件安装在bin目录中(例如使用install cmake 命令和make install bash 命令)。然后在Executalbe<x>/CMakeLists 中使用find_library 命令而不是add_subdirectory

2- 或者您创建一个具有以下结构的超级项目:

+  supper_project
---CMakeLists.txt #add_subdirectory(LibProjects/lib<x>)... add_subdirectory(Executables/Executalbe<x>)...
  + LibProjects/
  ---Bin/ (Originally empty)
  ---Lib1/CMakeLists.txt (+sources files, same level as the CMakeLists.txt)
  ...
  ---Lib10/CMakeLists.txt (same)
  + Executables/
  ---Executable1/CMakeLists.txt (source files here)
  --------------/AndroidFiles/build.gradle (and other android project files)
  (not any more:points to the CMakeLists.txt)
  ...
  ---Executable40/CMakeLists.txt

【讨论】:

  • 不幸的是,这个答案是“对不起 - 没有答案” - 选项 1 是我想要避免的事情之一(两步构建),而选项 2 不是一个选项 - 构建是混搭,而不是单片。所有这些都足够公平。我等会儿看看有没有人想出什么,否则我就奖励这个。
  • 澄清 - 我对现实感到失望,而不是答案:)
  • @Gerasimos 你说的混搭是什么意思。你真的应该澄清你的想法和你的问题,我的回答本质上是对你在做什么的猜测。我假设您通过为每个目录执行 cmake 命令来在每个可执行目录中执行构建。但是你最后的评论让我很困惑。我当然没有理解你的意图和你实际上在做什么。
  • 我认为你已经掌握了我想要做的事情 - 很抱歉混淆了。我所说的“混合搭配”是指每个“可执行”项目都必须可以单独构建,因此绝对不可能使用“ubercmake”文件构建所有内容。这就是我的意思。
  • “两步”构建有什么问题?请注意,每个可执行文件都可以触发它需要的所有库的构建和安装。
【解决方案2】:

我设法解决了这个问题 - 但最终是通过解决而不是使用 CMake。

我删除了 CMakeFile 级别的依赖项(add_subdirectory),只将库保留在链接级别(target_link_libraries Executable [库文件])

之后,我为每个库创建了 gradle 脚本,并在每个应用程序 gradle 脚本中为这些脚本添加了依赖项,这样库的构建就会由 gradle 依赖项而不是 CMake 依赖项触发。它比可以避免使用 gradle 时要慢,但比每次都重新构建要快得多,而且开销至少是恒定的(每个项目几秒钟)。

【讨论】:

    【解决方案3】:

    我认为问题在于您定义依赖项的方式。

    对于每个可执行文件,您使用 add_subdirectory 创建单独的目标。 例如对于可执行文件 1,您有 add_subdirectory(${PROJECTS_ROOT}/LibProjects/${Library1}),对于可执行文件 2,您也有 add_subdirectory(${PROJECTS_ROOT}/LibProjects/${Library1}),因此 cmake 将在每个可执行文件的子目录中为同一个 library1 创建两个单独的目标,因此它将创建单独的时间戳和缓存文件。这就是为什么看起来它多次构建同一个库,但实际上对于 cmake 它们是不同的目标。

    要解决此问题,您可以使用 add_subdirectory 在顶级 CMakeLists.txt 中包含所有库,并在每个可执行文件的 CMakeLists.txt 中使用 add_dependencies 命令添加依赖项。

    【讨论】:

    • 我不可能有这样的“顶级”cmake - 每个“可执行文件”都是一个单独的实体,具体取决于每个“库”。
    • 您可以尝试将可执行文件上的CMAKE_BINARY_DIR 或库中的CMAKE_CURRENT_BINARY_DIR 设置为绝对路径。
    • 能否详细说明?
    猜你喜欢
    • 1970-01-01
    • 2017-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多