2015-05-04 177 views
7

看来CMake ExternalProject始终假定外部项目的根目录 为源目录。但如果不是 的情况呢?CMake ExternalProject:如何指定根CMakeLists.txt的相对路径?

考虑下面的例子:

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

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. 不幸的是,更改SOURCE_DIR的参数ExternalProject确实 不起作用,因为此变量的值用作 的位置,其中libfoo的git存储库被克隆到该位置。这导致了一个不能被破坏的递归依赖性地狱。

  2. 更改libfoo的目录布局以符合ExternalProject。 很明显,这会起作用,但它可能不适用于其他(只读) 第三方库。

  3. 滥用ExternalProject的更新/补丁步骤,例如,通过指定

    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" 
    ) 
    

    这个工程,但它是hackish,很容易失败与其他外部项目。

  4. 建立在solution to another problem上:在CMake假设的位置添加一个临时的 CMakeLists.txt。这个临时文件 则包括实际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 
    ) 
    

    这工作以及和感觉比以前更好的解决方案。

然而,做了更优雅存在做?

回答

9

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

+1

该功能已被接受,并可从CMake 3.7获得:https://cmake.org/cmake/help/v3.7/module/ExternalProject.html – Matthew

1

您可以使用

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

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

+0

不幸的是,这是不可能的,这是我想(见问题1)的第一件事。在你的例子中,git仓库被克隆到'/ path/to/build-dir/EP_libfoo/src/libfoo/libfoo'中。现在我想让CMake使用'/ path/to/build-dir/EP_libfoo/src/libfoo/libfoo/libfoo'(3x libfoo)作为根CMakeLists.txt的路径(这是一种循环依赖) – nils

+0

快速更新:假设我手动克隆了'libfoo' *的存储库*我可以使用您的建议解决方案(而不是指定'GIT_REPOSITORY')。也许这就是你的意思。但是,手动下载依赖关系并不是真正的选择,因为它太容易出错。 – nils

0

我一直在努力解决我正在研究的项目中的同一个问题,这是我能够想出的解决方案。它导致不使用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) 
0

这里有一个简单的解决方案

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} ...) 

基本上你打破它分为两个部分。第一个是获取源代码,第二个是获取软件。我手动创建链接到标题,所以kdevelop的解析器不必分析整个项目。

0

对于那些谁仍然在寻找一个答案:尝试指定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)" 
) 
1

您可以简单地覆盖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")