CMake ExternalProject:如何指定根目录 CMakeLists.txt 的相对路径?

CMake ExternalProject: how to specify relative path to the root CMakeLists.txt?

似乎 CMake ExternalProject 总是假定根目录 外部项目的源目录。但如果那不是呢 案子?

考虑以下示例:

外部项目使用此目录布局:

libfoo.git                 <--- ExternalProject assumes this as source dir.
├── ...
└── libfoo                 <--- However, the actual source directory is this!
    ├── CMakeLists.txt
    └──  ...

在依赖项目中libfoo配置如下:

ExternalProject_Add( libfoo 
    PREFIX            "${CMAKE_CURRENT_BINARY_DIR}/EP_libfoo"
    GIT_REPOSITORY    "<link to remote which hosts libfoo.git>" 
    GIT_TAG           "<some hash>"
)

然后构建失败并显示以下错误消息:

$ cmake -H/path/to/source-dir -B/path/to/build-dir
...
$ cmake --build /path/to/build-dir/ --target all
...
CMake Error: The source directory "/path/to/build-dir/EP_libfoo/src/libfoo" does not appear to contain CMakeLists.txt.
...
$

因此,正如上面目录布局中指出的那样,CMake 认为根 外部项目是

/path/to/build-dir/EP_libfoo/src/libfoo

实际上是

/path/to/build-dir/EP_libfoo/src/libfoo/libfoo

我解决这个问题的尝试:

  1. 不幸的是,更改 ExternalProject 的参数 SOURCE_DIR 确实 不起作用,因为此变量的值用作 libfoo 的 git 存储库被克隆到其中。这导致无法打破的递归依赖地狱。

  2. 正在更改 libfoo 的目录布局以符合 ExternalProject。 显然,这可行,但可能不适用于其他(只读) 第三方库。

  3. 滥用 ExternalProject 的 update/patch 步骤,例如通过指定

    set( EP_LIBFOO_DIR "${CMAKE_CURRENT_BINARY_DIR}/EP_libfoo" )
    
    ExternalProject_Add( libfoo 
        PREFIX            "${EP_LIBFOO_DIR}"
        GIT_REPOSITORY    "<link to remote which hosts libfoo.git>" 
        GIT_TAG           "<some hash>"
    
        # Copy the content of `<...>/libfoo/libfoo` into `<...>/libfoo`.
        # Note to self: using symlinks instead copying is too platform-specific.
        PATCH_COMMAND     ${CMAKE_COMMAND} -E copy_directory "${EP_LIBFOO_DIR}/src/libfoo/libfoo" "${EP_LIBFOO_DIR}/src/libfoo"
    )
    

    这行得通,但它很老套,而且很容易与其他外部项目一起失败。

  4. 建立在solution to another problem:添加一个临时 CMakeLists.txt 在 CMake 假定的位置。这个临时文件 然后包括实际的 CMakeLists.txt:

    set( EP_LIBFOO_DIR "${CMAKE_CURRENT_BINARY_DIR}/EP_libfoo" )
    set( GENERATED_DIR "${CMAKE_BINARY_DIR}/generated" )
    
    file( MAKE_DIRECTORY ${GENERATED_DIR} )
    file( WRITE ${GENERATED_DIR}/CMakeLists.txt
        "cmake_minimum_required( VERSION 3.0 )\n"
        "add_subdirectory( libfoo )\n" 
    )
    
    ExternalProject_Add( libfoo 
        PREFIX            "${EP_LIBFOO_DIR}"
        GIT_REPOSITORY    "<link to remote which hosts libfoo.git>" 
        GIT_TAG           "<some hash>"
    
        # Copy the 
        UPDATE_COMMAND    ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/generated/CMakeLists.txt ${EP_LIBFOO_DIR}/src/libfoo
    )
    

    这也比以前的解决方案更有效,感觉更好。

但是,是否存在 更优雅 来做同样的事情?

您可以使用

SOURCE_DIR /path/to/build-dir/EP_libfoo/src/libfoo/libfoo  

