CMake:如何构建外部项目并包括其目标

CMake:如何构建外部项目并包括其目标,cmake,external-project,Cmake,External Project,我有一个项目a,它将静态库导出为目标: install(TARGETS alib DESTINATION lib EXPORT project_a-targets) install(EXPORT project_a-targets DESTINATION lib/alib) 现在,我想将项目A用作项目B的外部项目,并包括其构建目标: ExternalProject_Add(project_a URL ...project_a.tar.gz PREFIX ${CMAKE_CURRENT_

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

install(TARGETS alib DESTINATION lib EXPORT project_a-targets)
install(EXPORT project_a-targets DESTINATION lib/alib)
现在,我想将项目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)
ExternalProject\u Add(项目a
URL…project_a.tar.gz
前缀${CMAKE\u CURRENT\u BINARY\u DIR}/project\u a
CMAKE_ARGS-DCMAKE_INSTALL_前缀:PATH=
)
包括(${CMAKE\u CURRENT\u BINARY\u DIR}/lib/project\u a/project\u a-targets.CMAKE)
问题是,在运行项目B的CmakeList时,包含文件还不存在

有没有办法使include依赖于正在构建的外部项目

更新
我根据这一点和我遇到的其他常见问题写了一篇短文。

我想你在这里混淆了两种不同的范例

正如您所注意到的,高度灵活的模块在构建时运行其命令,因此您不能直接使用Project A的导入文件,因为它只在安装Project A后创建

如果您想
包含
项目A的导入文件,则必须在调用项目B的CMakeLists.txt之前手动安装项目A-就像以这种方式或通过
查找文件
/
查找库
/
查找包
添加的任何其他第三方依赖项一样

如果要使用
ExternalProject\u 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)
ExternalProject\u Add(项目a
URL…project_a.tar.gz
前缀${CMAKE\u CURRENT\u BINARY\u DIR}/project\u a
CMAKE_ARGS-DCMAKE_INSTALL_前缀:PATH=
)
包括(${CMAKE\u CURRENT\u BINARY\u DIR}/lib/project\u a/project\u a-targets.CMAKE)
外部项目获取属性(项目安装目录)
包含目录(${install\u dir}/include)
添加依赖项(项目b项目a)

target_link_库(project_exe${install_dir}/lib/alib.lib)
Edit:CMake现在已经内置了对此的支持。看哪一个使用

还可以在辅助生成过程中强制生成从属目标

请参阅相关主题。

有一个合理的答案:

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

# 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
然而,它看起来确实很粗糙。我想提出另一种解决方案——使用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"
然后在
MyProject/dependencies/gtest/CMakeList.txt
中,您可以执行以下操作:

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")
我还没有试过,但是看起来更干净

编辑:这种方法有一个缺点:子目录可能运行您不需要的
install()
命令。但那是辆马车,对我不起作用


编辑2:如果使用
add\u子目录(“googletest”EXCLUDE\u FROM\u ALL)
似乎意味着默认情况下不使用子目录中的
install()
命令。

cmake的
ExternalProject\u add确实可以使用,但我不喜欢它——它在构建、连续轮询等过程中执行某些操作。。。我更喜欢在构建阶段构建项目,而不是其他阶段。我多次尝试覆盖
ExternalProject\u Add
,但不幸没有成功

然后,我还尝试添加git子模块,但这会拖拽整个git存储库,而在某些情况下,我只需要整个git存储库的子集。我已经检查过了——确实可以执行稀疏git签出,但这需要单独的函数,我在下面写到

#-----------------------------------------------------------------------------
#
# Performs sparse (partial) git checkout
#
#   into ${checkoutDir} from ${url} of ${branch}
#
# List of folders and files to pull can be specified after that.
#-----------------------------------------------------------------------------
function (SparseGitCheckout checkoutDir url branch)
    if(EXISTS ${checkoutDir})
        return()
    endif()

    message("-------------------------------------------------------------------")
    message("sparse git checkout to ${checkoutDir}...")
    message("-------------------------------------------------------------------")

    file(MAKE_DIRECTORY ${checkoutDir})

    set(cmds "git init")
    set(cmds ${cmds} "git remote add -f origin --no-tags -t master ${url}")
    set(cmds ${cmds} "git config core.sparseCheckout true")

    # This command is executed via file WRITE
    # echo <file or folder> >> .git/info/sparse-checkout")

    set(cmds ${cmds} "git pull --depth=1 origin ${branch}")

    # message("In directory: ${checkoutDir}")

    foreach( cmd ${cmds})
        message("- ${cmd}")
        string(REPLACE " " ";" cmdList ${cmd})

        #message("Outfile: ${outFile}")
        #message("Final command: ${cmdList}")

        if(pull IN_LIST cmdList)
            string (REPLACE ";" "\n" FILES "${ARGN}")
            file(WRITE ${checkoutDir}/.git/info/sparse-checkout ${FILES} )
        endif()

        execute_process(
            COMMAND ${cmdList}
            WORKING_DIRECTORY ${checkoutDir}
            RESULT_VARIABLE ret
        )

        if(NOT ret EQUAL "0")
            message("error: previous command failed, see explanation above")
            file(REMOVE_RECURSE ${checkoutDir})
            break()
        endif()
    endforeach()

endfunction()


SparseGitCheckout(${CMAKE_BINARY_DIR}/catch_197 https://github.com/catchorg/Catch2.git v1.9.7 single_include)
SparseGitCheckout(${CMAKE_BINARY_DIR}/catch_master https://github.com/catchorg/Catch2.git master single_include)
#-----------------------------------------------------------------------------
#
#执行稀疏(部分)git签出
#
#从${branch}的${url}进入${checkoutDir}
#
#之后可以指定要拉取的文件夹和文件的列表。
#-----------------------------------------------------------------------------
函数(SparseGitCheckout checkoutDir url分支)
if(存在${checkoutDir})
返回()
endif()
消息(“----------------------------------------------------------------------------”)
消息(“稀疏git签出到${checkoutDir}…”)
消息(“----------------------------------------------------------------------------”)
文件(生成目录${checkoutDir})
集合(cmds“git init”)
set(cmds${cmds}“git remote add-f origin--no tags-t master${url}”)
设置(cmds${cmds}“git config core.sparseCheckout true”)
#此命令通过文件写入执行
#echo>>.git/info/sparse checkout”)
set(cmds${cmds}“git pull--depth=1 origin${branch}”)
#消息(“在目录中:${checkoutDir}”)
foreach(cmd${cmds})
消息(“-${cmd}”)
字符串(替换“;”cmdList${cmd})
#消息(“Outfile:${Outfile}”)
#消息(“最终命令:${cmdList}”)
if(拉入列表cmdList)
字符串(替换“\n”文件“${ARGN}”)
文件(写入${checkoutDir}/.git/info/sparse checkout${FILES})
endif()
执行程序(
命令${cmdList}
工作目录${checkoutDir}
结果变量ret
)
如果(不等于“0”)
消息(“错误:上一个命令失败,请参阅上面的解释”)
文件(删除\u RECURSE${checkoutDir})
break()
endif()
endforeach()
endfunction()
SparseGitCheckout(${CMAKE_BINARY_DIR}/catch_197)https://github.com/catchorg/Catch2.git v1.9.7单个(包括)
SparseGitCheckout(${CMAKE_BINARY_DIR}/catch_masterhttps://github.com/catchorg/Catch2.git 主单板(包括)
我在下面添加了两个函数调用,只是为了说明如何使用该函数

有人可能不喜欢签出master/trunk,因为它可能会坏掉——然后总是可以指定特定的标记


签出将只执行一次,直到您清除缓存文件夹。

我正在搜索类似的解决方案。此处的回复和顶部的教程提供了信息。我研究了此处引用的帖子/博客,以成功构建我的帖子。我发布了完整的CMakeLists.txt,对我来说很有用。我想
cmake_minimum_required(VERSION 3.10.2)

# Target Project
project (ClientProgram)

# Begin: Including Sources and Headers
include_directories(include)
file (GLOB SOURCES "src/*.c")
# End: Including Sources and Headers


# Begin: Generate executables
add_executable (ClientProgram ${SOURCES})
# End: Generate executables


# This Project Depends on External Project(s) 
include (ExternalProject)

# Begin: External Third Party Library
set (libTLS ThirdPartyTlsLibrary)
ExternalProject_Add (${libTLS}
PREFIX          ${CMAKE_CURRENT_BINARY_DIR}/${libTLS}
# Begin: Download Archive from Web Server
URL             http://myproject.com/MyLibrary.tgz
URL_HASH        SHA1=<expected_sha1sum_of_above_tgz_file>
DOWNLOAD_NO_PROGRESS ON
# End: Download Archive from Web Server

# Begin: Download Source from GIT Repository
#    GIT_REPOSITORY  https://github.com/<project>.git
#    GIT_TAG         <Refer github.com releases -> Tags>
#    GIT_SHALLOW     ON
# End: Download Source from GIT Repository

# Begin: CMAKE Comamnd Argiments
CMAKE_ARGS      -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_CURRENT_BINARY_DIR}/${libTLS}
CMAKE_ARGS      -DUSE_SHARED_LIBRARY:BOOL=ON
# End: CMAKE Comamnd Argiments    
)

# The above ExternalProject_Add(...) construct wil take care of \
# 1. Downloading sources
# 2. Building Object files
# 3. Install under DCMAKE_INSTALL_PREFIX Directory

# Acquire Installation Directory of 
ExternalProject_Get_Property (${libTLS} install_dir)

# Begin: Importing Headers & Library of Third Party built using ExternalProject_Add(...)
# Include PATH that has headers required by Target Project
include_directories (${install_dir}/include)

# Import librarues from External Project required by Target Project
add_library (lmytls SHARED IMPORTED)
set_target_properties (lmytls PROPERTIES IMPORTED_LOCATION ${install_dir}/lib/libmytls.so)
add_library (lmyxdot509 SHARED IMPORTED)
set_target_properties(lmyxdot509 PROPERTIES IMPORTED_LOCATION ${install_dir}/lib/libmyxdot509.so)

# End: Importing Headers & Library of Third Party built using ExternalProject_Add(...)
# End: External Third Party Library

# Begin: Target Project depends on Third Party Component
add_dependencies(ClientProgram ${libTLS})
# End: Target Project depends on Third Party Component

# Refer libraries added above used by Target Project
target_link_libraries (ClientProgram lmytls lmyxdot509)