CMake:如何构建外部项目并包含目标。

时间:2023-01-13 16:08:58

I have a Project A that exports a static library as a target:

我有一个项目a将一个静态库导出为目标:

install(TARGETS alib DESTINATION lib EXPORT project_a-targets)
install(EXPORT project_a-targets DESTINATION lib/alib)

Now I want to use Project A as an external project from Project B and include its built targets:

现在我想把A项目作为项目B的一个外部项目,包括它的建造目标:

ExternalProject_Add(project_a
  URL ...project_a.tar.gz
  PREFIX ${CMAKE_CURRENT_BINARY_DIR}/project_a
  CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
)

include(${CMAKE_CURRENT_BINARY_DIR}/lib/project_a/project_a-targets.cmake)

The problem is that the include file does not exist yet when CMakeLists of Project B is run.

问题是,当项目B的CMakeLists运行时,include文件还不存在。

Is there a way to make the include dependent on the external project being built?

是否有一种方法可以使include依赖于正在构建的外部项目?

3 个解决方案

#1


45  

I think you're mixing up two different paradigms here.

我认为你在这里混淆了两种不同的模式。

As you noted, the highly flexible ExternalProject module runs its commands at build time, so you can't make direct use of Project A's import file since it's only created once Project A has been installed.

正如您所指出的,高度灵活的ExternalProject模块在构建时运行它的命令,因此您不能直接使用项目A的导入文件,因为它只在安装了项目A之后才创建。

If you want to include Project A's import file, you'll have to install Project A manually before invoking Project B's CMakeLists.txt - just like any other third-party dependency added this way or via find_file / find_library / find_package.

如果您想要包含项目A的导入文件,那么在调用项目B的CMakeLists之前,您必须手动安装Project A。txt -就像任何其他第三方依赖添加了这种方式或通过find_file / find_library / find_package。

If you want to make use of ExternalProject_Add, you'll need to add something like the following to your CMakeLists.txt:

如果您想要使用ExternalProject_Add,您需要向CMakeLists.txt添加如下内容:

ExternalProject_Add(project_a
  URL ...project_a.tar.gz
  PREFIX ${CMAKE_CURRENT_BINARY_DIR}/project_a
  CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
)


  
  
  
    include(${CMAKE_CURRENT_BINARY_DIR}/lib/project_a/project_a-targets.cmake) 
  

ExternalProject_Get_Property(project_a install_dir)
include_directories(${install_dir}/include)

add_dependencies(project_b_exe project_a)
target_link_libraries(project_b_exe ${install_dir}/lib/alib.lib)

#2


8  

This post has a reasonable answer:

这篇文章有一个合理的答案:

CMakeLists.txt.in:

CMakeLists.txt.in:

cmake_minimum_required(VERSION 2.8.2)

project(googletest-download NONE)

include(ExternalProject)
ExternalProject_Add(googletest
  GIT_REPOSITORY    https://github.com/google/googletest.git
  GIT_TAG           master
  SOURCE_DIR        "${CMAKE_BINARY_DIR}/googletest-src"
  BINARY_DIR        "${CMAKE_BINARY_DIR}/googletest-build"
  CONFIGURE_COMMAND ""
  BUILD_COMMAND     ""
  INSTALL_COMMAND   ""
  TEST_COMMAND      ""
)

CMakeLists.txt:

CMakeLists.txt:

# Download and unpack googletest at configure time
configure_file(CMakeLists.txt.in
               googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
execute_process(COMMAND ${CMAKE_COMMAND} --build .
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )

# Prevent GoogleTest from overriding our compiler/linker options
# when building with Visual Studio
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)

# Add googletest directly to our build. This adds
# the following targets: gtest, gtest_main, gmock
# and gmock_main
add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
                 ${CMAKE_BINARY_DIR}/googletest-build)

# The gtest/gmock targets carry header search path
# dependencies automatically when using CMake 2.8.11 or
# later. Otherwise we have to add them here ourselves.
if (CMAKE_VERSION VERSION_LESS 2.8.11)
  include_directories("${gtest_SOURCE_DIR}/include"
                      "${gmock_SOURCE_DIR}/include")
endif()

# Now simply link your own targets against gtest, gmock,
# etc. as appropriate

However it does seem quite hacky. I'd like to propose an alternative solution - use Git submodules.

然而,这看起来确实很粗糙。我想提出一个替代解决方案——使用Git子模块。

cd MyProject/dependencies/gtest
git submodule add https://github.com/google/googletest.git
cd googletest
git checkout release-1.8.0
cd ../../..
git add *
git commit -m "Add googletest"

Then in MyProject/dependencies/gtest/CMakeList.txt you can do something like:

然后在MyProject / / gt / CMakeList的依赖性。你可以这样做:

cmake_minimum_required(VERSION 3.3)

if(TARGET gtest) # To avoid diamond dependencies; may not be necessary depending on you project.
    return()
endif()

add_subdirectory("googletest")

I haven't tried this extensively yet but it seems cleaner.

我还没有尝试过,但它看起来更干净。

Edit: There is a downside to this approach: The subdirectory might run install() commands that you don't want. This post has an approach to disable them but it was buggy and didn't work for me.

编辑:这种方法有一个缺点:子目录可能运行您不想要的安装()命令。这篇文章有一种方法可以禁用它们,但它是错误的,对我不起作用。

