Problem
Module.WithAmbiguousTargetsResolved() silently drops ambiguous headers without allowing gazelle to decide what to do with them. The headers won't be included in bzldep-index.json.
@jayconrod @WojciechMazur , I'm interested in your opinions about the problem.
Real-world example
E.g., every source in grpc/examples/cpp/helloworld contains #include <grpcpp/grpcpp.h>.
Unfortunately, this header is exposed by two targets in grpc module:
$ bazel query --keep_going 2>/dev/null '
rdeps(//..., "//:include/grpcpp/grpcpp.h", 1)
intersect kind(cc_library, //...)
intersect attr(visibility, //visibility:public, //...)
except attr(testonly, True, //...)
'
//:grpc++
//:grpc++_unsecure
Core differences are as follows:
|
//:grpc++ |
//:grpc++_unsecure |
| extra hdrs |
//:src/cpp/client/secure_credentials.h //:src/cpp/common/secure_auth_context.h //:src/cpp/server/secure_server_credentials.h |
🚫 |
| extra deps |
//:gpr_public_hdrs //:grpc++_base //:grpc++_xds_client //:grpc++_xds_server //:ref_counted_ptr //src/core:slice @com_google_absl//absl/base:core_headers @com_google_absl//absl/types:span @com_google_protobuf//:protobuf @com_google_protobuf//src/google/protobuf/io:io |
//:channel_arg_names //:gpr //:grpc++_base_unsecure //:grpc++_codegen_proto //:grpc_security_base //:grpc_unsecure //src/core:grpc_insecure_credentials |
In this particular case, we can assume that the user might prefer //:grpc++ over //:grpc++_unsecure because //:grpc++ is more generic. And usually, insecure transport is used only during the prototyping phase, quickly evolving later to some encryption method. Sometimes the choice is less obvious.
Anyway, something should be chosen, because almost always the user wants to set up a server and/or a client. Putting appopriate cc_grpc_library in deps won't be sufficient then.
Solution
- BCR indexer should include ambiguous headers in the output JSON file.
- Gazelle should later react somehow to this.
Questions
File format
The current file format is:
{
"hdr1.h": "@repo1//:target1",
"hdr2.h": "@repo2//:target2"
}
cannot be simply extended and has to be changed.
I see 2 approaches to achieve this.
Lists instead of single values
{
"hdr1.h": ["@repo1//:target1"],
"hdr2.h": ["@repo2//:target2"],
"ambiguous_header.h": ["@other_repo//:target1", "@other_repo//:target2"]
}
Root-level distinction
{
"unique" : {
"hdr1.h": ["@repo1//:target1"],
"hdr2.h": ["@repo2//:target2"]
},
"ambiguous": {
"ambiguous_header.h": ["@other_repo//:target1", "@other_repo//:target2"]
}
}
Gazelle behavior
How should the gazelle react in case of ambiguity? Should we provide some heuristics for automatic choice? We should consider 2 scenarios:
Dependency already specified
Referring to the example above, we should respect the user's decision when one of //:grpc++ or //:grpc++_unsecure is already put in deps of a rule. Whatever was chosen, we should not modify the deps, requiring the user to use # keep directive. No warnings should be displayed.
No dependency specified yet
It happens usually when there's no rule, and we generate it from scratch for the first time. Or, less often, the rule exists, but none of the ambiguous dependencies are specified.
What should we do? I think we should at least display a warning.
Should we additionally put one of arbitrarily chosen dependencies into the deps list?
- pros: increasing the chance that the project will be ready to build, minimizing the amount of work required to make everything buildable after the gazelle run
- cons: the user may ignore the displayed warning and forget to think more about the decision; in the opposite approach, the linking error could not be ignored
Cross-module ambiguity
Should we consider only ambiguities within a single module? Or should cross-module ambiguities also be displayed as warnings?
Problem
Module.WithAmbiguousTargetsResolved()silently drops ambiguous headers without allowing gazelle to decide what to do with them. The headers won't be included inbzldep-index.json.@jayconrod @WojciechMazur , I'm interested in your opinions about the problem.
Real-world example
E.g., every source in
grpc/examples/cpp/helloworldcontains#include <grpcpp/grpcpp.h>.Unfortunately, this header is exposed by two targets in grpc module:
Core differences are as follows:
//:src/cpp/common/secure_auth_context.h
//:src/cpp/server/secure_server_credentials.h
//:grpc++_base
//:grpc++_xds_client
//:grpc++_xds_server
//:ref_counted_ptr
//src/core:slice
@com_google_absl//absl/base:core_headers
@com_google_absl//absl/types:span
@com_google_protobuf//:protobuf
@com_google_protobuf//src/google/protobuf/io:io
//:gpr
//:grpc++_base_unsecure
//:grpc++_codegen_proto
//:grpc_security_base
//:grpc_unsecure
//src/core:grpc_insecure_credentials
In this particular case, we can assume that the user might prefer
//:grpc++over//:grpc++_unsecurebecause//:grpc++is more generic. And usually, insecure transport is used only during the prototyping phase, quickly evolving later to some encryption method. Sometimes the choice is less obvious.Anyway, something should be chosen, because almost always the user wants to set up a server and/or a client. Putting appopriate
cc_grpc_libraryindepswon't be sufficient then.Solution
Questions
File format
The current file format is:
{ "hdr1.h": "@repo1//:target1", "hdr2.h": "@repo2//:target2" }cannot be simply extended and has to be changed.
I see 2 approaches to achieve this.
Lists instead of single values
{ "hdr1.h": ["@repo1//:target1"], "hdr2.h": ["@repo2//:target2"], "ambiguous_header.h": ["@other_repo//:target1", "@other_repo//:target2"] }Root-level distinction
{ "unique" : { "hdr1.h": ["@repo1//:target1"], "hdr2.h": ["@repo2//:target2"] }, "ambiguous": { "ambiguous_header.h": ["@other_repo//:target1", "@other_repo//:target2"] } }Gazelle behavior
How should the gazelle react in case of ambiguity? Should we provide some heuristics for automatic choice? We should consider 2 scenarios:
Dependency already specified
Referring to the example above, we should respect the user's decision when one of
//:grpc++or//:grpc++_unsecureis already put indepsof a rule. Whatever was chosen, we should not modify thedeps, requiring the user to use# keepdirective. No warnings should be displayed.No dependency specified yet
It happens usually when there's no rule, and we generate it from scratch for the first time. Or, less often, the rule exists, but none of the ambiguous dependencies are specified.
What should we do? I think we should at least display a warning.
Should we additionally put one of arbitrarily chosen dependencies into the
depslist?Cross-module ambiguity
Should we consider only ambiguities within a single module? Or should cross-module ambiguities also be displayed as warnings?