使用忍者的外部项目下载步骤

时间:2018-05-17 21:13:41

标签: cmake ninja

这似乎是一个没有明确答案的常见问题。

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

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 Makefile作为生成器时,这非常有用。只根据需要安装依赖项,所有构建都能正常工作。

然而,在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()命令都没有。

所以:

  1. Ninja和CMake是否可以 ? (版本不是问题,如果可以解决问题我可以使用最新的CMake)
  2. 如果有一些方法可以明确列出BUILD_BYPRODUCTS,有没有办法简单地传达将要安装的整个目录是副产品?也就是说,/path/to/install/*是副产品?

2 个答案:

答案 0 :(得分:2)

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}
  )

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

如果你使用Ninja,它的依赖性检查将比make更严格。您target-ep正在进行提供libwhatever.a的下载,因此您需要BUILD_BYPRODUCTS告诉Ninja target-ep是创建该文件的原因。正如您所知,如果您不知道,那么target-imp会指向一个最初不存在的图书馆,Ninja正确地抱怨它已经丢失而且它没有知道如何创建它。如果您提供BUILD_BYPRODUCTS,那么构建步骤不应为空是有意义的,因此您可能需要执行某些操作作为构建步骤,即使它只是BUILD_COMMAND而不是#&# 39;实际上做了一些有意义的事。

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>下载命令的功能,但我假设它负责确保库在执行时{1}}存在。您也可以尝试将/path/to/install/lib/libwhatever.a设为空,并将DOWNLOAD_COMMAND改为<whatever>

解决您的具体问题:

  
      
  1. Ninja和CMake有可能吗? (版本不是问题,如果可以解决问题我可以使用最新的CMake)
  2.   

是的,我确认上面提到的方法适用于使用CMake 3.11.0在macOS上进行虚拟测试项目的Ninja 1.8.2。我希望它可以与CMake 3.2或更高版本一起使用(当添加BUILD_COMMAND选项的支持时)。

  
      
  1. 如果有明确列出BUILD_BYPRODUCTS的解决方法,有没有办法简单地传达将要安装的整个目录是副产品?也就是说,/ path / to / install / *是副产品吗?
  2.   

不太可能。 Ninja如何知道在这样的目录中会有什么?获得可靠依赖关系的唯一方法是明确列出预期存在的每个文件,在您的情况下使用BUILD_BYPRODUCTS

答案 1 :(得分:0)

如果您愿意在配置时下载,可以按照post进行操作。它使用google-test作为示例,但我对其他依赖项使用了相同的技术。只需将您的ExternalProject代码放在单独的文件中,例如“CMakeLists.txt.dependencies”,然后使用execute_process启动另一个cmake。我首先使用configure_file将配置信息注入外部项目并将其复制到构建树中。

configure_file(CMakeLists.txt.dependency.in dependency/CMakeLists.txt)
execute_process(COMMAND "${CMAKE_COMMAND}" -G "${CMAKE_GENERATOR}" .
        WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/dependency" )
execute_process(COMMAND "${CMAKE_COMMAND}" --build .
        WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/dependency" )

我在配置时执行此操作,因此find_packagefind_library命令可以处理依赖项。

现在使用什么发电机并不重要。