24

Let's say I have a project made of several subprojects A, B, C, D... All subprojects depends on A, which changes rather frequently. Plus, there might be some further dependencies: in this example, D depends on B.

Now: many people are working on these projects. The main CMakeLists.txt file should include all directories, so that the build all builds everything. But people would like also to be able to work only on one of these projects, and not having to build/install everything everytime.

If I am working on D, I can easily build "only" D by calling

cmake --build . --target D -- -j7

or

ninja -j7 D

This will also build A and B if something for them has changed. Perfect.

But how can I call install only for D without triggering build all? I would like that if I call:

ninja -j7 D install

it only built D (and dependencies) and then installed only D and its dependencies (A and B). Instead, it builds the target all and install all.

I would like to keep that the target all keep building everything. So EXCLUDE_FROM_ALL wouldn't be an option. But going in that direction I couldn't find any solution.

So I am thinking of the following strategy:

  • Apart from subproject A, all other targets are set to EXCLUDE_FROM_ALL, and OPTIONAL at installation.
  • I add one extra subproject that simply depends from all other sub-projects (maybe I make each target publish its name by using some variable set at PARENT_SCOPE), and people will have to call that when they want to build and install everything.

Is it going to work? Is there any better solution?

We would like to avoid that everybody has to edit the main CMakeLists.txt file to exclude projects he is not interested in. The solution should be portable to different OSs.

Edit:

I tried the strategy I proposed, but it didn't work: in my case, putting an install statement for a target (even if specified as OPTIONAL) will make ineffective EXCLUDE_FROM_ALL. Reading better in the documentation I found out that:

Installing a target with EXCLUDE_FROM_ALL set to true has undefined behavior.

I also get this warning:

Target <targetname> has EXCLUDE_FROM_ALL set and will not be built by default but an install rule has been provided for it.  CMake does not define behavior for this case.

Edit 2:

I tried putting EXCLUDE_FROM_ALL as an option of add_subdirectory (instead of add_library/add_executable), but then all the install statements in those sub-directory seem to be ignored: only install statements in non excluded-from-all subdirectories will be installed.

Edit 3:

Even if I activate CMAKE_SKIP_INSTALL_ALL_DEPENDENCY:

set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true)

in the main CMakeLists.txt file, and I omit all EXCLUDE_FROM_ALL, put installation of as many targets as I want optional (in my case, all but A), and if building of specific targets precede installation, yet the command:

ninja -j7 D && ninja install

for some reason will fail, stating that C (whose installation was set to OPTIONAL) does not exist (it was not created because D depended only on A and B)...

file INSTALL cannot find "<name of dll file for C>"

Edit 4:

It looks like a cmake bug to me. (I am using 2.8.11 under Windows, also tested 2.8.10) This INSTALL command

install(TARGETS ${targetname} RUNTIME DESTINATION . LIBRARY DESTINATION . OPTIONAL)

is converted in the cmake_install.cmake as:

