Search code examples
gitcmakeexternal-project

CMake ExternalProject_add with CMakeLists on subfolder


I'm trying to download and build DevIL with an ExternalProject_Add command, from cmake, but for some reason, the git repo has the CMakeLists.txt file inside a subfolder of the project.

I can't seem to find a way to make ExternalProject_add to invoke cmake on the correct folder. I tried using prefix, and source_dir, but it still fails.

Then, I saw an answer online which involved creating a temporary CMakeLists.txt, which would call the correct cmake path. But since I don't know how to provide the path to the cmake call, this meant I would have to put it inside the downloaded git repo, which later showed to be a pain... This is the current command I'm using: include ( "ExternalProject" )

######### Search for required third-party libraries. If not found, attempt to download and build them ########

set ( DEVIL_ROOT "${THIRD_PARTY_OUTPUT_PATH}/DevIL" )
option ( BUILD_DEVIL "Download and build DevIL" FALSE )

find_package ( Devil )
if ( NOT DEVIL_FOUND )
    message ( STATUS "DevIL not found. Forcing to build own copy...")
    set ( BUILD_DEVIL TRUE )
endif()

if ( BUILD_DEVIL )
set ( DEVIL_BUILD_PATH "${THIRD_PARTY_BUILD_PATH}/DevIL/" )
set ( DEVIL_OUTPUT_PATH "${THIRD_PARTY_OUTPUT_PATH}/DevIL" )

message ( STATUS "Downloading DevIL and building...")
file( WRITE ${DEVIL_BUILD_PATH}/DevilCMakeLists.txt "cmake_minimum_required( VERSION 3.0 ) \n  add_subdirectory ( DevIL )" )
ExternalProject_Add ( ThirdParty_DevIL
    PREFIX "${DEVIL_BUILD_PATH}"
    GIT_REPOSITORY "https://github.com/DentonW/DevIL"
    PATCH_COMMAND cmake -E copy "${DEVIL_BUILD_PATH}/DevilCMakeLists.txt" "${DEVIL_BUILD_PATH}/src/ThirdParty_DevIL/DevIL/"
    INSTALL_DIR ${DEVIL_OUTPUT_PATH}
    CMAKE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> )

list ( APPEND THIRD_PARTY_DEPENDENCIES ThirdParty_DevIL )
set ( DEVIL_INCLUDE_DIR "${DEVIL_OUTPUT_PATH}/include" )
set ( DEVIL_LIBRARY devil )
add_definitions ( -DDEVIL )
endif()

list ( APPEND THIRD_PARTY_INCLUDE_DIRS "${DEVIL_INCLUDE_DIR}" )
list ( APPEND THIRD_PARTY_LIBRARIES ${DEVIL_LIBRARY} )

I tried hardcoding the path like this ( the "/src/ThirdParty_DevIL/" part ), but it's ugly as hell, and if cmake decides to change it in the future, it'll break. Also, since this actually is copying a file inside the git tree, cmake later complains with

Failed to unstash changes in:
'<yadda yadda>/ThirdParty/build//DevIL//src/ThirdParty_DevIL/'.


You will have to resolve the conflicts manually

Is there a way to make this solution ( creating a temporary CMakeListst.txt ) work? Or even better, is there another solution, just involving correctly configuring ExternalProject_add ?

I have no requests regarding the parameters, paths, whatever, so feel free to suggest anything. How can I fix this? Thanks


Solution

  • Instead of patching source tree you may provide CONFIGURE_COMMAND option, which uses proper directory as source:

    ExternalProject_Add ( ThirdParty_DevIL
        PREFIX "${DEVIL_BUILD_PATH}"
        GIT_REPOSITORY "https://github.com/DentonW/DevIL"
        INSTALL_DIR ${DEVIL_OUTPUT_PATH}
        CONFIGURE_COMMAND ${CMAKE_COMMAND} -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
            <SOURCE_DIR>/DevIL # Tell CMake to use subdirectory as source.
    )
    

    In that case you should manually add to this command things, which normally are given with options like CMAKE_ARGS, CMAKE_CACHE_ARGS and so on. But this shouldn't be a real problem.


    Alternatively, you may split ExternalProject_Add into two calls: one which only downloads the package, and the second which does everything else. In these calls you may use different SOURCE_DIR options:

    ExternalProject_Add ( ThirdParty_DevIL_download
        PREFIX "${DEVIL_BUILD_PATH}"
        SOURCE_DIR "${DEVIL_BUILD_PATH}/src/ThirdParty_DevIL" # Explicitely set
        GIT_REPOSITORY "https://github.com/DentonW/DevIL"
        CONFIGURE_COMMAND "" # Disable all other steps
        BUILD_COMMAND ""
        INSTALL_COMMAND ""
    )
    
    ExternalProject_Add ( ThirdParty_DevIL
        PREFIX "${DEVIL_BUILD_PATH}"
        DOWNLOAD_COMMAND "" # Disable download step
        SOURCE_DIR "${DEVIL_BUILD_PATH}/src/ThirdParty_DevIL/DevIL" # Source dir for configuration
        INSTALL_DIR ${DEVIL_OUTPUT_PATH}
        CMAKE_CACHE_ARGS -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
    )
    
    # Add dependency between to calls
    add_dependencies(ThirdParty_DevIL ThirdParty_DevIL_download)
    

    Because computation of SOURCE_DIR based on PREFIX depends on external project's name and names are differ in two calls, this option is set explicitely.

    Disadvantage of this approach is that build log shows lines about many "not performed" steps (configure, build and install for the first ExternalProject_Add() call and download, path and update for the second call).