-
Notifications
You must be signed in to change notification settings - Fork 123
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
Add rosidl generate CLI. #567
Conversation
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Partial review
This function assumes ROS interface definition files follow the typical | ||
``rosidl`` install space layout i.e. 'package_name/subfolder/interface.idl'. | ||
""" | ||
return pathlib.Path(path).parents[1].name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Recommend documenting that it assumes path
is a relative path starting from the package's install prefix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, it should work with absolute paths too. I do realize I should've resolved the path. See 3a75bcd.
rosidl_cli/rosidl_cli/cli.py
Outdated
return subparser | ||
|
||
|
||
def main(*, script_name='rosidl', argv=None, description=None, commands=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no need for arguments; justdef main():
is fine because console_scripts
entry points are called with no arguments
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This I took from ros2cli
. I understand it's there to (cheaply) support reuse. I can drop it if you think it's not warranted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd recommend removing them. See 4fa6682 for a suggested way of doing it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See 1140a09.
with pytest.raises(ValueError): | ||
normalize_entry_point_specs(['name?']) | ||
|
||
specs = normalize_entry_point_specs(['c', 'cpp==0.1.0']) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the motivation for the version specification? IIUC this would mean two different packages could do
# First package
entry_points={
'rosidl_cli.command.generate.type_extensions': [
'cpp = first_pkg.v1.CppExtension',
],
# Second package
entry_points={
'rosidl_cli.command.generate.type_extensions': [
'cpp = second_pkg.v2.CppExtension',
],
but why would this situation arise?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the motivation for the version specification?
It's there to:
- Enforce tooling versioning to ensure API/ABI stability and simplify deprecation cycles
I envision packages pinning plugin versions to guarantee API/ABI stability.
IIUC this would mean two different packages could do
That would cause a warning at least, and an error at most. I do not expect the same extension to be provided by different Python distributions (i.e. packages), I expect source code generation to be attempted against different versions of the same Python distribution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I envision packages pinning plugin versions to guarantee API/ABI stability.
Pin in what context? As in, someone has a package with message files and they want to be sure they're only generated with cpp==0.1.0
? If they're releasing their package they can pin using the version_*
attributes in the <*depend>
tags of the package.xml
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As in, someone has a package with message files and they want to be sure they're only generated with cpp==0.1.0?
Precisely.
If they're releasing their package they can pin using the version_* attributes in the <*depend> tags of the package.xml.
Hmm, do build tools like colcon
enforce them? What if there's no package.xml
e.g. if source code generation is performed outside a ROS package? At any rate, it seems way simpler and cheaper to me to have plugin versions (that ultimately map to that of the package) than to overcome those limitations in ROS' package management system.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, do build tools like
colcon
enforce them? What if there's no package.xml e.g. if source code generation is performed outside a ROS package?
You're right that a version requirement in the package.xml
AFAIK is ignored by colcon
, but I don't see how that or generating messages inside or outside a ROS package would make a user choose to give a version specifier here. If the generator is installed from a deb/rpm and the packaged version doesn't meet a user's requirements, then they need to build the generator from source. If they're building the generator from source, then they get to pick the version by checking out the one they want to build.
Have a look at 4c39003 to see how much less code there is when the PEP 440 specifiers are removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the generator is installed from a deb/rpm and the packaged version doesn't meet a user's requirements, then they need to build the generator from source. If they're building the generator from source, then they get to pick the version by checking out the one they want to build.
That's absolutely correct whenever you have full control over sources. But it quickly becomes unmanageable as you integrate third-party code or start building software layers on top.
Let's say you want to use some third-party packages: foo
and bar
. Both have the same dependencies at the same version. Both use interfaces from a baz_msgs
package at its 1.0.0
version. And yet, it may not be possible to use them in your workspace, or even together at all. Which baz_msgs
API/ABI do these packages depend on? Version 1.0.0
only tells half the story. That exact same baz_msgs
package could exhibit completely different API/ABIs depending on the state of the workspace it was built in.
Today, we "ensure" consistency by resting on the assumption that no API/ABI breaks will (should?) occur within a ROS distro, and thus no API/ABI break will occur in code generated by released generators. In a sense, we're (implicitly) versioning packages by distro: package foo
depends on package baz_msgs
at version 1.0.0
in Foxy, but breaks for version 1.0.0
in Rolling because a relevant generator had a major version bump.
IMHO spooky actions at a distance like these are brittle (and break semver
if I'm not mistaken).
I don't see how that or generating messages inside or outside a ROS package would make a user choose to give a version specifier here.
By pinning a version specifier, you can make sure interfaces API/ABIs will stay the same (or fail to build with a self explanatory message) regardless of the state of workspace (and underlays).
An alternative solution would be to have each package that generates interfaces explicitly depend on the versions of the generators it uses. And drop all checks. It's less friendly for non-ROS packages consuming ROS, but it's an option.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As per offline discussion with @sloretz, the issue I'm trying to address is not exclusive to generators... A C++ package can see its public ABI changed if one of its dependencies changed its own -- all without modifying a single source file. Which kinda explains why our buildfarm has to use package versions, distro names, and build timestamps to ensure binaries come out OK.
It doesn't make sense to have an ad-hoc solution for the rosidl
pipeline alone. I'll strip away version specifiers. Thanks for the discussion Shane !!
Still, we should have a discussion about this @ros2/team. It's not semantic versioning what we do, or at least it's not preventing the dependency hell it is meant to prevent. Relevant discussion at semver/semver#341.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
No need for ` command`, rosidl has contol over all sub commands and can make sure none adds an argument called _command. Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
BTW made a few minor tweaks here and there in 9036d3b. |
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm almost done reviewing, just submitting a couple thoughts before lunch.
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. One last recommendation for a corner case, but I don't think it's blocking.
raise RuntimeError(msg) | ||
logger.warning(msg) | ||
continue | ||
if specs and name not in specs: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would recommend putting this check first so that if someone asked for the "c" entrypoint but there were two "cpp" entrypoints then it could still use the "c" extension without being blocked by the problem with the duplicate "cpp" extensions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like a great idea. See 0128d9d.
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
So, |
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
Finally. @sloretz would you mind to take another look? |
Ok, going in. Thanks for the thorough review @sloretz! |
Connected to #565. This patch puts the basic functionality for an extensible
rosidl generate
CLI in place. Largely inspired in ros2/ros2cli (minus troublesome dependencies likerclpy
).CI up to
rosidl_cli
: