【问题标题】:CMake ExternalProject: how to specify relative path to the root CMakeLists.txt?CMake ExternalProject:如何指定根 CMakeLists.txt 的相对路径?
【发布时间】:2015-07-13 17:16:45
【问题描述】:

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. 不幸的是,更改 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"
    )
    

    这可行,但它很老套,而且很容易在其他外部项目中失败。

  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
    )
    

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

但是,是否有一个更优雅的可以做到这一点?

【问题讨论】:

    标签: git build cmake build-process build-automation


    【解决方案1】:

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

    【讨论】:

    【解决方案2】:

    您可以简单地手动覆盖 cmake command ant set path 到 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")
    

    【讨论】:

      【解决方案3】:

      你可以使用

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

      ExternalProject_Add 调用。这指定了实际的源目录。

      【讨论】:

      • 不幸的是,这是不可能的,这是我尝试的第一件事(请参阅问题中的 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 的路径(这是一种循环依赖)
      • 只是一个快速更新:只要我克隆了libfoo 的存储库手动我可以使用您建议的解决方案(而不是指定GIT_REPOSITORY)。也许这就是你的意思。但是,手动下载依赖项并不是一个真正的选择,因为它太容易出错。
      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        这是一个简单的解决方案

        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 的解析器不必解析整个项目。

        【讨论】:

          【解决方案6】:

          对于那些仍在寻找答案的人:尝试指定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)"
          )
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-09-23
            • 1970-01-01
            • 2020-06-27
            相关资源
            最近更新 更多