15

I'm trying to add external project as a library to my project using ExternalProject_Add:

ExternalProject_Add(
        xgboost
        GIT_REPOSITORY https://github.com/dmlc/xgboost.git
        GIT_TAG v0.60
        PREFIX ${CMAKE_CURRENT_BINARY_DIR}
        INSTALL_COMMAND ""
)

Also, I'm defining library target and adding external project as a dependency:

set(XGBOOST_PREFIX_PATH "${CMAKE_CURRENT_BINARY_DIR}/src")
set(XGBOOST_BINARY_PATH "${XGBOOST_PREFIX_PATH}/xgboost-build")
set(XGBOOST_BINARY_INCLUDE "${XGBOOST_PREFIX_PATH}/xgboost/include;${XGBOOST_PREFIX_PATH}/xgboost/dmlc-core/include;${XGBOOST_PREFIX_PATH}/xgboost/rabit/include")
add_library(libxgboost IMPORTED STATIC GLOBAL)
add_dependencies(libxgboost xgboost)

set_target_properties(libxgboost PROPERTIES
        "IMPORTED_LOCATION" "${XGBOOST_BINARY_PATH}/liblibxgboost.dylib"
        "IMPORTED_LINK_INTERFACE_LIBRARIES" "${CMAKE_THREAD_LIBS_INIT}"
        "INTERFACE_INCLUDE_DIRECTORIES" "${XGBOOST_BINARY_INCLUDE}"
        )

in another CMakeLists.txt:

add_library(somelib STATIC SomeLib.cpp)
target_include_directories(somelib PUBLIC libxgboost)
target_link_libraries(somelib libxgboost)

The problem is that cmake INTERFACE_INCLUDE_DIRECTORIES doesn't allow to export include dir which does not exist.

Is there any other way to make header files being included automatically for all targets which depend on libxgboost ?

UPDATE:

Error message:

CMake Error in somelib/CMakeLists.txt:
  Imported target "libxgboost" includes non-existent path

    "build/xgboost/src/xgboost/include"

  in its INTERFACE_INCLUDE_DIRECTORIES.  Possible reasons include:

  * The path was deleted, renamed, or moved to another location.

  * An install or uninstall procedure did not complete successfully.

  * The installation package was faulty and references files it does not
  provide.
Stanislav Levental
  • 2,165
  • 1
  • 14
  • 28
  • Presize error message, please. – Tsyvarev Aug 04 '17 at 22:37
  • `IMPORTED_LINK_INTERFACE_LIBRARIES` is deprecated btw. Use `INTERFACE_LINK_LIBRARIES`. Have you considered using `execute_process()` to call `cmake` (twice) on the ExternalProject CMakeLists.txt then you would have dependencies available at configure time? Alternatively, you could fake the creation of just the dependency directories during CMakeLists.txt configure time. – utopia Aug 05 '17 at 09:04
  • That's my plan B, but still wondering if there is a good practice for that – Stanislav Levental Aug 07 '17 at 02:04
  • I faced the same exact issue. Any luck? – mchiasson Nov 16 '17 at 20:08
  • 1
    @mchiasson I've added the full CMakeLists.txt, hope it will help – Stanislav Levental Nov 17 '17 at 19:34

2 Answers2

14

I'll post the final CMakeLists.txt to include xgboost into your project, it might be useful for someone, the solution to the problem above is to create directories during cmake configure phase (NOTE: I'm using OSX to build that, so you would need to use liblibxgboost.so for GNU/Linux instead of liblibxgboost.dylib):

include(ExternalProject)

ExternalProject_Add(
        xgboost
        GIT_REPOSITORY https://github.com/dmlc/xgboost.git
        GIT_TAG v0.60
        PREFIX ${CMAKE_CURRENT_BINARY_DIR}
        INSTALL_COMMAND ""
)

set(XGBOOST_PREFIX_PATH "${CMAKE_CURRENT_BINARY_DIR}/src")
set(XGBOOST_BINARY_PATH "${XGBOOST_PREFIX_PATH}/xgboost-build")

