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

Change header install path #213

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

AugusteBourgois
Copy link
Contributor

Hi everyone,

Thanks for the great work behind generate_parameter_library ! It definitely spares us many lines of code.

A problem met by myself and some users according to #180 and #204 is that the generated header cannot be easily used outside the package that generated them. So I've made a small change to fix this. I'm open to discuss this more in depth if necessary, and add tests if required.

This PR solves #180 and #204 by installed the generated header in the same include folder as the current package, so that it can be used in other packages, and not only in the current one.

Copy link
Collaborator

@christophfroehlich christophfroehlich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this. I tried now to use your patch. The idea is then to include this with

#include "gpl_cmake_target_name.hpp"

in the same package as well as in dependent packages?

Maybe

#include "package_name/gpl_cmake_target_name.hpp"

would be a better choice? (or both, otherwise existing packages will break).

Currently, the dependencies of GPL are not exported (rsl + parameter_traits). A simple find_package of the package including the package creating the GPL-library is not sufficient. Could they be exported as well?

@AugusteBourgois
Copy link
Contributor Author

Hi, thanks for the feedback. I pushed a little patch as I noticed this first version did not work in all cases.

Now, say you have package1 generating a header param_file.hpp.

You need to link you libs and executables against the generated target to access the include file, and export it so that it can be accessed from other packages. Finally, you need to add generate_package_library to your ament_export_dependencies so that other packages have access to GPL's dependencies (rsl, package_traits...). I guess a find_package and ament_export_dependencies` could work, but I'm not sure.

find_package(ament_cmake REQUIRED)
find_package(generate_package_library REQUIRED)

generate_package_library(param_lib path_to_param_file.hpp)

# compiled library
add_library(lib1 PUBLIC lib1.cpp)
target_include_directories(lib1 PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>)
target_link_libraries(lib1 PUBLIC param_lib)

# header only library
add_library(lib2 INTERFACE)
target_include_directories(lib2 INTERFACE
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>)
target_link_libraries(lib2 PUBLIC param_lib)

# export targets
install(TARGETS lib1 lib2 param_lib EXPORT ${PROJECT_NAME}Targets)

ament_export_targets(${PROJECT_NAME}Targets HAS_LIBRARY_TARGET)
ament_export_dependencies(generate_parameter_library ...)
ament_package()

The generated header can be included in lib1.cpp and lib2.hpp using

#include <package1/param_file.hpp>

The include line will be the same for other packages.

The current solution breaks existing packages. I can improve it so that it does not, at the cost of duplicating the generated header in the build folder (seems fine to me). I can also try to avoid exporting generate_parameter_library as a dependency, which, I agree, would be ideal.

Any thoughts ?

@christophfroehlich
Copy link
Collaborator

You need to link you libs and executables against the generated target to access the include file, and export it so that it can be accessed from other packages. Finally, you need to add generate_package_library to your ament_export_dependencies so that other packages have access to GPL's dependencies (rsl, package_traits...). I guess a find_package and ament_export_dependencies` could work, but I'm not sure.

find_package(ament_cmake REQUIRED)
find_package(generate_package_library REQUIRED)

generate_package_library(param_lib path_to_param_file.hpp)

# compiled library
add_library(lib1 PUBLIC lib1.cpp)
target_include_directories(lib1 PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>)
target_link_libraries(lib1 PUBLIC param_lib)

# header only library
add_library(lib2 INTERFACE)
target_include_directories(lib2 INTERFACE
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>)
target_link_libraries(lib2 PUBLIC param_lib)

# export targets
install(TARGETS lib1 lib2 param_lib EXPORT ${PROJECT_NAME}Targets)

ament_export_targets(${PROJECT_NAME}Targets HAS_LIBRARY_TARGET)
ament_export_dependencies(generate_parameter_library ...)
ament_package()

The easier it is to use generate_parameter_library, the better. If there is a way to avoid additional cmake stuff in the library, which generates the code, I would vote for it. But maybe there is an argument to not exporting it, if it is not reused in a different package -> then I'm fine for a manual export step together with clear instructions in the docs (which is missing now anyways).

