如果没有明确的答案,这似乎是一个普遍的问题。
情况是:我们有一个第三方依赖,我们希望在构建依赖它的目标时在构建时安装它。大致来说:
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
这是因为我的下载步骤有权写入该路径,但是底层
mkdir
从add_custom_command()
运行的任何ExternalProject_Add()
命令都没有。所以:
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/