使用 cmake 从复杂项目中仅安装一个目标(及其依赖项)(对更好的解决方案持开放态度) [英] Installing only one target (and its dependencies) out of a complex project with cmake (open to better solutions)

查看:39
本文介绍了使用 cmake 从复杂项目中仅安装一个目标(及其依赖项)(对更好的解决方案持开放态度)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个由几个子项目 A、B、C、D 组成的项目......所有子项目都依赖于 A,它的变化相当频繁.另外,可能还有一些进一步的依赖:在这个例子中,D 依赖于 B.

现在:很多人都在从事这些项目.主 CMakeLists.txt 文件应包含所有目录,以便构建全部构建所有内容.但是人们也希望能够只在其中一个项目上工作,而不必每次都构建/安装所有内容.

如果我正在研究 D,我可以通过调用轻松构建仅"D

cmake --build .--目标D -- -j7

ninja -j7 D

如果 A 和 B 发生了变化,这也将构建它们.完美.

但是如何在不触发全部构建的情况下仅为 D 调用安装?如果我打电话给:

ninja -j7 D 安装

它只构建了 D(和依赖项),然后只安装了 D 及其依赖项(A 和 B).相反,它构建了所有目标并安装了所有.

我想保持目标全部继续构建所有内容.所以 EXCLUDE_FROM_ALL 不是一个选项.但是朝那个方向走,我找不到任何解决方案.

所以我正在考虑以下策略:

  • 除了子项目 A,所有其他目标都设置为 EXCLUDE_FROM_ALL,并在安装时设置为 OPTIONAL.
  • 我添加了一个额外的子项目,它仅依赖于所有其他子项目(也许我通过使用在 PARENT_SCOPE 中设置的一些变量来让每个目标发布其名称),并且人们在想要构建和安装所有内容时必须调用它.

它会起作用吗?有没有更好的解决办法?

我们希望避免每个人都必须编辑主 CMakeLists.txt 文件以排除他不感兴趣的项目.该解决方案应该可移植到不同的操作系统.

我尝试了我提出的策略,但没有奏效:就我而言,为目标放置安装语句(即使指定为 OPTIONAL)将使 EXCLUDE_FROM_ALL 无效.在文档中更好地阅读我发现:

安装 EXCLUDE_FROM_ALL 设置为 true 的目标具有未定义的行为.

我也收到此警告:

目标<目标名称>已设置 EXCLUDE_FROM_ALL 并且默认情况下不会构建,但已为其提供安装规则.CMake 没有定义这种情况下的行为.

编辑 2:

我尝试将 EXCLUDE_FROM_ALL 作为 add_subdirectory 的一个选项(而不是 add_library/add_executable),但是这些子目录中的所有安装语句似乎都被忽略了:只会安装非排除所有子目录中的安装语句.

编辑 3:

即使我激活了CMAKE_SKIP_INSTALL_ALL_DEPENDENCY:

set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY 真)

在主 CMakeLists.txt 文件中,我省略了所有 EXCLUDE_FROM_ALL,将安装尽可能多的目标设为可选(在我的情况下,除了 A 之外的所有目标),如果在安装之前构建特定目标,则命令:

ninja -j7 D&&忍者安装

由于某种原因会失败,说明 C(其安装设置为 OPTIONAL)不存在(它没有被创建,因为 D 仅依赖于 A 和 B)...

file INSTALL 找不到"

编辑 4:

对我来说这看起来像是一个 cmake 错误.(我在windows下用的是2.8.11,也测试了2.8.10)这个 INSTALL 命令

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

在 cmake_install.cmake 中被转换为:

<块引用>

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

文件(安装目的地${CMAKE_INSTALL_PREFIX}/."类型共享库文件*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(不是 CMAKE_INSTALL_COMPONENT 或${CMAKE_INSTALL_COMPONENT}"STREQUAL未指定")

缺少命令 FILE OPTIONAL!如果我手动添加 OPTIONAL,它会起作用!(注意:我在这里编辑了 *dll_name* 和 *path_to_dll* 占位符)

编辑 5:

我确认这是 cmake 的错误,或者至少是错误的文档.我会报告这个.情况解决了要么放一个更简单的

install(TARGETS ${targetname} DESTINATION . OPTIONAL)

(但在我的情况下,这也会安装我不想要的 .lib.a 文件)

或者在可选标志前面移动:

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

cmake 文档 中可以理解的是,OPTIONAL 应该被列为最后一个选项.

解决方案

什么有效:

  • 移除install"目标对all"目标的依赖(一次,在主 CMakeLists.txt 中):

    set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true)

  • 将您不想总是构建的所有目标设置为可选安装:

    install(TARGETS <> DESTINATION . OPTIONAL)

  • 构建要安装的目标

    ninja -j7 <<目标列表>>,或更一般地说:

    <代码><<你的建造者>><<你的选择>><<目标列表>>

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

  • 调用安装程序忍者安装.这将安装您构建的所有库、您明确提到的库以及它们所依赖的库.这解决了安装特定目标及其依赖项的问题!

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

    ninja -j7 <<目标列表>>&&忍者安装

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

    ninja -j7 &&忍者安装.

    目标all"仍然可用(它仍然是默认目标).

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

    add_custom_target(<<集体目标名称>>取决于<<目标列表>>)

    这将不会包含在目标全部中.还添加一个 COMMAND 还可以为我们想要的尽可能多的目标创建一个安装目标,例如 ninja -j7 install_D,但我认为现在我们超出了这个问题的范围.p>

进一步的考虑:

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

    install(TARGETS <<targetname>> OPTIONAL RUNTIME DESTINATION <<some dir>> LIBRARY DESTINATION <<<一些(其他)dir>>)

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

  • EXCLUDE_FROM_ALLINSTALL + OPTIONAL 结合使用是 一个坏主意

    <块引用>

    安装 EXCLUDE_FROM_ALL 设置为 true 的目标具有未定义的行为.

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

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.

解决方案

What works:

  • Remove dependency of "install" target to "all" target (once, in the main CMakeLists.txt):

    set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true)

  • Set to OPTIONAL installation of all targets you do not want to always build:

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

  • Build the targets you want to install

    ninja -j7 <<list of targets>>, or more generally:

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

    This will build all the targets listed and their dependencies

  • Call the installer ninja install. This will install all the libraries you have built, those you mentioned explicitly and those to which they depended. This solves the problem of installing a specific target together with its dependencies!

  • To concatenate the commands, both on Unix and Windows you can use

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

  • Note that, at least for ninja, you cannot simply prepend "install" to the list of targets, as the target "install" does not depend anymore on any target and ninja in parallelizing the job will run install while you are still building your targets. A replacement to the old ninja -j7 install is

    ninja -j7 && ninja install.

    The target "all" is still available (and it is still the default target).

  • If you need to create a list of targets you want to build together, you can define a custom target:

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

    This will not be included in the target all. Adding also a COMMAND would also allow to create an install target for as many as target as we want, for example ninja -j7 install_D, but I think now we are beyond the scope of this question.

Further considerations:

  • (Note, July 2018: This might be outdated) If you use RUNTIME DESTINATION, LIBRARY DESTINATION etc., most likely because of a CMake bug the OPTIONAL keyword should be put exactly in the position indicated below:

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

    This contradicts what written in the documentation, and I will proceed to report it as a bug to the CMake developers.

  • Using EXCLUDE_FROM_ALL in combination with INSTALL + OPTIONAL is a bad idea

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

    (and cmake tries to warn you when it parses the code)

这篇关于使用 cmake 从复杂项目中仅安装一个目标(及其依赖项)(对更好的解决方案持开放态度)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