@@ -126,9 +126,12 @@ def __init__(self, strategy):
126
126
# type: (Union[_ManifestBased, _DirectoryBased]) -> None
127
127
self ._strategy = strategy
128
128
self ._python_runfiles_root = _FindPythonRunfilesRoot ()
129
+ self ._repo_mapping = _ParseRepoMapping (
130
+ strategy .RlocationChecked ("_repo_mapping" )
131
+ )
129
132
130
- def Rlocation (self , path ):
131
- # type: (str) -> Optional[str]
133
+ def Rlocation (self , path , source_repo = None ):
134
+ # type: (str, Optional[str] ) -> Optional[str]
132
135
"""Returns the runtime path of a runfile.
133
136
134
137
Runfiles are data-dependencies of Bazel-built binaries and tests.
@@ -141,6 +144,13 @@ def Rlocation(self, path):
141
144
142
145
Args:
143
146
path: string; runfiles-root-relative path of the runfile
147
+ source_repo: string; optional; the canonical name of the repository
148
+ whose repository mapping should be used to resolve apparent to
149
+ canonical repository names in `path`. If `None` (default), the
150
+ repository mapping of the repository containing the caller of this
151
+ method is used. Explicitly setting this parameter should only be
152
+ necessary for libraries that want to wrap the runfiles library. Use
153
+ `CurrentRepository` to obtain canonical repository names.
144
154
Returns:
145
155
the path to the runfile, which the caller should check for existence, or
146
156
None if the method doesn't know about this runfile
@@ -165,7 +175,31 @@ def Rlocation(self, path):
165
175
raise ValueError ('path is absolute without a drive letter: "%s"' % path )
166
176
if os .path .isabs (path ):
167
177
return path
168
- return self ._strategy .RlocationChecked (path )
178
+
179
+ if source_repo is None and self ._repo_mapping :
180
+ # Look up runfiles using the repository mapping of the caller of the
181
+ # current method. If the repo mapping is empty, determining this
182
+ # name is not necessary.
183
+ source_repo = self .CurrentRepository (frame = 2 )
184
+
185
+ # Split off the first path component, which contains the repository
186
+ # name (apparent or canonical).
187
+ target_repo , _ , remainder = path .partition ("/" )
188
+ if not remainder or (source_repo , target_repo ) not in self ._repo_mapping :
189
+ # One of the following is the case:
190
+ # - not using Bzlmod, so the repository mapping is empty and
191
+ # apparent and canonical repository names are the same
192
+ # - target_repo is already a canonical repository name and does not
193
+ # have to be mapped.
194
+ # - path did not contain a slash and referred to a root symlink,
195
+ # which also should not be mapped.
196
+ return self ._strategy .RlocationChecked (path )
197
+
198
+ # target_repo is an apparent repository name. Look up the corresponding
199
+ # canonical repository name with respect to the current repository,
200
+ # identified by its canonical name.
201
+ target_canonical = self ._repo_mapping [(source_repo , target_repo )]
202
+ return self ._strategy .RlocationChecked (target_canonical + "/" + remainder )
169
203
170
204
def EnvVars (self ):
171
205
# type: () -> Dict[str, str]
@@ -254,6 +288,31 @@ def _FindPythonRunfilesRoot():
254
288
return root
255
289
256
290
291
+ def _ParseRepoMapping (repo_mapping_path ):
292
+ # type: (Optional[str]) -> Dict[Tuple[str, str], str]
293
+ """Parses the repository mapping manifest."""
294
+ # If the repository mapping file can't be found, that is not an error: We
295
+ # might be running without Bzlmod enabled or there may not be any runfiles.
296
+ # In this case, just apply an empty repo mapping.
297
+ if not repo_mapping_path :
298
+ return {}
299
+ try :
300
+ with open (repo_mapping_path , "r" ) as f :
301
+ content = f .read ()
302
+ except FileNotFoundError :
303
+ return {}
304
+
305
+ repo_mapping = {}
306
+ for line in content .split ("\n " ):
307
+ if not line :
308
+ # Empty line following the last line break
309
+ break
310
+ current_canonical , target_local , target_canonical = line .split ("," )
311
+ repo_mapping [(current_canonical , target_local )] = target_canonical
312
+
313
+ return repo_mapping
314
+
315
+
257
316
class _ManifestBased (object ):
258
317
"""`Runfiles` strategy that parses a runfiles-manifest to look up runfiles."""
259
318
0 commit comments