set(XGBOOST_LIB_INCLUDE "${XGBOOST_PREFIX_PATH}/xgboost/include")
set(DMLC_LIB_INCLUDE "${XGBOOST_PREFIX_PATH}/xgboost/dmlc-core/include")
set(RABIT_LIB_INCLUDE "${XGBOOST_PREFIX_PATH}/xgboost/rabit/include")
set(XGBOOST_BINARY_INCLUDE "${XGBOOST_LIB_INCLUDE};${DMLC_LIB_INCLUDE};${RABIT_LIB_INCLUDE}")

# Hack to make it work, otherwise INTERFACE_INCLUDE_DIRECTORIES will not be propagated
file(MAKE_DIRECTORY ${XGBOOST_LIB_INCLUDE})
file(MAKE_DIRECTORY ${DMLC_LIB_INCLUDE})
file(MAKE_DIRECTORY ${RABIT_LIB_INCLUDE})

add_library(libxgboost IMPORTED STATIC GLOBAL)
add_dependencies(libxgboost xgboost)

set_target_properties(libxgboost PROPERTIES
        "IMPORTED_LOCATION" "${XGBOOST_BINARY_PATH}/liblibxgboost.dylib"
        "INTERFACE_INCLUDE_DIRECTORIES" "${XGBOOST_BINARY_INCLUDE}"
)
Stanislav Levental
  • 2,165
  • 1
  • 14
  • 28
  • 5
    I hope CMake will support this better; it seems like a bit of a hack. Is there no way to handle this with dependencies somehow? For example, you can set the IMPORTED_LOCATION of a library without it existing. Having INTERFACE_INCLUDE_DIRECTORIES be different seems inconsistent. – Jeff Trull Nov 20 '17 at 00:27
  • 3
    @JeffTrull I wish it supports it in a better way. But it's the only way I've found, still, it doesn't mean that it's the only way that exists. I'm new to cmake so I could be missing something here. – Stanislav Levental Nov 20 '17 at 23:38
  • 2
    FWIW, `file(MAKE_DIRECTORY ...)` is what people came up with on the CMake bug tracker too: https://gitlab.kitware.com/cmake/cmake/issues/15052 – HunterZ Jun 20 '19 at 23:18
1

Note that it is possible to combine FetchContent and ExternalProject_Add to achieve the following:

  • FetchContent will make the source available at configure time, thereby bypassing the issue that the INTERFACE_INCLUDE_DIRECTORIES doesn't exist.
  • ExternalProject_Add will use the source downloaded by FetchContent to build at build time

Here is an example building libpqxx:

include(FetchContent)
include(ExternalProject)

find_package(PostgreSQL)

# Using FetchContent means we will download libpqxx at configure-time

FetchContent_Declare(libpqxx_ext
    GIT_REPOSITORY
        https://github.com/jtv/libpqxx
    GIT_TAG
        tags/7.4.1
    )

FetchContent_GetProperties(libpqxx_ext)

if(NOT libpqxx_ext_POPULATED)
  FetchContent_Populate(libpqxx_ext)
endif()

set(LIB_PQXX_SRC_DIR ${CMAKE_BINARY_DIR}/_deps/libpqxx_ext-src)

# Build libpqxx

ExternalProject_Add(libpqxx_ext
    SOURCE_DIR
        ${libpqxx_ext_SOURCE_DIR}
    PREFIX
        ext
    BINARY_DIR
        build
    CMAKE_ARGS
        -DSKIP_BUILD_TEST=on
    INSTALL_COMMAND
        ""
    BUILD_ALWAYS
        OFF
    )

set(LIB_PQXX_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/build/src)

# Make the libpxqq artifacts available to our project

add_library(libpqxx STATIC IMPORTED GLOBAL)

set_target_properties(libpqxx
    PROPERTIES

    INTERFACE_INCLUDE_DIRECTORIES
        ${LIB_PQXX_SRC_DIR}/include
    IMPORTED_LOCATION
        ${LIB_PQXX_BUILD_DIR}/libpqxx.a
    INTERFACE_LINK_LIBRARIES
        ${PostgreSQL_LIBRARIES})

add_dependencies(libpqxx libpqxx_ext)
Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213