如果没有明确的答案,这似乎是一个普遍的问题。

情况是:我们有一个第三方依赖,我们希望在构建依赖它的目标时在构建时安装它。大致来说:

ExternalProject_Add(target-ep
    DOWNLOAD_COMMAND <whatever>
    BUILD_COMMAND ""
    INSTALL_COMMAND ""
    CONFIGURE_COMMAND "")

add_library(target-imp STATIC IMPORTED)
set_target_properties(target-imp PROPERTIES
    INTERFACE_INCLUDE_DIRECTORIES /path/to/install/include
    IMPORTED_LOCATION /path/to/install/lib/libwhatever.a)

add_library(target INTERFACE)
target_link_libraries(target INTERFACE target-imp)
add_dependencies(target target-ep)

(由于cmake issue 15052,这里的探戈需要三个人)

当使用Unix Makefiles作为生成器时,这很好用。仅按需安装依赖项,所有构建均可正常运行。

但是,在Ninja上,此操作立即失败,并显示以下内容:
ninja: error: '/path/to/install/lib/libwhatever.a', needed by 'something', missing and no known rule to make it

这是因为Ninja扫描依赖关系的方式与Make有所不同(请参阅ninja issue 760)。因此,我们要做的实际上是告诉Ninja该外部依赖关系存在。我们能做到这一点:
ExternalProject_Add(target-ep
    DOWNLOAD_COMMAND <whatever>
    BUILD_BYPRODUCTS /path/to/install/lib/libwhatever.a
    BUILD_COMMAND ""
    INSTALL_COMMAND ""
    CONFIGURE_COMMAND "")

不幸的是,它也失败了:
No build step for 'target-ep'ninja: error: mkdir(/path/to/install): Permission denied

这是因为我的下载步骤有权写入该路径,但是底层mkdiradd_custom_command()运行的任何ExternalProject_Add()命令都没有。

所以:
  • Ninja和CMake完全有可能吗? (版本不是问题,如果可以解决问题,我可以使用最新的CMake)
  • 如果可以通过某种方式显式列出BUILD_BYPRODUCTS来解决,是否有一种方法可以简单地传达将要安装的整个目录是副产品?也就是说,/path/to/install/*是副产品吗?
  • 最佳答案

    mkdir的隐藏ExternalProject步骤(所有其他步骤直接或间接依赖于此步骤)始终尝试创建完整的目录集,即使它们不会被使用。您可以看到此here。作为引用,它执行以下操作:

    ExternalProject_Add_Step(${name} mkdir
      COMMENT "Creating directories for '${name}'"
      COMMAND ${CMAKE_COMMAND} -E make_directory ${source_dir}
      COMMAND ${CMAKE_COMMAND} -E make_directory ${binary_dir}
      COMMAND ${CMAKE_COMMAND} -E make_directory ${install_dir}
      COMMAND ${CMAKE_COMMAND} -E make_directory ${tmp_dir}
      COMMAND ${CMAKE_COMMAND} -E make_directory ${stamp_dir}${cfgdir}
      COMMAND ${CMAKE_COMMAND} -E make_directory ${download_dir}
      COMMAND ${CMAKE_COMMAND} -E make_directory ${log_dir}   # This one only since CMake 3.13
      )
    

    Unix系统上的默认安装位置可能是/usr/local,因此,如果您没有尝试访问的所有目录的写入权限,则可能与您的问题有关。我建议您检查每个位置的权限,并确保它们已经存在或可写。另外,您可以指定一个在构建树本地的安装目录,以便即使不使用它,也至少可以始终创建它(请参见下面的示例)。

    如果您使用Ninja,则它的依赖性检查将比make更加严格。您需要target-ep进行提供libwhatever.a的下载,因此您确实需要BUILD_BYPRODUCTS来告知Ninja target-ep是创建该文件的源。如您所知,如果不这样做,target-imp将指向最初不存在的库,Ninja会正确地提示它丢失了,并且不知道如何创建它。如果提供BUILD_BYPRODUCTS,则有意义的是构建步骤不应为空,因此,即使它只是一个BUILD_COMMAND实际上并没有做任何有意义的事情,您也可能需要在构建步骤中做一些事情。

    以下对target-ep的修改后的定义有望使您满意:
    ExternalProject_Add(target-ep
        INSTALL_DIR ${CMAKE_CURRENT_BUILD_DIR}/dummyInstall
        DOWNLOAD_COMMAND <whatever>
        BUILD_BYPRODUCTS /path/to/install/lib/libwhatever.a
        BUILD_COMMAND ${CMAKE_COMMAND} -E echo_append
        INSTALL_COMMAND ""
        CONFIGURE_COMMAND "")
    

    您最初的问题还导致对错误目标的依赖。 target-imp应该取决于target-ep,但是您让target取决于target-ep。正确的依赖关系可以这样表示:
     add_dependencies(target-imp target-ep)
    

    使用BUILD_BYPRODUCTS选项,Ninja已经知道上面的依赖关系,但是其他生成器(包括make)都需要它。

    您尚未指定<whatever>下载命令的功能,但我假设它负责确保执行后该库将存在于/path/to/install/lib/libwhatever.a处。您还可以尝试将DOWNLOAD_COMMAND设置为空,然后将<whatever>替换为BUILD_COMMAND

    要解决您的特定问题:



    是的,我验证了上述方法可与Ninja 1.8.2一起使用CMake 3.11.0在macOS上进行虚拟测试项目。我希望它可以与CMake 3.2或更高版本一起使用(那是在添加对BUILD_BYPRODUCTS选项的支持时)。



    不太可能。忍者将如何知道这样的目录中应该包含什么内容?获得可靠依赖关系的唯一方法是显式列出预期存在的每个文件,在这种情况下,您可以使用BUILD_BYPRODUCTS进行操作。

    关于cmake - 在Ninja中使用ExternalProject下载步骤,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/50400592/

    10-09 09:35