1

I have a small project whose CMakeLists.txt looks something like this:

include(ExternalProject)
ExternalProject_Add(gainput_project
  PREFIX gainput
  GIT_REPOSITORY https://github.com/jkuhlmann/gainput.git
  GIT_TAG e21b15f0bc3dd3f1a745fe89a966a2457e940142
  INSTALL_COMMAND ""
)
ExternalProject_Get_Property(gainput_project SOURCE_DIR)
ExternalProject_Get_Property(gainput_project BINARY_DIR)
set(GAINPUT_SOURCE_DIR "${SOURCE_DIR}")
set(GAINPUT_BINARY_DIR "${BINARY_DIR}")

add_executable(demo main.cpp)
add_dependencies(demo gainput_project)

target_include_directories(demo PUBLIC
  SYSTEM ${GAINPUT_SOURCE_DIR}/lib/include
)

# TODO: this is bad
if(WIN32)
  target_link_libraries(demo PUBLIC
    debug ${GAINPUT_BINARY_DIR}/lib/Debug/gainput-d.lib
    optimized ${GAINPUT_BINARY_DIR}/lib/Release/gainput.lib
  )
else()
  target_link_libraries(demo PUBLIC
    debug ${GAINPUT_BINARY_DIR}/lib/gainput-d.so
    optimized ${GAINPUT_BINARY_DIR}/lib/gainput.so
  )
endif()

Obviously, those calls to target_link_libraries are not ideal; they are very repetitive and I am explicitly writing paths and filenames that the build system should already know. What is the best way to handle the different output directories and filenames from the external project?

Is there a way I can query the external project to get the actual output dir? (e.g. via ExternalProject_Get_Property?) Is there some magical generator expression I can use? (ExternalProject_Get_Property(gainput_project RUNTIME_OUTPUT_DIRECTORY) does not work.)

It seems like find_library would work, except that the output library does not exist at configure time, so that fails.

Maybe I should set the external project's INSTALL_DIR to some hard-coded directory that I control and always use that? (Would it still get the Debug and Release subdirectories on Windows?)

Would the FetchContent module give me better access to the outputs of the included project? I don't see anything obvious in the docs there.

Idk, I've already tried quite a few ideas and nothing has worked so far. I feel like I may be missing something fundemental - this is the first time I've used ExternalProject. Any tips?

0x5453
  • 12,753
  • 1
  • 32
  • 61
  • 1
    "I am explicitly writing paths and filenames that the build system should already know." - No, build system knows nothing about paths in a subproject used via `ExternalProject_Add`. Look: the subproject is not even configured when you run `cmake` for the main project. "Is there a way I can query the external project to get the *actual* output dir?" - Only if the specific subproject supports such query. Or read the subproject's documentation. Or read the subproject's `CMakeLists.txt`. Build system does't know even about `-d` suffix for a debug library. – Tsyvarev Aug 18 '20 at 06:38
  • 1
    `FetchContent` approach (which is eventually calls `add_subdirectory`) allows you to build a subproject as a **part of the main project**. That is, the subproject and the main project would **share** targets, variables, etc. This sharing could be an advantage (e.g. in the main project you can directly use targets defined in the subproject) or a disadvantage (e.g. projects could have conflicting requirements for the variables settings). `ExternalProject_Add` builds a subproject in its **own environment**, so a conflict for CMake variables isn't possible. – Tsyvarev Aug 18 '20 at 06:54
  • Thanks @Tsyvarev. `FetchContent` plus generator expressions (e.g. `$`) seems to be what I was looking for. Feel free to post that as an answer. – 0x5453 Aug 18 '20 at 12:38

1 Answers1

0

As @Tsyvarev suggested, I solved this by switching from ExternalProject to FetchContent and then using a generator expression to find the output file:

include(FetchContent)
FetchContent_Declare(gainput
  PREFIX gainput
  GIT_REPOSITORY https://github.com/jkuhlmann/gainput.git
  GIT_TAG e21b15f0bc3dd3f1a745fe89a966a2457e940142
  INSTALL_COMMAND ""
)
FetchContent_MakeAvailable(gainput)

add_executable(demo main.cpp)
add_dependencies(demo gainput)

target_include_directories(demo PUBLIC
  SYSTEM ${gainput_SOURCE_DIR}/lib/include
)

target_link_libraries(demo PUBLIC
  $<TARGET_LINKER_FILE:gainput>
)
0x5453
  • 12,753
  • 1
  • 32
  • 61