Edit 2: If you use add_subdirectory("googletest" EXCLUDE_FROM_ALL) it seems means the install() commands in the subdirectory aren't used by default.

编辑2:如果你使用add_subdirectory(“googletest”除外),它似乎意味着在子目录下的install()命令不会被默认使用。

#3


4  

You can also force the build of the dependent target in a secondary make process

您还可以在第二个make过程中强制构建依赖目标。

See my answer on a related topic.

请参阅我关于相关主题的答案。

#1


45  

I think you're mixing up two different paradigms here.

我认为你在这里混淆了两种不同的模式。

As you noted, the highly flexible ExternalProject module runs its commands at build time, so you can't make direct use of Project A's import file since it's only created once Project A has been installed.

正如您所指出的,高度灵活的ExternalProject模块在构建时运行它的命令,因此您不能直接使用项目A的导入文件,因为它只在安装了项目A之后才创建。

If you want to include Project A's import file, you'll have to install Project A manually before invoking Project B's CMakeLists.txt - just like any other third-party dependency added this way or via find_file / find_library / find_package.

如果您想要包含项目A的导入文件,那么在调用项目B的CMakeLists之前,您必须手动安装Project A。txt -就像任何其他第三方依赖添加了这种方式或通过find_file / find_library / find_package。

If you want to make use of ExternalProject_Add, you'll need to add something like the following to your CMakeLists.txt:

如果您想要使用ExternalProject_Add,您需要向CMakeLists.txt添加如下内容:

ExternalProject_Add(project_a
  URL ...project_a.tar.gz
  PREFIX ${CMAKE_CURRENT_BINARY_DIR}/project_a
  CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
)


  
  
  
    include(${CMAKE_CURRENT_BINARY_DIR}/lib/project_a/project_a-targets.cmake) 
  

ExternalProject_Get_Property(project_a install_dir)
include_directories(${install_dir}/include)

add_dependencies(project_b_exe project_a)
target_link_libraries(project_b_exe ${install_dir}/lib/alib.lib)

#2


8  

This post has a reasonable answer:

这篇文章有一个合理的答案:

CMakeLists.txt.in:

CMakeLists.txt.in:

cmake_minimum_required(VERSION 2.8.2)

project(googletest-download NONE)

include(ExternalProject)
ExternalProject_Add(googletest
  GIT_REPOSITORY    https://github.com/google/googletest.git
  GIT_TAG           master
  SOURCE_DIR        "${CMAKE_BINARY_DIR}/googletest-src"
  BINARY_DIR        "${CMAKE_BINARY_DIR}/googletest-build"
  CONFIGURE_COMMAND ""
  BUILD_COMMAND     ""
  INSTALL_COMMAND   ""
  TEST_COMMAND      ""
)

CMakeLists.txt:

CMakeLists.txt:

# Download and unpack googletest at configure time
configure_file(CMakeLists.txt.in
               googletest-download/CMakeLists.txt)
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )
execute_process(COMMAND ${CMAKE_COMMAND} --build .
  WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-download )

# Prevent GoogleTest from overriding our compiler/linker options
# when building with Visual Studio
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)

# Add googletest directly to our build. This adds
# the following targets: gtest, gtest_main, gmock
# and gmock_main
add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src
                 ${CMAKE_BINARY_DIR}/googletest-build)

# The gtest/gmock targets carry header search path
# dependencies automatically when using CMake 2.8.11 or
# later. Otherwise we have to add them here ourselves.
if (CMAKE_VERSION VERSION_LESS 2.8.11)
  include_directories("${gtest_SOURCE_DIR}/include"
                      "${gmock_SOURCE_DIR}/include")
endif()

# Now simply link your own targets against gtest, gmock,
# etc. as appropriate

However it does seem quite hacky. I'd like to propose an alternative solution - use Git submodules.

然而,这看起来确实很粗糙。我想提出一个替代解决方案——使用Git子模块。

cd MyProject/dependencies/gtest
git submodule add https://github.com/google/googletest.git
cd googletest
git checkout release-1.8.0
cd ../../..
git add *
git commit -m "Add googletest"

Then in MyProject/dependencies/gtest/CMakeList.txt you can do something like:

然后在MyProject / / gt / CMakeList的依赖性。你可以这样做:

cmake_minimum_required(VERSION 3.3)

if(TARGET gtest) # To avoid diamond dependencies; may not be necessary depending on you project.
    return()
endif()

add_subdirectory("googletest")

I haven't tried this extensively yet but it seems cleaner.

我还没有尝试过,但它看起来更干净。

Edit: There is a downside to this approach: The subdirectory might run install() commands that you don't want. This post has an approach to disable them but it was buggy and didn't work for me.

编辑:这种方法有一个缺点:子目录可能运行您不想要的安装()命令。这篇文章有一种方法可以禁用它们,但它是错误的,对我不起作用。

Edit 2: If you use add_subdirectory("googletest" EXCLUDE_FROM_ALL) it seems means the install() commands in the subdirectory aren't used by default.

编辑2:如果你使用add_subdirectory(“googletest”除外),它似乎意味着在子目录下的install()命令不会被默认使用。

#3


4  

You can also force the build of the dependent target in a secondary make process

您还可以在第二个make过程中强制构建依赖目标。

See my answer on a related topic.

请参阅我关于相关主题的答案。