Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ament_export_dependencies can't export all PkgConfig projects found in CMake #504

Open
Ryanf55 opened this issue Feb 8, 2024 · 2 comments
Labels
enhancement New feature or request more-information-needed Further information is required

Comments

@Ryanf55
Copy link
Contributor

Ryanf55 commented Feb 8, 2024

I'm refactoring a large repository to be installable with ament_cmake. It's a CMake project, and relies on library libav, which does not support CMake. libav only comes with Pkgconfig support. Gstreamer is pulled in through pkgconfig also.

I'm using CMake 3.28, so I have full access to the latest language features.

Current Behavior

Thus, here's how the library foo brings it in with CMake's built in PkgConfig support.

Some details omitted for brevity.

pkg_check_modules(LIBAV REQUIRED IMPORTED_TARGET libavcodec>=50)
pkg_check_modules(GST REQUIRED IMPORTED_TARGET gstreamer-1.0>=1.2)

add_executable(my_node)
target_link_libraries(my_node PRIVATE PkgConfig::LIBAV PkgConfig::GST)

ament_export_dependencies(
   LIBAV
   GST
)
ament_package()

This builds fine. But, a consuming package bar can't use the exported dependencies.

--- stderr: bar
CMake Error at /ws/install/foo/share/foo/cmake/ament_cmake_export_dependencies-extras.cmake:21 (find_package):
  By not providing "FindLIBAV.cmake" in CMAKE_MODULE_PATH this project has
  asked CMake to find a package configuration file provided by "LIBAV", but
  CMake did not find one.

  Could not find a package configuration file provided by "LIBAV" with any of
  the following names:

    LIBAVConfig.cmake
    libav-config.cmake

  Add the installation prefix of "LIBAV" to CMAKE_PREFIX_PATH or set
  "LIBAV_DIR" to a directory containing one of the above files.  If "LIBAV"
  provides a separate development package or SDK, be sure it has been
  installed.
Call Stack (most recent call first):
  /ws/install/foo/share/foo/cmake/fooConfig.cmake:41 (include)
  CMakeLists.txt:84 (find_package)

Library foo has this as the INTERFACE_INCLUDE_DIRECTORIES: `INTERFACE_LINK_LIBRARIES "PkgConfig::GST;PkgConfig::LIBAV" in the export.

And, in ament_cmake_export_dependencies-extras.cmake

set(_exported_dependencies "GST;LIBAV")

Expected Behavior

ament_export_dependencies can support and recognize targets created with PkgConfig and support downstream packages with PkgConfig.

Workarounds

Proposal

Amend ament_cmake_export_dependencies-extras.cmake to also try to find PkgConfig projects as a backup using pkg_check_modules. Note that the flag IMPORTED_TARGET means that users of this will have to use that also.

set(_exported_dependencies "LIBAV;GST")

find_package(ament_cmake_libraries QUIET REQUIRED)

if(NOT _exported_dependencies STREQUAL "")
  find_package(ament_cmake_core QUIET REQUIRED)
  set(Foo_DEPENDENCIES ${_exported_dependencies})
  set(Foo_RECURSIVE_DEPENDENCIES ${_exported_dependencies})
  set(_libraries)
  foreach(_dep ${_exported_dependencies})
    if(NOT ${_dep}_FOUND)
      find_package("${_dep}" QUIET)
    endif()
    if(NOT ${_dep}_FOUND)
      pkg_check_modules(${_dep} IMPORTED_TARGET)
    endif()
    if(NOT ${_dep}_FOUND)
      find_package("${_dep}" REQUIRED)
    endif()

This will work because of the following in FooTargetExport.cmake

set_target_properties(Foo::FooPROPERTIES
  INTERFACE_INCLUDE_DIRECTORIES "${_IMPORT_PREFIX}/include"
  INTERFACE_LINK_LIBRARIES "PkgConfig::GST;PkgConfig::LIBAV"
)
@sloretz
Copy link
Contributor

sloretz commented Feb 8, 2024

Write my own find script for the library and install that with config-extras.

This is the route I'd recommend.

Not exactly the same as using pkg-config, but a similar example is how tf2_bullet deals with Bullet only offering old-style standard CMake variables instead of exported targets. It uses the config-extras feature to create imported targets: https://github.com/ros2/geometry2/blob/4a46a8f090df5bf436d80f35bd3285adee06a448/tf2_bullet/bullet-extras.cmake#L35-L40

I'd recommend creating a libav_cmake_module package that had the sole purpose of exporting a FindLIBAV module that uses pkg-config to create imported targets. Then packages can depend on libav_cmake_module and export dependencies on both libav_cmake_module and LIBAV. python_cmake_module is an example worth looking at.

ament_export_dependencies can support and recognize targets created with PkgConfig and support downstream packages with PkgConfig

I'm unfamiliar with PkgConfig creating targets. If a package did ament_export_dependencies(LIBAV), how would ament_cmake determine that downstream packages need to run pkg_check_modules(LIBAV REQUIRED IMPORTED_TARGET libavcodec>=50)? Where would knowledge of the argument libavcodec>=50 come from?

@sloretz sloretz added enhancement New feature or request more-information-needed Further information is required labels Feb 8, 2024
@Ryanf55
Copy link
Contributor Author

Ryanf55 commented Feb 8, 2024

Here's what I've got so far without relying on fragile variables.

FindLIBAV
It's not clear how to let users of the find script express version info, or a list of components they want. This would ideally be pushed upstream to LIBAV, or CMake's built-in find scripts. ament_cmake doesn't need to be concerned with where these come from.

# FindLIBAV.cmake
# This Finds libav using CMake.
# https://github.com/libav/libav
#
# Reference https://stackoverflow.com/questions/71531333/how-set-cmake-to-find-a-local-package-ffmpeg

find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBAV IMPORTED_TARGET
    libavdevice
    libavfilter
    libavformat
    libavcodec
    libswresample
    libswscale
    libavutil
)

I have Find Scripts living in a separate ROS-independent library cmake_helpers that is re-used throughout the codebase in multiple repos. These are already exported to the build tree, accessible with find_package(cmake_helpers CONFIG REQUIRED) which adds these to CMAKE_MODULE_PATH.

Next, a consuming library Foo depends on cmake_helpers and the binary dependencies.

<depend>gstreamer1.0</depend>
<depend>gstreamer1.0-libav</depend>
<depend>cmake_helpers</depend>
project(Foo)
find_package(av_cmake_helpers 0 CONFIG REQUIRED)
find_package(LIBAV MODULE REQUIRED) # No ability to specify versioning yet
find_package(GST MODULE REQUIRED) # No ability to specify versioning yet
target_link_libraries(FooLibrary
   PUBLIC
      PkgConfig::GST
      PkgConfig::LIBAV)
include(GNUInstallDirs)
install(
  TARGETS FooLibrary
  EXPORT ${PROJECT_NAME}Export
  FILE_SET HEADERS
  INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
ament_export_targets(${PROJECT_NAME}Export HAS_LIBRARY_TARGET)
ament_export_dependencies(
    rclcpp
)

ament_package(CONFIG_EXTRAS ${PROJECT_NAME}-extras.cmake.in)

And finally:
Foo-extras.cmake.in

find_dependency(GST MODULE REQUIRED) # No way to express version dependencies here
find_dependency(LIBAV MODULE REQUIRED) # No way to express version dependencies here

This, the scope of this request would have ament automatically handle the exports of dependencies in ament_export_dependencies with versioning and component requirements properly expressed in the link interface.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request more-information-needed Further information is required
Projects
None yet
Development

No branches or pull requests

2 participants