forked from bazelbuild/bazel
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove the concept of a "default" repo name and consolidate label par…
…sing logic This CL started as an effort to clean up references to RepositoryName.DEFAULT. This constant signifies that a label starts without a `@repo` part, and so usually refers to the "current repo". A Label or PackageIdentifier with such a repo name is unusable. This constant is really only ever meaningfully used *while* parsing a label to be relative from another one (Label#getRelativeWithMapping). So we remove it. This also necessarily meant that we have to clean up the label parsing logic. There are multiple places in the Label, PackageIdentifier, and LabelValidator classes where the raw label string is parsed in some way and often they call each other, making the code nigh impossible to follow. This CL consolidates all the label parsing logic into one class, LabelParser, and makes all Label factory methods use this parser instead of their own logic. PiperOrigin-RevId: 415235412
- Loading branch information
1 parent
d7f0724
commit 103ea9d
Showing
47 changed files
with
386 additions
and
406 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
308 changes: 106 additions & 202 deletions
308
src/main/java/com/google/devtools/build/lib/cmdline/Label.java
Large diffs are not rendered by default.
Oops, something went wrong.
195 changes: 195 additions & 0 deletions
195
src/main/java/com/google/devtools/build/lib/cmdline/LabelParser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
// Copyright 2021 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 com.google.common.collect.ImmutableSet; | ||
import com.google.devtools.build.lib.util.StringUtilities; | ||
import com.google.errorprone.annotations.FormatMethod; | ||
import javax.annotation.Nullable; | ||
|
||
/** Utilities to help parse labels. */ | ||
final class LabelParser { | ||
/** | ||
* 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()); | ||
|
||
private LabelParser() {} | ||
|
||
/** | ||
* Contains the parsed elements of a label string. The parts are validated (they don't contain | ||
* invalid characters). See {@link #parse} for valid label patterns. | ||
*/ | ||
static final class Parts { | ||
/** | ||
* The {@code @repo} part of the string (sans the {@literal @}); can be null if it doesn't have | ||
* such a part. | ||
*/ | ||
@Nullable final String repo; | ||
/** Whether the package part of the string is prefixed by double-slash. */ | ||
final boolean pkgIsAbsolute; | ||
/** The package part of the string (sans double-slash, if any). */ | ||
final String pkg; | ||
/** The target part of the string (sans colon). */ | ||
final String target; | ||
/** The original unparsed raw string. */ | ||
final String raw; | ||
|
||
private Parts( | ||
@Nullable String repo, boolean pkgIsAbsolute, String pkg, String target, String raw) { | ||
this.repo = repo; | ||
this.pkgIsAbsolute = pkgIsAbsolute; | ||
this.pkg = pkg; | ||
this.target = target; | ||
this.raw = raw; | ||
} | ||
|
||
private static Parts validateAndCreate( | ||
@Nullable String repo, boolean pkgIsAbsolute, String pkg, String target, String raw) | ||
throws LabelSyntaxException { | ||
return new Parts( | ||
validateAndProcessRepoName(repo, pkg), | ||
pkgIsAbsolute, | ||
validateAndProcessPackageName(pkg, target), | ||
validateAndProcessTargetName(pkg, target), | ||
raw); | ||
} | ||
|
||
/** | ||
* Parses a raw label string into parts. The logic can be summarized by the following table: | ||
* | ||
* {@code | ||
* raw | repo | pkgIsAbsolute | pkg | target | ||
* ---------------------+--------+---------------+-----------+----------- | ||
* foo/bar | null | false | "" | "foo/bar" | ||
* //foo/bar | null | true | "foo/bar" | "bar" | ||
* @repo | "repo" | true | "" | "repo" | ||
* @repo//foo/bar | "repo" | true | "foo/bar" | "bar" | ||
* :quux | null | false | "" | "quux" | ||
* foo/bar:quux | null | false | "foo/bar" | "quux" | ||
* //foo/bar:quux | null | true | "foo/bar" | "quux" | ||
* @repo//foo/bar:quux | "repo" | true | "foo/bar" | "quux" | ||
* } | ||
*/ | ||
static Parts parse(String rawLabel) throws LabelSyntaxException { | ||
@Nullable final String repo; | ||
final int startOfPackage; | ||
final int doubleSlashIndex = rawLabel.indexOf("//"); | ||
final boolean pkgIsAbsolute; | ||
if (rawLabel.startsWith("@")) { | ||
if (doubleSlashIndex < 0) { | ||
// Special case: the label "@foo" is synonymous with "@foo//:foo". | ||
repo = rawLabel.substring(1); | ||
return validateAndCreate( | ||
repo, /*pkgIsAbsolute=*/ true, /*pkg=*/ "", /*target=*/ repo, rawLabel); | ||
} else { | ||
repo = rawLabel.substring(1, doubleSlashIndex); | ||
startOfPackage = doubleSlashIndex + 2; | ||
pkgIsAbsolute = true; | ||
} | ||
} else { | ||
// If the label begins with '//', it's an absolute label. Otherwise, treat it as relative | ||
// (the command-line kind). | ||
pkgIsAbsolute = doubleSlashIndex == 0; | ||
startOfPackage = doubleSlashIndex == 0 ? 2 : 0; | ||
repo = null; | ||
} | ||
|
||
final String pkg; | ||
final String target; | ||
final int colonIndex = rawLabel.indexOf(':', startOfPackage); | ||
if (colonIndex >= 0) { | ||
pkg = rawLabel.substring(startOfPackage, colonIndex); | ||
target = rawLabel.substring(colonIndex + 1); | ||
} else if (pkgIsAbsolute) { | ||
// Special case: the label "[@repo]//foo/bar" is synonymous with "[@repo]//foo/bar:bar". | ||
pkg = rawLabel.substring(startOfPackage); | ||
// The target name is the last package segment (works even if `pkg` contains no slash) | ||
target = pkg.substring(pkg.lastIndexOf('/') + 1); | ||
} else { | ||
// Special case: the label "foo/bar" is synonymous with ":foo/bar". | ||
pkg = ""; | ||
target = rawLabel.substring(startOfPackage); | ||
} | ||
return validateAndCreate(repo, pkgIsAbsolute, pkg, target, rawLabel); | ||
} | ||
|
||
@Nullable | ||
private static String validateAndProcessRepoName(@Nullable String repo, String pkg) | ||
throws LabelSyntaxException { | ||
if (repo == null && ABSOLUTE_PACKAGE_NAMES.contains(pkg)) { | ||
// These package names when used without a "@" part are always absolutely in the main repo. | ||
return ""; | ||
} | ||
if (repo == null) { | ||
return null; | ||
} | ||
String error = RepositoryName.validate('@' + repo); | ||
if (error != null) { | ||
throw syntaxErrorf("invalid repository name '@%s': %s", repo, error); | ||
} | ||
return repo; | ||
} | ||
|
||
private static String validateAndProcessPackageName(String pkg, String target) | ||
throws LabelSyntaxException { | ||
String pkgError = LabelValidator.validatePackageName(pkg); | ||
if (pkgError != null) { | ||
throw syntaxErrorf( | ||
"invalid package name '%s': %s%s", pkg, pkgError, perhapsYouMeantMessage(pkg, target)); | ||
} | ||
return pkg; | ||
} | ||
|
||
void checkPkgIsAbsolute() throws LabelSyntaxException { | ||
if (!pkgIsAbsolute) { | ||
throw syntaxErrorf("invalid label '%s': absolute label must begin with '@' or '//'", raw); | ||
} | ||
} | ||
} | ||
|
||
@FormatMethod | ||
static LabelSyntaxException syntaxErrorf(String format, Object... args) { | ||
return new LabelSyntaxException( | ||
StringUtilities.sanitizeControlChars(String.format(format, args))); | ||
} | ||
|
||
private static String perhapsYouMeantMessage(String pkg, String target) { | ||
return pkg.endsWith('/' + target) ? " (perhaps you meant \":" + target + "\"?)" : ""; | ||
} | ||
|
||
static String validateAndProcessTargetName(String pkg, String target) | ||
throws LabelSyntaxException { | ||
String targetError = LabelValidator.validateTargetName(target); | ||
if (targetError != null) { | ||
throw syntaxErrorf( | ||
"invalid target name '%s': %s%s", | ||
target, targetError, perhapsYouMeantMessage(pkg, target)); | ||
} | ||
// TODO(bazel-team): This should be an error, but we can't make it one for legacy reasons. | ||
if (target.endsWith("/.")) { | ||
return target.substring(0, target.length() - 2); | ||
} | ||
return target; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.