CMake 共享项目子目录可避免重新生成

CMake shared project subdirectories avoid rebuilding

本文关键字:新生 可避免 子目录 共享 项目 CMake      更新时间:2023-10-16

对于一个项目,我正在使用带有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

库的 CMakeList 使用以下方法将其输出重定向到 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插件足够聪明,然后发现cmakefiles和目标文件都不需要重新生成, 并避免重建。

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

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

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

所以你有两个选择:

1-您可以构建库并将其目标文件安装在bin目录中(例如使用installcmake命令和make installbash命令)。然后在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

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

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

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

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

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

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