@@ -126,9 +126,12 @@ def __init__(self, strategy):
126126 # type: (Union[_ManifestBased, _DirectoryBased]) -> None
127127 self ._strategy = strategy
128128 self ._python_runfiles_root = _FindPythonRunfilesRoot ()
129+ self ._repo_mapping = _ParseRepoMapping (
130+ strategy .RlocationChecked ("_repo_mapping" )
131+ )
129132
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]
132135 """Returns the runtime path of a runfile.
133136
134137 Runfiles are data-dependencies of Bazel-built binaries and tests.
@@ -141,6 +144,13 @@ def Rlocation(self, path):
141144
142145 Args:
143146 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.
144154 Returns:
145155 the path to the runfile, which the caller should check for existence, or
146156 None if the method doesn't know about this runfile
@@ -165,7 +175,31 @@ def Rlocation(self, path):
165175 raise ValueError ('path is absolute without a drive letter: "%s"' % path )
166176 if os .path .isabs (path ):
167177 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 )
169203
170204 def EnvVars (self ):
171205 # type: () -> Dict[str, str]
@@ -254,6 +288,31 @@ def _FindPythonRunfilesRoot():
254288 return root
255289
256290
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+
257316class _ManifestBased (object ):
258317 """`Runfiles` strategy that parses a runfiles-manifest to look up runfiles."""
259318
0 commit comments