这似乎是一个没有明确答案的常见问题。
情况是:在构建依赖于它的目标时,我们有一个第三方依赖项,我们希望在构建时安装 。这大致是:
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)
,这里需要三个探戈
当使用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
这是因为我的下载步骤具有写入该路径的权限,但基础mkdir
从add_custom_command()
运行的任何ExternalProject_Add()
命令都没有。
所以:
BUILD_BYPRODUCTS
,有没有办法简单地传达将要安装的整个目录是副产品?也就是说,/path/to/install/*
是副产品?答案 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>
。
解决您的具体问题:
- Ninja和CMake有可能吗? (版本不是问题,如果可以解决问题我可以使用最新的CMake)
醇>
是的,我确认上面提到的方法适用于使用CMake 3.11.0在macOS上进行虚拟测试项目的Ninja 1.8.2。我希望它可以与CMake 3.2或更高版本一起使用(当添加BUILD_COMMAND
选项的支持时)。
- 如果有明确列出BUILD_BYPRODUCTS的解决方法,有没有办法简单地传达将要安装的整个目录是副产品?也就是说,/ path / to / install / *是副产品吗?
醇>
不太可能。 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_package
和find_library
命令可以处理依赖项。
现在使用什么发电机并不重要。