The generated header can be included in lib1.cpp and lib2.hpp using

#include <package1/param_file.hpp>

The include line will be the same for other packages.

This is great.

The current solution breaks existing packages. I can improve it so that it does not, at the cost of duplicating the generated header in the build folder (seems fine to me). I can also try to avoid exporting generate_parameter_library as a dependency, which, I agree, would be ideal.

Could we deprecate the files in the "old" path with some compile-time warning? and then remove that after a couple of releases?

@AugusteBourgois
Copy link
Contributor Author

My last push :

  • installs the generated header (and validation header) so that they are accessible via #include <package_name/parameter_file.hpp> from inside and outside the package.
  • copies the generated header (and validation header) so they they remain accessible via #include "parameter_file.hpp" as they used to from inside the same package.
  • deprecates the local generated header, so that a warning is printed at build time.
  • exports the generated header dependencies (fmt...) so that there is no need for ament_export_dependencies(generate_parameter_library) anymore.

In the next push, I will propose an updated documentation and an extra example package using the generated header from generate_parameter_library_example.

The only thing I haven't tried is the python version, since we don't use python at the moment.

Copy link
Collaborator

@christophfroehlich christophfroehlich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the followup, I just tested this within my projects:
It works as described, but I had to fight CMake to export that as expected (see my comments below).

Furthermore, the include paths in the old examples have to be updated:

from src/generate_parameter_library/example/src/minimal_publisher.cpp:29:
/build/generate_parameter_library_example/include/admittance_controller_parameters.hpp:1:179: note: ‘#pragma message: #include "admittance_controller_parameters.hpp" is deprecated. Use #include <generate_parameter_library_example/admittance_controller_parameters.hpp> instead.’
    1 | #pragma message("#include \"admittance_controller_parameters.hpp\" is deprecated. Use #include <generate_parameter_library_example/admittance_controller_parameters.hpp> instead.")

example_external/CHANGELOG.rst Outdated Show resolved Hide resolved
README.md Show resolved Hide resolved
@AugusteBourgois
Copy link
Contributor Author

Hi @christophfroehlich, I have pushed something to correct the cmake linter error. colcon test now runs without errors, at least locally.

Copy link
Collaborator

@christophfroehlich christophfroehlich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just have tested this again. Could you comment on the changes of your last commit? Where is the problem with a breaking change?

I needed to add the equivalent of

install(TARGETS ${LIB_NAME} EXPORT ${PROJECT_NAME}Targets)
ament_export_targets(${PROJECT_NAME}Targets HAS_LIBRARY_TARGET)

to my code to get that exported, the snippet from the README without ament_export_targets() did not work for me.

README.md Outdated Show resolved Hide resolved
Copy link
Collaborator

@christophfroehlich christophfroehlich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your efforts, I have tested that now successfully and would love to use that in ros2_control soon 👍

Only one question: Is it necessary to link the parameters from the external library? I did not see any difference if I don't link it against the lib.
target_link_libraries(my_lib PUBLIC turtlesim::turtlesim_parameters)

@AugusteBourgois
Copy link
Contributor Author

Great ! Thank you for your review :)

I'm not sure I understand your use case. Can you please give me more information about what you are trying to do ?

PUBLIC
rclcpp::rclcpp
rclcpp_components::component
generate_parameter_library_example::admittance_controller_parameters
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just referring to the external example you provided: I did the same but not added it to the target_link_libraries, and it builds fine. (not sure about the implications with CMake and header-only libraries).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you comment out this line and try to build this package in a clean workspace, it should crash and say that it cannot find the parameters headers.

I haven't seen your example and the way dependencies are created and linked together, but my guess is that the generated parameter library is linked one way or another to your own library, preventing the crash.
Another guess is that since it is a header only library, so if you somehow indicate the headers' path in target_include_directories, the build won't crash.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other words, linking a target with an interface library comes down to adding the correct headers' path. This is the modern Cmake way of doing things, AFAIK.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants