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

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

问题描述

让我们假设我有一个项目由几个子项目A,B,C,D ...
所有子项目取决于A ,它更改频繁。
此外,还可能有一些进一步的依赖关系:在这个例子中,
D依赖于B



很多人都在做这些项目。主CMakeLists.txt文件应包括所有目录,以便构建所有目录。但是人们也希望只能在这些项目中的一个上工作,而不必每次都建立/安装所有的东西。



如果我在D上工作,我可以通过调用

  cmake --build轻松构建仅D。 --target D  -  -j7 

  ninja -j7 D 

和B如果他们的东西已经改变。完美。



但是我怎么才能调用安装只为D而不触发构建全部?
如果我打电话,我想:

  ninja -j7 D安装

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



我想保持目标所有的一切。所以EXCLUDE_FROM_ALL不会是一个选项。



所以我想到了下面的策略:




  • 除了子项目A,所有其他目标都设置为EXCLUDE_FROM_ALL,并在安装时可选。

  • 我添加一个额外的子项目, - 项目(也许我让每个目标通过使用在PARENT_SCOPE设置的一些变量发布它的名字),并且当他们想要构建和安装一切时,人们必须调用它。



它会工作吗?有没有更好的解决方案?



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



编辑



,但它不工作:在我的情况下,为目标(即使指定为可选)安装语句将使无效EXCLUDE_FROM_ALL。更好地阅读文档,我发现:

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

我也收到此警告:

 目标< targetname>已设置EXCLUDE_FROM_ALL,并且将不会默认构建,但已提供安装规则。 CMake不定义这种情况的行为。 

编辑2


$ b b

我尝试将EXCLUDE_FROM_ALL作为add_subdirectory(而不是add_library / add_executable)的一个选项,但是这些子目录中的所有install语句似乎被忽略:只有在非excluded-from-all子目录中的install语句将会被



即使我已启用 CMAKE_SKIP_INSTALL_ALL_DEPENDENCY

 设置(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true )

在我的CMakeLists.txt文件中,我省略了EXCLUDE_FROM_ALL,因为我想要可选的(在我的情况下,除了A),如果建立特定的目标在安装之前,但命令:

  ninja -j7 D&& ninja安装

由于某种原因将失败,说明C(安装设置为可选)存在(它不是因为D只依赖于A和B而创建的)...

 文件INSTALL找不到< name的dll文件的C> 



编辑4: b

它看起来像一个cmake bug给我。 (我在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}STREQUALUnspecified)

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 *) p>

  IF(CMAKE_INSTALL_DO_STRIP)

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

ENDIF(CMAKE_INSTALL_DO_STRIP)ENDIF()ENDIF(NOT CMAKE_INSTALL_COMPONENT或$ {CMAKE_INSTALL_COMPONENT}STREQUALUnspecified)


如果我手动添加OPTIONAL,它的工作原理!
(注意:我在此处编辑了* dll_name *和* path_to_dll *占位符)



编辑5: >

我确认这是cmake的错误,或至少错误的文档。我会报这个。
这种情况​​解决了更简单的问题。

  install(TARGETS $ {targetname} DESTINATION。 b  

(但在我的例子中也会安装我不想要的.lib.a文件)



或移动前面的OPTIONAL标志:

  install(TARGETS $ {targetname }可选的运行时目标文件目录。)

cmake documentation 是OPTIONAL应该作为最后一个选项。

解决方案

有什么作用:




  • 将安装目标的依赖关系移除到所有目标(一次,在主CMakeLists.txt中):

    设定(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY true)


  • 可选择安装您不想永远生成的所有目标:



    安装(TARGETS<< targetname>目的地。



    code> ninja -j7<<<> ninja -j7>>
    ,或更一般地:



    << your builder>> <<您的选项>> <<< target of targets>>



    这将构建列出的所有目标 / p>


  • 调用安装程序
    ninja install 。这将安装所有你已经构建的库,明确提到的和他们依赖的库。这解决了安装特定目标及其依赖关系的问题!


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



    ninja -j7<<目标列表>> &&& ninja install


  • 请注意,至少对于忍者来说,你不能简单地在目标列表前面加上install目标安装不再依赖任何目标和忍者在并行化作业将运行安装,而你仍在构建目标。替换旧的 ninja -j7 install



    ninja -j7&&& ; ninja install



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


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



    add_custom_target(< ;集合目标名称>>> DEPENDS<<<<< list of targets>>)



    。添加一个COMMAND也将允许创建一个与我们想要的目标一样多的安装目标,例如 ninja -j7 install_D ,但我认为现在我们超出了范围




其他注意事项:




  • 如果使用RUNTIME DESTINATION,LIBRARY DESTINATION等,最有可能是因为CMake错误,OPTIONAL关键字应该放在下面所示的位置:



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



    这与文档,我将继续将其作为一个错误报告给CMake开发人员。


  • 使用 EXCLUDE_FROM_ALL 结合 INSTALL + 可选一个坏主意


    将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:

  • 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天全站免登陆