CMake共享项目子目录,避免重建

时间:2017-12-13 16:29:13

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

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

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

+ 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文件夹中找到每个库的构建输出,但它们不会在可执行文件中重复使用 - 没有CMake"项目文件" (这是正确的术语)在bin文件夹中,所有这些都在可执行构建目录中生成。

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

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

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

3 个答案:

答案 0 :(得分:2)

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 :(得分:0)

我认为问题在于你定义依赖关系的方式。

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

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

答案 2 :(得分:0)

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

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

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