@@ -14,6 +14,43 @@ def _windows_path_normalize(path):
1414 return path .replace ("/" , "\\ " )
1515 return path
1616
17+ # These 3 helpers are from "@protobuf//bazel/common:proto_common.bzl"
18+ # After migrating, from @rules_proto to @protobuf, they can be replaced by a single load statement.
19+ # See https://github.com/protocolbuffers/protobuf/blob/main/bazel/common/proto_common.bzl .
20+
21+ def _import_virtual_proto_path (path ):
22+ """Imports all paths for virtual imports.
23+
24+ They're of the form:
25+ 'bazel-out/k8-fastbuild/bin/external/foo/e/_virtual_imports/e' or
26+ 'bazel-out/foo/k8-fastbuild/bin/e/_virtual_imports/e'"""
27+ if path .count ("/" ) > 4 :
28+ return "-I%s" % path
29+ return None
30+
31+ def _import_repo_proto_path (path ):
32+ """Imports all paths for generated files in external repositories.
33+
34+ They are of the form:
35+ 'bazel-out/k8-fastbuild/bin/external/foo' or
36+ 'bazel-out/foo/k8-fastbuild/bin'"""
37+ path_count = path .count ("/" )
38+ if path_count > 2 and path_count <= 4 :
39+ return "-I%s" % path
40+ return None
41+
42+ def _import_main_output_proto_path (path ):
43+ """Imports all paths for generated files or source files in external repositories.
44+
45+ They're of the form:
46+ 'bazel-out/k8-fastbuild/bin'
47+ 'external/foo'
48+ '../foo'
49+ """
50+ if path .count ("/" ) <= 2 and path != "." :
51+ return "-I%s" % path
52+ return None
53+
1754# buildifier: disable=function-docstring-header
1855def _protoc_action (ctx , proto_info , outputs ):
1956 """Create an action like
@@ -26,6 +63,13 @@ def _protoc_action(ctx, proto_info, outputs):
2663 """
2764 inputs = depset (proto_info .direct_sources , transitive = [proto_info .transitive_descriptor_sets ])
2865
66+ # ensure that bin_dir doesn't get duplicated in the path
67+ # e.g. by proto_library(strip_import_prefix=...)
68+ proto_root = proto_info .proto_source_root
69+ if proto_root .startswith (ctx .bin_dir .path ):
70+ proto_root = proto_root [len (ctx .bin_dir .path ) + 1 :]
71+ plugin_output = ctx .bin_dir .path + "/" + proto_root
72+
2973 options = dict ({
3074 "keep_empty_files" : True ,
3175 "target" : "js+dts" ,
@@ -40,23 +84,38 @@ def _protoc_action(ctx, proto_info, outputs):
4084 args .add_joined (["--plugin" , "protoc-gen-es" , _windows_path_normalize (ctx .executable .protoc_gen_es .path )], join_with = "=" )
4185 for (key , value ) in options .items ():
4286 args .add_joined (["--es_opt" , key , value ], join_with = "=" )
43- args .add_joined (["--es_out" , ctx . bin_dir . path ], join_with = "=" )
87+ args .add_joined (["--es_out" , plugin_output ], join_with = "=" )
4488
4589 if ctx .attr .gen_connect_es :
4690 args .add_joined (["--plugin" , "protoc-gen-connect-es" , _windows_path_normalize (ctx .executable .protoc_gen_connect_es .path )], join_with = "=" )
4791 for (key , value ) in options .items ():
4892 args .add_joined (["--connect-es_opt" , key , value ], join_with = "=" )
49- args .add_joined (["--connect-es_out" , ctx . bin_dir . path ], join_with = "=" )
93+ args .add_joined (["--connect-es_out" , plugin_output ], join_with = "=" )
5094
5195 if ctx .attr .gen_connect_query :
5296 args .add_joined (["--plugin" , "protoc-gen-connect-query" , _windows_path_normalize (ctx .executable .protoc_gen_connect_query .path )], join_with = "=" )
5397 for (key , value ) in options .items ():
5498 args .add_joined (["--connect-query_opt" , key , value ], join_with = "=" )
55- args .add_joined (["--connect-query_out" , ctx . bin_dir . path ], join_with = "=" )
99+ args .add_joined (["--connect-query_out" , plugin_output ], join_with = "=" )
56100
57101 args .add ("--descriptor_set_in" )
58102 args .add_joined (proto_info .transitive_descriptor_sets , join_with = ctx .configuration .host_path_separator )
59103
104+ # Also from "@protobuf//bazel/common:proto_common.bzl":
105+ #
106+ # Protoc searches for .protos -I paths in order they are given and then
107+ # uses the path within the directory as the package.
108+ # This requires ordering the paths from most specific (longest) to least
109+ # specific ones, so that no path in the list is a prefix of any of the
110+ # following paths in the list.
111+ # For example: 'bazel-out/k8-fastbuild/bin/external/foo' needs to be listed
112+ # before 'bazel-out/k8-fastbuild/bin'. If not, protoc will discover file under
113+ # the shorter path and use 'external/foo/...' as its package path.
114+ args .add_all (proto_info .transitive_proto_path , map_each = _import_virtual_proto_path )
115+ args .add_all (proto_info .transitive_proto_path , map_each = _import_repo_proto_path )
116+ args .add_all (proto_info .transitive_proto_path , map_each = _import_main_output_proto_path )
117+ args .add ("-I." )
118+
60119 args .add_all (proto_info .direct_sources )
61120
62121 proto_toolchain_enabled = len (proto_toolchains .use_toolchain (_PROTO_TOOLCHAIN_TYPE )) > 0
@@ -81,10 +140,17 @@ def _declare_outs(ctx, info, ext):
81140 if ctx .attr .gen_connect_es :
82141 outs .extend (proto_common .declare_generated_files (ctx .actions , info , "_connect" + ext ))
83142 if ctx .attr .gen_connect_query :
143+ proto_sources = info .direct_sources
144+ proto_source_map = {src .basename : src for src in proto_sources }
145+
146+ # FIXME: we should refer to source files via labels instead of filenames
84147 for proto , services in ctx .attr .gen_connect_query_service_mapping .items ():
148+ if not proto in proto_source_map :
149+ fail ("{} is not provided by proto_srcs" .format (proto ))
150+ src = proto_source_map .get (proto )
151+ prefix = proto .replace (".proto" , "" )
85152 for service in services :
86- prefix = proto .replace (".proto" , "" )
87- outs .append (ctx .actions .declare_file ("{}-{}_connectquery{}" .format (prefix , service , ext )))
153+ outs .append (ctx .actions .declare_file ("{}-{}_connectquery{}" .format (prefix , service , ext ), sibling = src ))
88154
89155 return outs
90156
0 commit comments