joaocandre
joaocandre

Reputation: 1745

How to exporting targets fetched with FetchContent

I'm trying to use FetchContent CMake helper to automate building required dependencies in case they are not installed/available through find_package, but hitting a wall at how to handle exporting the build targets.

My own project e.g. needs TinyXML2 as a dependency:

## TinyXML2
find_package(tinyxml2 QUIET)
if (${tinyxml2_FOUND})
    message(STATUS "tinyxml2 FOUND!(${tinyxml2_LIBRARIES})")
    echo_all_cmake_variable_values()
else()
    message(STATUS "tinyxml2 NOT FOUND, fetching from source!")
    FetchContent_Declare(tinyxml2
      GIT_REPOSITORY     https://github.com/leethomason/TinyXML2
      GIT_TAG            9.0.0
    )
    FetchContent_MakeAvailable(tinyxml2)
    set(tinyxml2_LIBRARIES tinyxml2)
endif()

Which is then used to link the project targets:

# ...
target_link_libraries(${PROJECT_NAME} PUBLIC ${Boost_LIBRARIES}
                                             pthread
                                             ${tinyxml2_LIBRARIES})

And the linked target is then exported:

# ...
export(EXPORT ${PROJECT_NAME}Targets
       FILE ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake
       NAMESPACE ${PROJECT_NAME}::)

The issue is that, when fetching from source, the export step fails:

CMake Error in src/myproject/CMakeLists.txt:
    export called with target "myproject" which requires target "tinyxml2" that
    is not in any export set.

Is there a way to also auto-export tinyxml2 as a required dependency?

Upvotes: 10

Views: 3176

Answers (1)

Alex Reinking
Alex Reinking

Reputation: 19936

Is there a way to also auto-export tinyxml2 as a required dependency?

Not as of CMake 3.23 (pre-release).

When you use FetchContent, it's as if you wrote the "third-party" code yourself. CMake has no idea that it's external. This is called "vendoring" code.

Because you aren't going through find_package, you will need to install() "your" target like any other dependency that you will export. It will export into your namespace, too, to avoid conflicts with other copies of tinyxml2 that might be floating around.

I also find this tedious, which is why I shy away from FetchContent for anything that might be installed or exported and use proper source-based package management tools like vcpkg instead. Such tools let you use find_package to locate your dependencies, which is (imo) what you should be doing 100% of the time, anyway.


Also, your code is wrong (see my top level comment). Here's a more correct version (I would just say find_package(tinyxml2 REQUIRED), personally).

## TinyXML2
find_package(tinyxml2 QUIET)
if (NOT tinyxml2_FOUND)
  message(STATUS "tinyxml2 NOT FOUND, fetching from source!")
  FetchContent_Declare(tinyxml2
    GIT_REPOSITORY     https://github.com/leethomason/TinyXML2
    GIT_TAG            9.0.0
  )
  FetchContent_MakeAvailable(tinyxml2)
  install(
    TARGETS tinyxml2 
    EXPORT ${PROJECT_NAME}Targets
    # More arguments as necessary...
  )
endif ()

# ... usage ...
target_link_libraries(${PROJECT_NAME} PUBLIC ${Boost_LIBRARIES}
                                             pthread
                                             tinyxml2::tinyxml2)

This is less typing than going through a variable, too.

Upvotes: 8

Related Questions