ExternalProject_Add来电。这指定了实际的源目录。

我一直在努力解决我正在处理的项目中的相同问题,这是我能够提出的解决方案。它导致不使用 ExternalProject 进行 git 处理,但据我所知导致相同的行为。

CMakeLists.txt

include(ExternalProject)
set(libfoo_prefix ${CMAKE_HOME_DIRECTORY}/libfoo)

# during generation remove any previous repo and clone.
file(REMOVE_RECURSE ${libfoo_prefix})
execute_process(
    COMMAND git clone <link to remote which hosts libfoo.git>
    WORKING_DIRECTORY ${CMAKE_HOME_DIRECTORY})

# Add the external project.
ExternalProject_Add(libfoo
    PREFIX ${libfoo_prefix}
    SOURCE_DIR ${libfoo_prefix}/libfoo)
# As part of the pre-build step update the git repo.
add_custom_command(
    TARGET libfoo
    PRE_BUILD
    COMMAND ${CMAKE_COMMAND} -P GitPull.cmake)

GitPull.cmake

execute_process(
    COMMAND git pull origin master
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIRECTORY}/libfoo)

这是一个简单的解决方案

set(EXTERNAL_PROJECTS "")
set(EXTERNAL_LIBS "")
include(ExternalProject)

# Set compiler(s) per project as required to CMAKE_ARGS in ExternalProject_Add(..).
#       -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
#       -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
set(EXTERNAL_CMAKE_ARGS -D CMAKE_SYSROOT=${CMAKE_SYSROOT}
    -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=${CMAKE_FIND_ROOT_PATH_MODE_PROGRAM}
    -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=${CMAKE_FIND_ROOT_PATH_MODE_LIBRARY}
    -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=${CMAKE_FIND_ROOT_PATH_MODE_INCLUDE}
    -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=${CMAKE_FIND_ROOT_PATH_MODE_PACKAGE}
)

set(AIOUSB aiousb)
set(AIOUSB_SRC aiousb_src)
set(EXTERNAL_PROJECTS ${EXTERNAL_PROJECTS} ${AIOUSB_SRC} ${AIOUSB})
set(AIOUSB_SRC_GIT_BRANCH "master")

ExternalProject_Add(${AIOUSB_SRC}
    PREFIX ${AIOUSB_SRC}
    GIT_REPOSITORY "https://github.com/accesio/AIOUSB.git"
    GIT_TAG ${AIOUSB_SRC_GIT_BRANCH}
    UPDATE_COMMAND ""
    CONFIGURE_COMMAND ""
    BUILD_COMMAND ""
    BUILD_BYPRODUCTS ${CMAKE_BINARY_DIR}/${AIOUSB_SRC}/src/${AIOUSB_SRC}/AIOUSB/CMakeLists.txt
    INSTALL_COMMAND ""
)

set(AIOUSB_LIBRARY ${CMAKE_BINARY_DIR}/${AIOUSB}/src/${AIOUSB}-build/lib/${CMAKE_STATIC_LIBRARY_PREFIX}${AIOUSB}${CMAKE_STATIC_LIBRARY_SUFFIX})
set(AIOUSBCPP_LIBRARY ${CMAKE_BINARY_DIR}/${AIOUSB}/src/${AIOUSB}-build/lib/${CMAKE_STATIC_LIBRARY_PREFIX}${AIOUSB}cpp${CMAKE_STATIC_LIBRARY_SUFFIX})

