|  | // Copyright 2014 The Bazel Authors. All rights reserved. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //    http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  | package com.google.devtools.build.lib.cmdline; | 
|  |  | 
|  | import static com.google.devtools.build.lib.cmdline.LabelParser.validateAndProcessTargetName; | 
|  |  | 
|  | import com.google.common.base.Preconditions; | 
|  | import com.google.common.collect.ComparisonChain; | 
|  | import com.google.common.collect.ImmutableMap; | 
|  | import com.google.common.collect.ImmutableSet; | 
|  | import com.google.common.collect.Interner; | 
|  | import com.google.devtools.build.docgen.annot.DocCategory; | 
|  | import com.google.devtools.build.lib.actions.CommandLineItem; | 
|  | import com.google.devtools.build.lib.cmdline.LabelParser.Parts; | 
|  | import com.google.devtools.build.lib.concurrent.BlazeInterners; | 
|  | import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; | 
|  | import com.google.devtools.build.lib.concurrent.ThreadSafety.ThreadSafe; | 
|  | import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions; | 
|  | import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; | 
|  | import com.google.devtools.build.lib.vfs.PathFragment; | 
|  | import com.google.devtools.build.skyframe.SkyFunctionName; | 
|  | import com.google.devtools.build.skyframe.SkyKey; | 
|  | import java.util.Arrays; | 
|  | import javax.annotation.Nullable; | 
|  | import net.starlark.java.annot.Param; | 
|  | import net.starlark.java.annot.StarlarkBuiltin; | 
|  | import net.starlark.java.annot.StarlarkMethod; | 
|  | import net.starlark.java.eval.Printer; | 
|  | import net.starlark.java.eval.StarlarkSemantics; | 
|  | import net.starlark.java.eval.StarlarkThread; | 
|  | import net.starlark.java.eval.StarlarkValue; | 
|  |  | 
|  | /** | 
|  | * A class to identify a BUILD target. All targets belong to exactly one package. The name of a | 
|  | * target is called its label. A typical label looks like this: //dir1/dir2:target_name where | 
|  | * 'dir1/dir2' identifies the package containing a BUILD file, and 'target_name' identifies the | 
|  | * target within the package. | 
|  | * | 
|  | * <p>Parsing is robust against bad input, for example, from the command line. | 
|  | */ | 
|  | @StarlarkBuiltin(name = "Label", category = DocCategory.BUILTIN, doc = "A BUILD target identifier.") | 
|  | @AutoCodec | 
|  | @Immutable | 
|  | @ThreadSafe | 
|  | public final class Label implements Comparable<Label>, StarlarkValue, SkyKey, CommandLineItem { | 
|  | /** | 
|  | * Package names that aren't made relative to the current repository because they mean special | 
|  | * things to Bazel. | 
|  | */ | 
|  | private static final ImmutableSet<String> ABSOLUTE_PACKAGE_NAMES = | 
|  | ImmutableSet.of( | 
|  | // Used for select's `//conditions:default` label (not a target) | 
|  | "conditions", | 
|  | // Used for the public and private visibility labels (not targets) | 
|  | "visibility", | 
|  | // There is only one //external package | 
|  | LabelConstants.EXTERNAL_PACKAGE_NAME.getPathString()); | 
|  |  | 
|  | // Intern "__pkg__" and "__subpackages__" pseudo-targets, which appears in labels used for | 
|  | // visibility specifications. This saves a couple tenths of a percent of RAM off the loading | 
|  | // phase. Note that general interning of all values for `name` is *not* beneficial. See | 
|  | // Google-internal cl/386077913 and cl/185394812 for more context. | 
|  | private static final String PKG_VISIBILITY_NAME = "__pkg__"; | 
|  | private static final String SUBPACKAGES_VISIBILITY_NAME = "__subpackages__"; | 
|  |  | 
|  | public static final SkyFunctionName TRANSITIVE_TRAVERSAL = | 
|  | SkyFunctionName.createHermetic("TRANSITIVE_TRAVERSAL"); | 
|  |  | 
|  | private static final Interner<Label> LABEL_INTERNER = BlazeInterners.newWeakInterner(); | 
|  |  | 
|  | // TODO(b/200024947): Make this public. | 
|  | /** | 
|  | * Parses a raw label string that contains the canonical form of a label. It must be of the form | 
|  | * {@code [@repo]//foo/bar[:quux]}. If the {@code @repo} part is present, it must be a canonical | 
|  | * repo name, otherwise the label will be assumed to be in the main repo. | 
|  | */ | 
|  | private static Label parseCanonical(String raw) throws LabelSyntaxException { | 
|  | Parts parts = Parts.parse(raw); | 
|  | parts.checkPkgIsAbsolute(); | 
|  | RepositoryName repoName = | 
|  | parts.repo == null | 
|  | ? RepositoryName.MAIN | 
|  | : RepositoryName.createFromValidStrippedName(parts.repo); | 
|  | return createUnvalidated( | 
|  | PackageIdentifier.create(repoName, PathFragment.create(parts.pkg)), parts.target); | 
|  | } | 
|  |  | 
|  | /** Computes the repo name for the label, within the context of a current repo. */ | 
|  | private static RepositoryName computeRepoNameWithRepoContext( | 
|  | Parts parts, RepositoryName currentRepo, RepositoryMapping repoMapping) { | 
|  | if (parts.repo == null) { | 
|  | // Certain package names when used without a "@" part are always absolutely in the main repo, | 
|  | // disregarding the current repo and repo mappings. | 
|  | return ABSOLUTE_PACKAGE_NAMES.contains(parts.pkg) ? RepositoryName.MAIN : currentRepo; | 
|  | } | 
|  | // TODO(b/200024947): Make repo mapping take a string and return a RepositoryName. | 
|  | return repoMapping.get(RepositoryName.createFromValidStrippedName(parts.repo)); | 
|  | } | 
|  |  | 
|  | // TODO(b/200024947): Make this public. | 
|  | /** | 
|  | * Parses a raw label string within the context of a current repo. It must be of the form {@code | 
|  | * [@repo]//foo/bar[:quux]}. If the {@code @repo} part is present, it will undergo {@code | 
|  | * repoMapping}, otherwise the label will be assumed to be in {@code currentRepo}. | 
|  | */ | 
|  | private static Label parseWithRepoContext( | 
|  | String raw, RepositoryName currentRepo, RepositoryMapping repoMapping) | 
|  | throws LabelSyntaxException { | 
|  | Parts parts = Parts.parse(raw); | 
|  | parts.checkPkgIsAbsolute(); | 
|  | RepositoryName repoName = computeRepoNameWithRepoContext(parts, currentRepo, repoMapping); | 
|  | return createUnvalidated( | 
|  | PackageIdentifier.create(repoName, PathFragment.create(parts.pkg)), parts.target); | 
|  | } | 
|  |  | 
|  | // TODO(b/200024947): Make this public. | 
|  | /** | 
|  | * Parses a raw label string within the context of a current package. It can be of a | 
|  | * package-relative form ({@code :quux}). Otherwise, it must be of the form {@code | 
|  | * [@repo]//foo/bar[:quux]}. If the {@code @repo} part is present, it will undergo {@code | 
|  | * repoMapping}, otherwise the label will be assumed to be in the repo of {@code | 
|  | * packageIdentifier}. | 
|  | */ | 
|  | private static Label parseWithPackageContext( | 
|  | String raw, PackageIdentifier packageIdentifier, RepositoryMapping repoMapping) | 
|  | throws LabelSyntaxException { | 
|  | Parts parts = Parts.parse(raw); | 
|  | // pkg is either absolute or empty | 
|  | if (!parts.pkg.isEmpty()) { | 
|  | parts.checkPkgIsAbsolute(); | 
|  | } | 
|  | RepositoryName repoName = | 
|  | computeRepoNameWithRepoContext(parts, packageIdentifier.getRepository(), repoMapping); | 
|  | PathFragment pkgFragment = | 
|  | parts.pkgIsAbsolute | 
|  | ? PathFragment.create(parts.pkg) | 
|  | : packageIdentifier.getPackageFragment(); | 
|  | return createUnvalidated(PackageIdentifier.create(repoName, pkgFragment), parts.target); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Factory for Labels from absolute string form. e.g. | 
|  | * | 
|  | * <pre> | 
|  | * //foo/bar | 
|  | * //foo/bar:quux | 
|  | * {@literal @}foo | 
|  | * {@literal @}foo//bar | 
|  | * {@literal @}foo//bar:baz | 
|  | * </pre> | 
|  | * | 
|  | * <p>Labels that don't begin with a repository name are considered to be in the main repository, | 
|  | * so for instance {@code //foo/bar} will turn into {@code @//foo/bar}. | 
|  | * | 
|  | * <p>Labels that begin with a repository name will undergo {@code repositoryMapping}. | 
|  | * | 
|  | * @param absName label-like string to be parsed | 
|  | * @param repositoryMapping map of repository names from the local name found in the current | 
|  | *     repository to the global name declared in the main repository | 
|  | */ | 
|  | // TODO(b/200024947): Remove this. | 
|  | public static Label parseAbsolute(String absName, RepositoryMapping repositoryMapping) | 
|  | throws LabelSyntaxException { | 
|  | Preconditions.checkNotNull(repositoryMapping); | 
|  | return parseWithRepoContext(absName, RepositoryName.MAIN, repositoryMapping); | 
|  | } | 
|  |  | 
|  | // TODO(b/200024947): Remove this. | 
|  | public static Label parseAbsolute( | 
|  | String absName, ImmutableMap<RepositoryName, RepositoryName> repositoryMapping) | 
|  | throws LabelSyntaxException { | 
|  | return parseAbsolute(absName, RepositoryMapping.createAllowingFallback(repositoryMapping)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Alternate factory method for Labels from absolute strings. This is a convenience method for | 
|  | * cases when a Label needs to be initialized statically, so the declared exception is | 
|  | * inconvenient. | 
|  | * | 
|  | * <p>Do not use this when the argument is not hard-wired. | 
|  | */ | 
|  | // TODO(b/200024947): Remove this. | 
|  | public static Label parseAbsoluteUnchecked(String absName) { | 
|  | try { | 
|  | return parseCanonical(absName); | 
|  | } catch (LabelSyntaxException e) { | 
|  | throw new IllegalArgumentException(e); | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Factory for Labels from separate components. | 
|  | * | 
|  | * @param packageName The name of the package. The package name does <b>not</b> include {@code | 
|  | *     //}. Must be valid according to {@link LabelValidator#validatePackageName}. | 
|  | * @param targetName The name of the target within the package. Must be valid according to {@link | 
|  | *     LabelValidator#validateTargetName}. | 
|  | * @throws LabelSyntaxException if either of the arguments was invalid. | 
|  | */ | 
|  | // TODO(b/200024947): Remove this...? | 
|  | public static Label create(String packageName, String targetName) throws LabelSyntaxException { | 
|  | return createUnvalidated( | 
|  | PackageIdentifier.parse(packageName), | 
|  | validateAndProcessTargetName(packageName, targetName)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Similar factory to above, but takes a package identifier to allow external repository labels to | 
|  | * be created. | 
|  | */ | 
|  | // TODO(b/200024947): Remove this...? | 
|  | public static Label create(PackageIdentifier packageId, String targetName) | 
|  | throws LabelSyntaxException { | 
|  | return createUnvalidated( | 
|  | packageId, | 
|  | validateAndProcessTargetName(packageId.getPackageFragment().getPathString(), targetName)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Similar factory to above, but does not perform target name validation. | 
|  | * | 
|  | * <p>Only call this method if you know what you're doing; in particular, don't call it on | 
|  | * arbitrary {@code name} inputs | 
|  | */ | 
|  | @AutoCodec.Instantiator | 
|  | public static Label createUnvalidated(PackageIdentifier packageIdentifier, String name) { | 
|  | String internedName = name; | 
|  | if (internedName.equals(PKG_VISIBILITY_NAME)) { | 
|  | internedName = PKG_VISIBILITY_NAME; | 
|  | } else if (internedName.equals(SUBPACKAGES_VISIBILITY_NAME)) { | 
|  | internedName = SUBPACKAGES_VISIBILITY_NAME; | 
|  | } | 
|  | return LABEL_INTERNER.intern(new Label(packageIdentifier, internedName)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Parses and resolves a label string relative to the given workspace-relative directory. | 
|  | * | 
|  | * <ul> | 
|  | *   <li>If the input is an absolute label, it is parsed as normal. | 
|  | *   <li>If the input starts with a colon or does not contain a colon, the package path is taken | 
|  | *       to be the working directory, and the part after the leading colon (if present) is taken | 
|  | *       to be the target. | 
|  | *   <li>If the input has a non-empty part before a colon, it is appended to the working directory | 
|  | *       to form the package path, and the part after the colon is taken as the target. | 
|  | * </ul> | 
|  | * | 
|  | * <p>Note that this method does not support any of the special syntactic constructs otherwise | 
|  | * supported on the command line, like ":all", "/...", and so on. | 
|  | * | 
|  | * <p>It would be cleaner to use the TargetPatternEvaluator for this resolution, but that is not | 
|  | * possible, because it is sometimes necessary to resolve a relative label before the package path | 
|  | * is setup (maybe not anymore...) | 
|  | * | 
|  | * @throws LabelSyntaxException if the resulting label is not valid | 
|  | */ | 
|  | public static Label parseCommandLineLabel(String raw, PathFragment workspaceRelativePath) | 
|  | throws LabelSyntaxException { | 
|  | Preconditions.checkArgument(!workspaceRelativePath.isAbsolute()); | 
|  | Parts parts = Parts.parse(raw); | 
|  | PathFragment pathFragment; | 
|  | if (parts.repo == null && !parts.pkgIsAbsolute) { | 
|  | pathFragment = workspaceRelativePath.getRelative(parts.pkg); | 
|  | } else { | 
|  | pathFragment = PathFragment.create(parts.pkg); | 
|  | } | 
|  | // TODO(b/200024947): This method will eventually need to take a repo mapping too. | 
|  | RepositoryName repoName = | 
|  | parts.repo == null | 
|  | ? RepositoryName.MAIN | 
|  | : RepositoryName.createFromValidStrippedName(parts.repo); | 
|  | return create(PackageIdentifier.create(repoName, pathFragment), parts.target); | 
|  | } | 
|  |  | 
|  | /** The name and repository of the package. */ | 
|  | private final PackageIdentifier packageIdentifier; | 
|  |  | 
|  | /** The name of the target within the package. Canonical. */ | 
|  | private final String name; | 
|  |  | 
|  | private Label(PackageIdentifier packageIdentifier, String name) { | 
|  | Preconditions.checkNotNull(packageIdentifier); | 
|  | Preconditions.checkNotNull(name); | 
|  |  | 
|  | this.packageIdentifier = packageIdentifier; | 
|  | this.name = name; | 
|  | } | 
|  |  | 
|  | public PackageIdentifier getPackageIdentifier() { | 
|  | return packageIdentifier; | 
|  | } | 
|  |  | 
|  | public RepositoryName getRepository() { | 
|  | return packageIdentifier.getRepository(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns the name of the package in which this rule was declared (e.g. {@code | 
|  | * //file/base:fileutils_test} returns {@code file/base}). | 
|  | */ | 
|  | @StarlarkMethod( | 
|  | name = "package", | 
|  | structField = true, | 
|  | doc = | 
|  | "The package part of this label. " | 
|  | + "For instance:<br>" | 
|  | + "<pre class=language-python>Label(\"//pkg/foo:abc\").package == \"pkg/foo\"</pre>") | 
|  | public String getPackageName() { | 
|  | return packageIdentifier.getPackageFragment().getPathString(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns the execution root for the workspace, relative to the execroot (e.g., for label | 
|  | * {@code @repo//pkg:b}, it will returns {@code external/repo/pkg} and for label {@code //pkg:a}, | 
|  | * it will returns an empty string. | 
|  | * | 
|  | * @deprecated The sole purpose of this method is to implement the workspace_root method. For | 
|  | *     other purposes, use {@link RepositoryName#getExecPath} instead. | 
|  | */ | 
|  | @StarlarkMethod( | 
|  | name = "workspace_root", | 
|  | structField = true, | 
|  | doc = | 
|  | "Returns the execution root for the workspace of this label, relative to the execroot. " | 
|  | + "For instance:<br>" | 
|  | + "<pre class=language-python>Label(\"@repo//pkg/foo:abc\").workspace_root ==" | 
|  | + " \"external/repo\"</pre>", | 
|  | useStarlarkSemantics = true) | 
|  | @Deprecated | 
|  | public String getWorkspaceRootForStarlarkOnly(StarlarkSemantics semantics) { | 
|  | return packageIdentifier | 
|  | .getRepository() | 
|  | .getExecPath(semantics.getBool(BuildLanguageOptions.EXPERIMENTAL_SIBLING_REPOSITORY_LAYOUT)) | 
|  | .toString(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns the path fragment of the package in which this rule was declared (e.g. {@code | 
|  | * //file/base:fileutils_test} returns {@code file/base}). | 
|  | * | 
|  | * <p>This is <b>not</b> suitable for inferring a path under which files related to a rule with | 
|  | * this label will be under the exec root, in particular, it won't work for rules in external | 
|  | * repositories. | 
|  | */ | 
|  | public PathFragment getPackageFragment() { | 
|  | return packageIdentifier.getPackageFragment(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns the label as a path fragment, using the package and the label name. | 
|  | * | 
|  | * <p>Make sure that the label refers to a file. Non-file labels do not necessarily have | 
|  | * PathFragment representations. | 
|  | * | 
|  | * <p>The package's repository is not included in the returned fragment. To account for it, | 
|  | * compose this with {@code #getRepository()#getExecPath}. | 
|  | */ | 
|  | public PathFragment toPathFragment() { | 
|  | // PathFragments are normalized, so if we do this on a non-file target named '.' | 
|  | // then the package would be returned. Detect this and throw. | 
|  | // A target named '.' can never refer to a file. | 
|  | Preconditions.checkArgument(!name.equals(".")); | 
|  | return packageIdentifier.getPackageFragment().getRelative(name); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns the name by which this rule was declared (e.g. {@code //foo/bar:baz} returns {@code | 
|  | * baz}). | 
|  | */ | 
|  | @StarlarkMethod( | 
|  | name = "name", | 
|  | structField = true, | 
|  | doc = | 
|  | "The name of this label within the package. " | 
|  | + "For instance:<br>" | 
|  | + "<pre class=language-python>Label(\"//pkg/foo:abc\").name == \"abc\"</pre>") | 
|  | public String getName() { | 
|  | return name; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Renders this label in canonical form. | 
|  | * | 
|  | * <p>invariant: {@code parseAbsolute(x.toString(), false).equals(x)} | 
|  | */ | 
|  | @Override | 
|  | public String toString() { | 
|  | return getCanonicalForm(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Renders this label in canonical form. | 
|  | * | 
|  | * <p>invariant: {@code parseAbsolute(x.getCanonicalForm(), false).equals(x)} | 
|  | */ | 
|  | public String getCanonicalForm() { | 
|  | return packageIdentifier.getCanonicalForm() + ":" + name; | 
|  | } | 
|  |  | 
|  | public String getUnambiguousCanonicalForm() { | 
|  | return packageIdentifier.getRepository() | 
|  | + "//" | 
|  | + packageIdentifier.getPackageFragment() | 
|  | + ":" | 
|  | + name; | 
|  | } | 
|  |  | 
|  | /** Return the name of the repository label refers to without the leading `at` symbol. */ | 
|  | @StarlarkMethod( | 
|  | name = "workspace_name", | 
|  | structField = true, | 
|  | doc = | 
|  | "The repository part of this label. For instance, " | 
|  | + "<pre class=language-python>Label(\"@foo//bar:baz\").workspace_name" | 
|  | + " == \"foo\"</pre>") | 
|  | public String getWorkspaceName() { | 
|  | return packageIdentifier.getRepository().strippedName(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Renders this label in shorthand form. | 
|  | * | 
|  | * <p>Labels with canonical form {@code //foo/bar:bar} have the shorthand form {@code //foo/bar}. | 
|  | * All other labels have identical shorthand and canonical forms. | 
|  | */ | 
|  | public String toShorthandString() { | 
|  | if (!getPackageFragment().getBaseName().equals(name)) { | 
|  | return toString(); | 
|  | } | 
|  | String repository; | 
|  | if (packageIdentifier.getRepository().isMain()) { | 
|  | repository = ""; | 
|  | } else { | 
|  | repository = packageIdentifier.getRepository().getName(); | 
|  | } | 
|  | return repository + "//" + getPackageFragment(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns a label in the same package as this label with the given target name. | 
|  | * | 
|  | * @throws LabelSyntaxException if {@code targetName} is not a valid target name | 
|  | */ | 
|  | public Label getLocalTargetLabel(String targetName) throws LabelSyntaxException { | 
|  | return create(packageIdentifier, targetName); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Resolves a relative or absolute label name. If given name is absolute, then this method calls | 
|  | * {@link #parseAbsolute}. Otherwise, it calls {@link #getLocalTargetLabel}. | 
|  | * | 
|  | * <p>For example: {@code :quux} relative to {@code //foo/bar:baz} is {@code //foo/bar:quux}; | 
|  | * {@code //wiz:quux} relative to {@code //foo/bar:baz} is {@code //wiz:quux}. | 
|  | * | 
|  | * @param relName the relative label name; must be non-empty. | 
|  | * @param thread the Starlark thread, which must provide a thread-local {@code HasRepoMapping}. | 
|  | */ | 
|  | @StarlarkMethod( | 
|  | name = "relative", | 
|  | doc = | 
|  | "Resolves a label that is either absolute (starts with <code>//</code>) or relative to " | 
|  | + "the current package. If this label is in a remote repository, the argument will " | 
|  | + "be resolved relative to that repository. If the argument contains a repository " | 
|  | + "name, the current label is ignored and the argument is returned as-is, except " | 
|  | + "that the repository name is rewritten if it is in the current repository mapping. " | 
|  | + "Reserved labels will also be returned as-is.<br>" | 
|  | + "For example:<br>" | 
|  | + "<pre class=language-python>\n" | 
|  | + "Label(\"//foo/bar:baz\").relative(\":quux\") == Label(\"//foo/bar:quux\")\n" | 
|  | + "Label(\"//foo/bar:baz\").relative(\"//wiz:quux\") == Label(\"//wiz:quux\")\n" | 
|  | + "Label(\"@repo//foo/bar:baz\").relative(\"//wiz:quux\") == " | 
|  | + "Label(\"@repo//wiz:quux\")\n" | 
|  | + "Label(\"@repo//foo/bar:baz\").relative(\"//visibility:public\") == " | 
|  | + "Label(\"//visibility:public\")\n" | 
|  | + "Label(\"@repo//foo/bar:baz\").relative(\"@other//wiz:quux\") == " | 
|  | + "Label(\"@other//wiz:quux\")\n" | 
|  | + "</pre>" | 
|  | + "<p>If the repository mapping passed in is <code>{'@other' : '@remapped'}</code>, " | 
|  | + "then the following remapping will take place:<br>" | 
|  | + "<pre class=language-python>\n" | 
|  | + "Label(\"@repo//foo/bar:baz\").relative(\"@other//wiz:quux\") == " | 
|  | + "Label(\"@remapped//wiz:quux\")\n" | 
|  | + "</pre>", | 
|  | parameters = { | 
|  | @Param(name = "relName", doc = "The label that will be resolved relative to this one.") | 
|  | }, | 
|  | useStarlarkThread = true) | 
|  | public Label getRelative(String relName, StarlarkThread thread) throws LabelSyntaxException { | 
|  | HasRepoMapping hrm = thread.getThreadLocal(HasRepoMapping.class); | 
|  | return getRelativeWithRemapping(relName, hrm.getRepoMappingForCurrentBzlFile(thread)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * An interface for retrieving a repository mapping that's applicable for the repo containing the | 
|  | * current .bzl file (more precisely, the .bzl file where the function at the innermost Starlark | 
|  | * stack frame lives). | 
|  | * | 
|  | * <p>This has only a single implementation, {@code BazelStarlarkContext}, but we can't mention | 
|  | * that type here because logically it belongs in Bazel, above this package. | 
|  | */ | 
|  | public interface HasRepoMapping { | 
|  | RepositoryMapping getRepoMappingForCurrentBzlFile(StarlarkThread thread); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Resolves a relative or absolute label name. If given name is absolute, then this method calls | 
|  | * {@link #parseAbsolute}. Otherwise, it calls {@link #getLocalTargetLabel}. | 
|  | * | 
|  | * <p>For example: {@code :quux} relative to {@code //foo/bar:baz} is {@code //foo/bar:quux}; | 
|  | * {@code //wiz:quux} relative to {@code //foo/bar:baz} is {@code //wiz:quux}; | 
|  | * {@code @repo//foo:bar} relative to anything will be {@code @repo//foo:bar} if {@code @repo} is | 
|  | * not in {@code repositoryMapping} but will be {@code @other_repo//foo:bar} if there is an entry | 
|  | * {@code @repo -> @other_repo} in {@code repositoryMapping}. | 
|  | * | 
|  | * @param relName the relative label name; must be non-empty | 
|  | * @param repositoryMapping the map of local repository names in external repository to global | 
|  | *     repository names in main repo; can be empty, but not null | 
|  | */ | 
|  | // TODO(b/200024947): Remove this. | 
|  | public Label getRelativeWithRemapping(String relName, RepositoryMapping repositoryMapping) | 
|  | throws LabelSyntaxException { | 
|  | Preconditions.checkNotNull(repositoryMapping); | 
|  | if (relName.isEmpty()) { | 
|  | throw new LabelSyntaxException("empty package-relative label"); | 
|  | } | 
|  | return parseWithPackageContext(relName, packageIdentifier, repositoryMapping); | 
|  | } | 
|  |  | 
|  | // TODO(b/200024947): Remove this. | 
|  | public Label getRelativeWithRemapping( | 
|  | String relName, ImmutableMap<RepositoryName, RepositoryName> repositoryMapping) | 
|  | throws LabelSyntaxException { | 
|  | return getRelativeWithRemapping( | 
|  | relName, RepositoryMapping.createAllowingFallback(repositoryMapping)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public SkyFunctionName functionName() { | 
|  | return TRANSITIVE_TRAVERSAL; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return hashCode(name, packageIdentifier); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Specialization of {@link Arrays#hashCode()} that does not require constructing a 2-element | 
|  | * array. | 
|  | */ | 
|  | private static int hashCode(Object obj1, Object obj2) { | 
|  | int result = 31 + (obj1 == null ? 0 : obj1.hashCode()); | 
|  | return 31 * result + (obj2 == null ? 0 : obj2.hashCode()); | 
|  | } | 
|  |  | 
|  | /** Two labels are equal iff both their name and their package name are equal. */ | 
|  | @Override | 
|  | public boolean equals(Object other) { | 
|  | if (this == other) { | 
|  | return true; | 
|  | } | 
|  | if (!(other instanceof Label)) { | 
|  | return false; | 
|  | } | 
|  | Label otherLabel = (Label) other; | 
|  | // Package identifiers are (weakly) interned so we compare them first. | 
|  | return packageIdentifier.equals(otherLabel.packageIdentifier) && name.equals(otherLabel.name); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Defines the order between labels. | 
|  | * | 
|  | * <p>Labels are ordered primarily by package name and secondarily by target name. Both components | 
|  | * are ordered lexicographically. Thus {@code //a:b/c} comes before {@code //a/b:a}, i.e. the | 
|  | * position of the colon is significant to the order. | 
|  | */ | 
|  | @Override | 
|  | public int compareTo(Label other) { | 
|  | if (this == other) { | 
|  | return 0; | 
|  | } | 
|  | return ComparisonChain.start() | 
|  | .compare(packageIdentifier, other.packageIdentifier) | 
|  | .compare(name, other.name) | 
|  | .result(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns a suitable string for the user-friendly representation of the Label. Works even if the | 
|  | * argument is null. | 
|  | */ | 
|  | public static String print(@Nullable Label label) { | 
|  | return label == null ? "(unknown)" : label.toString(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns a {@link PathFragment} corresponding to the directory in which {@code label} would | 
|  | * reside, if it were interpreted to be a path. | 
|  | */ | 
|  | public static PathFragment getContainingDirectory(Label label) { | 
|  | PathFragment pkg = label.getPackageFragment(); | 
|  | String name = label.name; | 
|  | if (name.equals(".")) { | 
|  | return pkg; | 
|  | } | 
|  | if (PathFragment.isNormalizedRelativePath(name) && !PathFragment.containsSeparator(name)) { | 
|  | // Optimize for the common case of a label like '//pkg:target'. | 
|  | return pkg; | 
|  | } | 
|  | return pkg.getRelative(name).getParentDirectory(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isImmutable() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void repr(Printer printer) { | 
|  | printer.append("Label("); | 
|  | printer.repr(getCanonicalForm()); | 
|  | printer.append(")"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void str(Printer printer) { | 
|  | printer.append(getCanonicalForm()); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String expandToCommandLine() { | 
|  | return getCanonicalForm(); | 
|  | } | 
|  | } |