IF(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" STREQUAL "Unspecified")   

FILE(INSTALL DESTINATION "${CMAKE_INSTALL_PREFIX}/." TYPE SHARED_LIBRARY FILES *path_to_dll*)

IF(EXISTS "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/./" AND NOT IS_SYMLINK "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/./*dll_name*")

IF(CMAKE_INSTALL_DO_STRIP)

  EXECUTE_PROCESS(COMMAND "C:/Programs/MinGW/bin/strip.exe" "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/./*dll_name*")

ENDIF(CMAKE_INSTALL_DO_STRIP)   ENDIF() ENDIF(NOT CMAKE_INSTALL_COMPONENT OR "${CMAKE_INSTALL_COMPONENT}" STREQUAL "Unspecified")

with the command FILE missing OPTIONAL! If I add OPTIONAL manually, it works! (note: I have edited here to put *dll_name* and *path_to_dll* placeholders)

Edit 5:

I confirm it's a bug of cmake, or at least wrong documentation. I will report this. The situation solved either putting a more simple

install(TARGETS ${targetname} DESTINATION . OPTIONAL)

(but this in my case will also install .lib.a files that I don't want)

or moving in front the OPTIONAL flag:

install(TARGETS ${targetname} OPTIONAL RUNTIME DESTINATION . LIBRARY DESTINATION .)

What one understands from the cmake documentation is that OPTIONAL should be put as last option.

4

4 回答 4

24

什么有效:

  • 删除“安装”目标对“全部”目标的依赖(一次,在主 CMakeLists.txt 中):

    set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true)

  • 设置为您不想始终构建的所有目标的可选安装:

    install(TARGETS <<targetname>> DESTINATION . OPTIONAL)

  • 构建要安装的目标

    ninja -j7 <<list of targets>>,或更一般地说:

    <<your builder>> <<your options>> <<list of targets>>

    这将构建列出的所有目标及其依赖项

  • 致电安装人员 ninja install。这将安装您构建的所有库,您明确提到的库以及它们所依赖的库。这解决了安装特定目标及其依赖项的问题!

  • 要在 Unix 和 Windows 上连接命令,您可以使用

    ninja -j7 <<list of targets>> && ninja install

  • 请注意,至少对于 ninja,您不能简单地将“install”添加到目标列表中,因为目标“install”不再依赖于任何目标,并且 ninja 在并行化作业时将在您仍在构建目标时运行 install . 旧的替代品ninja -j7 install

    ninja -j7 && ninja install.

    目标“all”仍然可用(它仍然是默认目标)。

  • 如果您需要创建要一起构建的目标列表,可以定义自定义目标

    add_custom_target(<<collective target name>> DEPENDS <<list of targets>>)

    这不会包含在目标中的所有内容中。还添加一个 COMMAND 还允许为我们想要的任意多个目标创建安装目标,例如ninja -j7 install_D,但我认为现在我们超出了这个问题的范围。

进一步的考虑:

  • (注意,2018 年 7 月:这可能已过时)如果您使用 RUNTIME DESTINATION、LIBRARY DESTINATION 等,很可能是由于 CMake 错误,应将 OPTIONAL 关键字准确放置在如下所示的位置:

    install(TARGETS <<targetname>> OPTIONAL RUNTIME DESTINATION <<some dir>> LIBRARY DESTINATION <<some (other) dir>>)

    这与文档中的内容相矛盾,我将继续将其作为错误报告给 CMake 开发人员。

  • 与+EXCLUDE_FROM_ALL结合使用是个坏主意INSTALLOPTIONAL

    安装一个将 EXCLUDE_FROM_ALL 设置为 true 的目标具有未定义的行为。

    (并且 cmake 在解析代码时会尝试警告您)

于 2013-06-19T21:08:10.290 回答
7

正如我在这里回答的那样,最新版本的 Ninja 提供了仅在子目录中构建目标的选项。

相同的功能适用于安装。因此,如果您的问题中的目标D位于子目录D中,您可以运行以下命令:

ninja D/install

在此处的忍者生成器的 CMake 文档中查找更多信息。

于 2017-07-16T12:17:39.000 回答
5

如果您曾经add_subdirectory将子项目添加到 CMakeLists.txt,您会看到,在您的构建目录中,您有所有子项目的子目录。

如果您只想从 D 安装目标,只需执行以下操作:

cd build-dir/D
make install

这只会安装 D-targets。

当然,您必须先在 build-dir 中构建您的项目。我正在使用 gnu-make 以这种方式使用它。我不知道它是否适用于忍者。

于 2013-06-18T09:09:15.023 回答
3

正如 unapiedra 所提到的,由 cmake 生成的 ninja 构建文件包括用于构建/测试/安装/打包特定目录中的内容的规则。

这很好,但你不能这样做:

ninja <targetName>/install

你只能做

ninja path/where/targetName/is/install

如果您不知道targetName在哪里,您可以使用:

ninja -t query <targetName>

查看输出。

于 2017-12-18T10:47:39.313 回答