@@ -71,7 +71,7 @@ def _proto_library_impl(ctx):
71
71
72
72
proto_path , direct_sources = _create_proto_sources (ctx , srcs , import_prefix , strip_import_prefix )
73
73
descriptor_set = ctx .actions .declare_file (ctx .label .name + "-descriptor-set.proto.bin" )
74
- proto_info = _create_proto_info (direct_sources , deps , proto_path , descriptor_set )
74
+ proto_info = _create_proto_info (direct_sources , deps , proto_path , descriptor_set , bin_dir = ctx . bin_dir . path )
75
75
76
76
_write_descriptor_set (ctx , direct_sources , deps , exports , proto_info , descriptor_set )
77
77
@@ -90,6 +90,35 @@ def _proto_library_impl(ctx):
90
90
),
91
91
]
92
92
93
+ def _remove_sibling_repo (relpath ):
94
+ # Addresses sibling repository layout: ../repo/package/path -> package/path
95
+ if relpath .startswith ("../" ):
96
+ split = relpath .split ("/" , 2 )
97
+ relpath = split [2 ] if len (split ) >= 3 else ""
98
+ return relpath
99
+
100
+ def _from_root (root , relpath ):
101
+ """Constructs an exec path from root to relpath"""
102
+ if not root :
103
+ # `relpath` is a directory with an input source file, the exec path is one of:
104
+ # - when in main repo: `package/path`
105
+ # - when in a external repository: `external/repo/package/path`
106
+ # - with sibling layout: `../repo/package/path`
107
+ return relpath
108
+ else :
109
+ # `relpath` is a directory with a generated file or an output directory:
110
+ # - when in main repo: `{root}/package/path`
111
+ # - when in an external repository: `{root}/external/repo/package/path`
112
+ # - with sibling layout: `{root}/package/path`
113
+ return _join (root , _remove_sibling_repo (relpath ))
114
+
115
+ def _empty_to_dot (path ):
116
+ return path if path else "."
117
+
118
+ def _uniq (iterable ):
119
+ unique_elements = {element : None for element in iterable }
120
+ return list (unique_elements .keys ())
121
+
93
122
def _create_proto_sources (ctx , srcs , import_prefix , strip_import_prefix ):
94
123
"""Transforms Files in srcs to ProtoSourceInfos, optionally symlinking them to _virtual_imports.
95
124
@@ -105,17 +134,13 @@ def _create_proto_sources(ctx, srcs, import_prefix, strip_import_prefix):
105
134
return _symlink_to_virtual_imports (ctx , srcs , import_prefix , strip_import_prefix )
106
135
else :
107
136
# No virtual source roots
108
- direct_sources = []
109
- for src in srcs :
110
- if ctx .label .workspace_name == "" or ctx .label .workspace_root .startswith (".." ):
111
- # source_root == ''|'bazel-out/foo/k8-fastbuild/bin'
112
- source_root = src .root .path
113
- else :
114
- # source_root == ''|'bazel-out/foo/k8-fastbuild/bin' / 'external/repo'
115
- source_root = _join (src .root .path , ctx .label .workspace_root )
116
- direct_sources .append (ProtoSourceInfo (_source_file = src , _proto_path = source_root ))
117
-
118
- return ctx .label .workspace_root if ctx .label .workspace_root else "." , direct_sources
137
+ proto_path = ctx .label .workspace_root # ''|'../repo'|'external/repo'
138
+ direct_sources = [ProtoSourceInfo (
139
+ _source_file = src ,
140
+ _proto_path = _from_root (src .root .path , proto_path ),
141
+ ) for src in srcs ]
142
+
143
+ return proto_path , direct_sources
119
144
120
145
def _join (* path ):
121
146
return "/" .join ([p for p in path if p != "" ])
@@ -127,12 +152,8 @@ def _symlink_to_virtual_imports(ctx, srcs, import_prefix, strip_import_prefix):
127
152
A pair proto_path, directs_sources.
128
153
"""
129
154
virtual_imports = _join ("_virtual_imports" , ctx .label .name )
130
- if ctx .label .workspace_name == "" or ctx .label .workspace_root .startswith (".." ): # siblingRepositoryLayout
131
- # Example: `bazel-out/[repo/]target/bin / pkg / _virtual_imports/name`
132
- proto_path = _join (ctx .genfiles_dir .path , ctx .label .package , virtual_imports )
133
- else :
134
- # Example: `bazel-out/target/bin / repo / pkg / _virtual_imports/name`
135
- proto_path = _join (ctx .genfiles_dir .path , ctx .label .workspace_root , ctx .label .package , virtual_imports )
155
+ proto_path = _join (ctx .label .workspace_root , ctx .label .package , virtual_imports )
156
+ root_proto_path = _from_root (ctx .genfiles_dir .path , proto_path )
136
157
137
158
if ctx .label .workspace_name == "" :
138
159
full_strip_import_prefix = strip_import_prefix
@@ -156,10 +177,10 @@ def _symlink_to_virtual_imports(ctx, srcs, import_prefix, strip_import_prefix):
156
177
target_file = src ,
157
178
progress_message = "Symlinking virtual .proto sources for %{label}" ,
158
179
)
159
- direct_sources .append (ProtoSourceInfo (_source_file = virtual_src , _proto_path = proto_path ))
180
+ direct_sources .append (ProtoSourceInfo (_source_file = virtual_src , _proto_path = root_proto_path ))
160
181
return proto_path , direct_sources
161
182
162
- def _create_proto_info (direct_sources , deps , proto_path , descriptor_set ):
183
+ def _create_proto_info (direct_sources , deps , proto_path , descriptor_set , bin_dir ):
163
184
"""Constructs ProtoInfo."""
164
185
165
186
# Construct ProtoInfo
@@ -173,10 +194,15 @@ def _create_proto_info(direct_sources, deps, proto_path, descriptor_set):
173
194
transitive = [dep .transitive_sources for dep in deps ],
174
195
order = "preorder" ,
175
196
)
197
+
198
+ # There can be up more than 1 direct proto_paths, for example when there's
199
+ # a generated and non-generated .proto file in srcs
200
+ root_paths = _uniq ([src ._source_file .root .path for src in direct_sources ])
176
201
transitive_proto_path = depset (
177
- direct = [proto_path ],
202
+ direct = [_empty_to_dot ( _from_root ( root , proto_path )) for root in root_paths ],
178
203
transitive = [dep .transitive_proto_path for dep in deps ],
179
204
)
205
+
180
206
if direct_sources :
181
207
check_deps_sources = depset (direct = [src ._source_file for src in direct_sources ])
182
208
else :
@@ -198,7 +224,8 @@ def _create_proto_info(direct_sources, deps, proto_path, descriptor_set):
198
224
transitive_sources = transitive_sources ,
199
225
direct_descriptor_set = descriptor_set ,
200
226
transitive_descriptor_sets = transitive_descriptor_sets ,
201
- proto_source_root = proto_path ,
227
+ #TODO(b/281812523): remove bin_dir from proto_source_root (when users assuming it's there are migrated)
228
+ proto_source_root = _empty_to_dot (_from_root (bin_dir , proto_path ) if "_virtual_imports/" in proto_path else _remove_sibling_repo (proto_path )),
202
229
transitive_proto_path = transitive_proto_path ,
203
230
check_deps_sources = check_deps_sources ,
204
231
transitive_imports = transitive_sources ,
@@ -208,7 +235,10 @@ def _create_proto_info(direct_sources, deps, proto_path, descriptor_set):
208
235
)
209
236
210
237
def _get_import_path (proto_source ):
211
- return paths .relativize (proto_source ._source_file .path , proto_source ._proto_path )
238
+ proto_path = proto_source ._proto_path
239
+ if proto_path and not proto_source ._source_file .path .startswith (proto_path + "/" ):
240
+ fail ("Bad proto_path %s for proto %s" % (proto_path , proto_source ._source_file .path ))
241
+ return proto_source ._source_file .path .removeprefix (proto_path + "/" )
212
242
213
243
def _write_descriptor_set (ctx , direct_sources , deps , exports , proto_info , descriptor_set ):
214
244
"""Writes descriptor set."""
0 commit comments