-
Notifications
You must be signed in to change notification settings - Fork 4k
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
[WIP] Add import_prefix
and strip_import_prefix
to proto_library
.
#5536
[WIP] Add import_prefix
and strip_import_prefix
to proto_library
.
#5536
Conversation
Pushed the in-progress ProtoInfo provider work so you can see where I'm stuck. My rough plan is:
|
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.
(/cc @laurentlb for someone who (rightfully) wants to create a new Java `TransitiveInfoProvider and is having difficulties with it -- it really is not trivial, I myself also usually resort to copy-paste programming...)
|
||
@Immutable | ||
@AutoCodec | ||
public final class ProtoInfo extends NativeInfo implements ProtoInfoApi<Artifact> { |
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.
Why do you need to create a new provider? There is already one that tells which source files a proto_library
provides, ProtoSourcesProvider
.
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.
ProtoSourcesProvider
doesn't seem to be exactly what I want here -- it describes only the .proto source files, but doesn't have information at the target level.
I'm thinking a ProtoInfo
provider would have a srcs :: ProtoSourcesProvider
, and also provide fields to discover what the prefix handling should be.
Alternatively, we could try adding info into ProtoSourcesProvider
such that each source is no longer just a file
, but a (path, file)
tuple.
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.
Ooh, looking through this again I remember the other reasons I started on a new provider:
ProtoSourcesProvider
is an "old-style" provider, so it's not possible to return it from a Skylark rule (ProtoSourcesProvider is not available as a Provider #3701).- The relationship between
ProtoSourcesProviderApi
andProtoSourcesProvider
is pretty unclear to me -- why is there this separation? The former is generic across implementations ofFileApi
, are there any cases where we'd use something other thanArtifact
as the file type of a protobuf library target?
I suspect the second is more of an Enterprise™ Java™ Best™ Practice™ thing vs a Bazel-specific issue, but it does aggravate the lack of documentation when I'm not able to figure out why there are multiple Java types that seem related to the same Skylark type.
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.
Well, I'm also somewhat bemused at the presence of *Api
classes, but I assume the Starlark folks know what they are doing, so let's follow that example.
I think that the best course of action here is to turn ProtoSourcesProvider
into a new-style provider. The old and new styles can coexist for an indefinite period of time so you would not need to do the shave the "deprecate the old-style API for ProtoSourcesProvider
" yak. Adding the new-style (provider-based) API does require a bit of boilerplate, but following the footsteps of JavaRuntimeInfo
and JavaInfo
should be pretty straightforward: you make ProtoSourcesProvider
a subclass of BuiltinProvider
, add a static field called PROVIDER
to it, then add a method to proto_common
(just like JavaSkylarkCommon#getJavaProvider
) that returns the provider.
(Unfortunately, neither JavaInfo
nor JavaRuntimeInfo
is 100% correct: the former makes JavaInfo
a top-level symbol, which is wrong, and the latter uses one of the deprecated APIs for Skylark providers defined in Java)
Looking at the code, there are plenty of places where we add the result of ProtoSourcesProvider#getTransitiveProtoSources()
to nested sets so it's probably better to either another field returning a nested set of (path, artifact) tuples or, even better, that only for those artifacts whose path is different from the default one. Nested sets can contain anything that has a #equals()
/ #hashCode()
method. In particular, AutoValue
works.
@@ -596,13 +605,27 @@ static void addIncludeMapArguments( | |||
* </code> | |||
*/ | |||
private static String getPathIgnoringRepository(Artifact artifact) { | |||
// FIXME(jmillikin): How can I plumb `ruleContext` through the @AutoCodec closures |
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.
RuleContext
should not be put into a TransitiveInfoProvider
. Its lifetime is limited to the analysis of that particular configured target. Why do you need to do that? For the getPathIgnoringRepository()
call?
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.
Yes, I wanted some way to get at the properties of the rule (which AIUI are only part of RuleContext)
.
|
||
public static final ProtoInfoProvider PROVIDER = new ProtoInfoProvider(); | ||
|
||
public static final ProtoInfo EMPTY = ProtoInfo.Builder.create().build(); |
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.
That said, this looks like the right way to create a Java provider. How does this not work?
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.
It just never appears in the Skylark context.
There's no error messages that I can see, and I don't see any sort of central registry of Skylark symbols, so I'm at a bit of a loss about why Skylark doesn't see this type.
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.
Oh, that I know :) Just adding a PROVIDER
field is not enough, you also need to tell Bazel that this should be available as a symbol. Here is now Java does it:
Add a proto_common
top-level symbol should be pretty straightforward by following that template. Let me know if it still doesn't work!
commandLine.addAll(VectorArg.of(transitiveImports).mapped(EXPAND_TRANSITIVE_IMPORT_ARG)); | ||
for (Artifact proto : transitiveImports) { | ||
commandLine.addFormatted( | ||
"-I%s=%s", getPathIgnoringRepository(ruleContext, proto), proto.getExecPathString()); |
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.
Why do you compute the path a particular proto file is accessible at here instead of the rule in whose srcs
attribute the proto file is? That way, you would not need a RuleContext
.
And fortunately, ProtoSourcesProvider
(or your new provider) already providers a place to do that. What I would do is to make ProtoSourcesProvider#getTransitiveProtoSources()
a NestedSet
of not artifacts, but of import path - artifact pairs and compute the import path of a .proto
file in the proto_library()
rule that owns it. Alternatively, if you think that the version without import path munging is the common case, have two methods: one for default import paths (without import_prefix
or strip_import_prefix
attributes) and one for changed import paths.
Thinking again, the latter is preferable because it makes merging this code a bit easier due to the existing users of that method.
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 was looking for where protoc
was invoked, then trying to modify code closest to that invocation (for testing). It's certainly likely that this logic belongs in some other part of the build graph.
Are there any constraints on what types a NestedSet
can contain? I'm not very familiar with Java, but IIRC there's no generic tuple types -- do I need to find a ProtoSourcePath
type to handle that? Does it need to implement some sort of hashable interface to be put in a NestedSet?
@@ -77,7 +79,14 @@ public RuleClass build(RuleClass.Builder builder, final RuleDefinitionEnvironmen | |||
the source root will be the workspace directory (default). | |||
<!-- #END_BLAZE_RULE.ATTRIBUTE --> */ | |||
.add(attr("proto_source_root", STRING)) | |||
/* <!-- #BLAZE_RULE(proto_library).ATTRIBUTE(import_prefix) --> | |||
<!-- #END_BLAZE_RULE.ATTRIBUTE --> */ | |||
.add(attr("import_prefix", STRING)) |
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.
Extra kudos for making the logic consistent with what cc_library
does :)
Sorry for the delayed response, having some task-switch overhead. Comments inline. |
Any updates here? I think this PR will fix a major pain point of working with protos in Bazel. They inherit the same problems from the use of importing/including a specified path that C/C++ headers do. So adopting the same approach to amending the import path as the C/C++ rules seems like a great idea. |
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.
Erm, I apologize for the delay. This got lost in my inbox somewhere, not very professional.
|
||
public static final ProtoInfoProvider PROVIDER = new ProtoInfoProvider(); | ||
|
||
public static final ProtoInfo EMPTY = ProtoInfo.Builder.create().build(); |
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.
Oh, that I know :) Just adding a PROVIDER
field is not enough, you also need to tell Bazel that this should be available as a symbol. Here is now Java does it:
Add a proto_common
top-level symbol should be pretty straightforward by following that template. Let me know if it still doesn't work!
|
||
@Immutable | ||
@AutoCodec | ||
public final class ProtoInfo extends NativeInfo implements ProtoInfoApi<Artifact> { |
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.
Well, I'm also somewhat bemused at the presence of *Api
classes, but I assume the Starlark folks know what they are doing, so let's follow that example.
I think that the best course of action here is to turn ProtoSourcesProvider
into a new-style provider. The old and new styles can coexist for an indefinite period of time so you would not need to do the shave the "deprecate the old-style API for ProtoSourcesProvider
" yak. Adding the new-style (provider-based) API does require a bit of boilerplate, but following the footsteps of JavaRuntimeInfo
and JavaInfo
should be pretty straightforward: you make ProtoSourcesProvider
a subclass of BuiltinProvider
, add a static field called PROVIDER
to it, then add a method to proto_common
(just like JavaSkylarkCommon#getJavaProvider
) that returns the provider.
(Unfortunately, neither JavaInfo
nor JavaRuntimeInfo
is 100% correct: the former makes JavaInfo
a top-level symbol, which is wrong, and the latter uses one of the deprecated APIs for Skylark providers defined in Java)
Looking at the code, there are plenty of places where we add the result of ProtoSourcesProvider#getTransitiveProtoSources()
to nested sets so it's probably better to either another field returning a nested set of (path, artifact) tuples or, even better, that only for those artifacts whose path is different from the default one. Nested sets can contain anything that has a #equals()
/ #hashCode()
method. In particular, AutoValue
works.
+ "more providers created with different ijar requests </li> </ul>", | ||
structField = true | ||
) | ||
public SkylarkNestedSet getCompileTimeJars(); |
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.
Why all these Java-specific things in ProtoInfo
?
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.
Dunno, this isn't my code. I think it got picked up by a rebase.
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.
It looks like copypasta from JavaInfoApi.java
.
Closing in favor of Lukács's branch. |
Fixes #3867
WIP because I'd like feedback on this approach, and it's not ready to merge yet.
@AutoCodec
)adjustImportPath
once its behavior is settled.