ExternalProject_Add(${AIOUSB}
    DEPENDS ${AIOUSB_SRC}
    PREFIX ${AIOUSB}
    DOWNLOAD_COMMAND ""
    SOURCE_DIR ${CMAKE_BINARY_DIR}/${AIOUSB_SRC}/src/${AIOUSB_SRC}/AIOUSB
    CMAKE_ARGS ${EXTERNAL_CMAKE_ARGS} -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
        -DBUILD_SAMPLES:BOOL=OFF -DBUILD_AIOUSB_SHARED:BOOL=OFF -DBUILD_AIOUSBDBG_SHARED:BOOL=OFF -DBUILD_AIOUSBCPP_SHARED:BOOL=OFF
        -DBUILD_AIOUSBCPPDBG_SHARED:BOOL=OFF
    BUILD_BYPRODUCTS ${CMAKE_SOURCE_DIR}/src/lib/include/${AIOUSB} ${CMAKE_BINARY_DIR}/${AIOUSB}/src/${AIOUSB}-build/lib/libaiousb.a
        ${CMAKE_BINARY_DIR}/${AIOUSB}/src/${AIOUSB}-build/lib/libaiousbcpp.a
    INSTALL_COMMAND rm -rf ${CMAKE_SOURCE_DIR}/src/lib/include/${AIOUSB} && mkdir -p ${CMAKE_SOURCE_DIR}/src/lib/include/${AIOUSB} &&
        echo "ln -sr ${CMAKE_BINARY_DIR}/${AIOUSB_SRC}/src/${AIOUSB_SRC}/AIOUSB/lib/*.h ${CMAKE_SOURCE_DIR}/src/lib/include/${AIOUSB}" | bash
)

set(LIBAIOUSB libaiousb)
add_library(${LIBAIOUSB} STATIC IMPORTED)
set_property(TARGET ${LIBAIOUSB} PROPERTY IMPORTED_LOCATION ${AIOUSB_LIBRARY})
add_dependencies(${LIBAIOUSB} ${AIOUSB})
set(EXTERNAL_LIBS ${EXTERNAL_LIBS} ${LIBAIOUSB})

set(LIBAIOUSBCPP libaiousbcpp)
add_library(${LIBAIOUSBCPP} STATIC IMPORTED)
set_property(TARGET ${LIBAIOUSBCPP} PROPERTY IMPORTED_LOCATION ${AIOUSBCPP_LIBRARY})
add_dependencies(${LIBAIOUSBCPP} ${AIOUSB})
set(EXTERNAL_LIBS ${EXTERNAL_LIBS} ${LIBAIOUSBCPP})

...
add_dependencies(${PROJECT_NAME} ${EXTERNAL_PROJECTS})
...
also add 
target_link_libraries(${PROJECT_NAME} ${EXTERNAL_LIBS} ...)

基本上你把它分成两部分。第一个只是为了获取源代码,第二个是构建软件。我手动创建指向 headers 的链接,因此 kdevelop 的解析器不必解析整个项目。

我已提交 a merge request 添加一个 SOURCE_SUBDIR 选项到 ExternalProject_Add 来解决这个用例。希望它将在 CMake 3.7 中可用。 (您也可以将 ExternalProject*.cmake 本地复制到您自己的项目中以立即利用该功能。)

对于那些仍在寻找答案的人:尝试指定 CONFIGURE_COMMAND:

ExternalProject_Add(libfoo 
    GIT_REPOSITORY "<link to remote which hosts libfoo.git>" 
    GIT_TAG "<some hash>"
    SOURCE_DIR "where to put the source"
    CONFIGURE_COMMAND
        "${CMAKE_COMMAND}"
        "-HPathToDirectoryWhereTheCMakeListsAre"
        "-BWhereToBuild"
    BUILD_COMMAND
        "${CMAKE_COMMAND}" --build "Path to the directory where you are building (specified with -B flag in CONFIGURE_COMMAND)"
)

您可以简单地将 cmake 命令蚂蚁设置路径手动覆盖到 CMakeLists.txt。例如

ExternalProject_Add(libfoo
   PREFIX            "${EP_LIBFOO_DIR}"
   GIT_REPOSITORY    "<link to remote which hosts libfoo.git>" 
   GIT_TAG           "<some hash>"
   CONFIGURE_COMMAND ${CMAKE_COMMAND} -G${CMAKE_GENERATOR} "${EP_LIBFOO_DIR}/src/libfoo/libfoo")