diff options
Diffstat (limited to 'src/main/java/com')
13 files changed, 288 insertions, 353 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java index 3b7ddd47e3..b46fea748c 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java @@ -18,7 +18,6 @@ import static com.google.common.base.Preconditions.checkNotNull; import static com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType.ABSTRACT; import static com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType.TEST; -import com.google.common.base.Preconditions; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; @@ -81,7 +80,7 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { */ public static class Builder implements RuleDefinitionEnvironment { private final StringBuilder defaultWorkspaceFile = new StringBuilder(); - private PathFragment preludePath; + private Label preludeLabel; private String runfilesPrefix; private final List<ConfigurationFragmentFactory> configurationFragments = new ArrayList<>(); private final List<BuildInfoFactory> buildInfoFactories = new ArrayList<>(); @@ -105,11 +104,14 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { defaultWorkspaceFile.append(contents); } - public Builder setPrelude(String workspaceRelativePath) { - PathFragment preludePathFragment = new PathFragment(workspaceRelativePath); - Preconditions.checkArgument(!preludePathFragment.isAbsolute()); - Preconditions.checkArgument(preludePathFragment.isNormalized()); - this.preludePath = preludePathFragment; + public Builder setPrelude(String preludeLabelString) { + try { + this.preludeLabel = Label.parseAbsolute(preludeLabelString); + } catch (LabelSyntaxException e) { + String errorMsg = + String.format("Prelude label '%s' is not syntactically valid", preludeLabelString); + throw new IllegalArgumentException(errorMsg); + } return this; } @@ -226,7 +228,7 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { } return new ConfiguredRuleClassProvider( - preludePath, + preludeLabel, runfilesPrefix, ImmutableMap.copyOf(ruleClassMap), ImmutableMap.copyOf(ruleDefinitionMap), @@ -269,9 +271,9 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { String defaultWorkspaceFile; /** - * Workspace-relative path to the prelude. + * Label for the prelude file. */ - private final PathFragment preludePath; + private final Label preludeLabel; /** * The default runfiles prefix. @@ -315,7 +317,7 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { private final Environment.Frame globals; public ConfiguredRuleClassProvider( - PathFragment preludePath, + Label preludeLabel, String runfilesPrefix, ImmutableMap<String, RuleClass> ruleClassMap, ImmutableMap<String, Class<? extends RuleDefinition>> ruleDefinitionMap, @@ -327,7 +329,7 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { ConfigurationCollectionFactory configurationCollectionFactory, PrerequisiteValidator prerequisiteValidator, ImmutableMap<String, SkylarkType> skylarkAccessibleJavaClasses) { - this.preludePath = preludePath; + this.preludeLabel = preludeLabel; this.runfilesPrefix = runfilesPrefix; this.ruleClassMap = ruleClassMap; this.ruleDefinitionMap = ruleDefinitionMap; @@ -346,8 +348,8 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { } @Override - public PathFragment getPreludePath() { - return preludePath; + public Label getPreludeLabel() { + return preludeLabel; } @Override diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java index 6151ee9fb4..df8d9c3236 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java @@ -227,7 +227,7 @@ public class BazelRuleClassProvider { .addBuildInfoFactory(new CppBuildInfo()) .addBuildInfoFactory(new ObjcBuildInfoFactory()) .setConfigurationCollectionFactory(new BazelConfigurationCollection()) - .setPrelude("tools/build_rules/prelude_bazel") + .setPrelude("//tools/build_rules:prelude_bazel") .setRunfilesPrefix("") .setPrerequisiteValidator(new BazelPrerequisiteValidator()); diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java index e2ce1ba21f..170180e6e0 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java +++ b/src/main/java/com/google/devtools/build/lib/packages/RuleClassProvider.java @@ -14,6 +14,7 @@ package com.google.devtools.build.lib.packages; +import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.syntax.Environment; import com.google.devtools.build.lib.syntax.Environment.Extension; @@ -30,9 +31,9 @@ import javax.annotation.Nullable; public interface RuleClassProvider { /** - * Workspace relative path to the prelude file. + * Label referencing the prelude file. */ - PathFragment getPreludePath(); + Label getPreludeLabel(); /** * The default runfiles prefix (may be overwritten by the WORKSPACE file). diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java index a7cd395b33..130386793e 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupFunction.java @@ -14,10 +14,8 @@ package com.google.devtools.build.lib.skyframe; -import com.google.common.collect.ImmutableList; -import com.google.devtools.build.lib.cmdline.PackageIdentifier; +import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.packages.RuleClassProvider; -import com.google.devtools.build.lib.pkgcache.PathPackageLocator; import com.google.devtools.build.lib.syntax.BuildFileAST; import com.google.devtools.build.lib.syntax.Mutability; import com.google.devtools.build.lib.syntax.Runtime; @@ -32,167 +30,95 @@ import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; import java.io.IOException; -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; import javax.annotation.Nullable; /** - * A SkyFunction for {@link ASTFileLookupValue}s. Tries to locate a file and load it as a - * syntax tree and cache the resulting {@link BuildFileAST}. If the file doesn't exist - * the function doesn't fail but returns a specific NO_FILE ASTLookupValue. + * A SkyFunction for {@link ASTFileLookupValue}s. Given a Label referencing a Skylark file, + * attempts to locate the file and load it as a syntax tree ({@link BuildFileAST}). If the file + * (or the package containing it) doesn't exist, the function doesn't fail, but instead returns a + * specific {@code NO_FILE} {@link ASTFileLookupValue}. */ public class ASTFileLookupFunction implements SkyFunction { - private abstract static class FileLookupResult { - /** Returns whether the file lookup was successful. */ - public abstract boolean lookupSuccessful(); - - /** If {@code lookupSuccessful()}, returns the {@link RootedPath} to the file. */ - public abstract RootedPath rootedPath(); - - /** If {@code lookupSuccessful()}, returns the file's size, in bytes. */ - public abstract long fileSize(); - - static FileLookupResult noFile() { - return UnsuccessfulFileResult.INSTANCE; - } - - static FileLookupResult file(RootedPath rootedPath, long fileSize) { - return new SuccessfulFileResult(rootedPath, fileSize); - } - - private static class SuccessfulFileResult extends FileLookupResult { - private final RootedPath rootedPath; - private final long fileSize; - - private SuccessfulFileResult(RootedPath rootedPath, long fileSize) { - this.rootedPath = rootedPath; - this.fileSize = fileSize; - } - - @Override - public boolean lookupSuccessful() { - return true; - } - - @Override - public RootedPath rootedPath() { - return rootedPath; - } - - @Override - public long fileSize() { - return fileSize; - } - } - - private static class UnsuccessfulFileResult extends FileLookupResult { - private static final UnsuccessfulFileResult INSTANCE = new UnsuccessfulFileResult(); - private UnsuccessfulFileResult() { - } - - @Override - public boolean lookupSuccessful() { - return false; - } - - @Override - public RootedPath rootedPath() { - throw new IllegalStateException("unsuccessful lookup"); - } - - @Override - public long fileSize() { - throw new IllegalStateException("unsuccessful lookup"); - } - } - } - - private final AtomicReference<PathPackageLocator> pkgLocator; private final RuleClassProvider ruleClassProvider; - public ASTFileLookupFunction(AtomicReference<PathPackageLocator> pkgLocator, - RuleClassProvider ruleClassProvider) { - this.pkgLocator = pkgLocator; + public ASTFileLookupFunction(RuleClassProvider ruleClassProvider) { this.ruleClassProvider = ruleClassProvider; } @Override public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException, InterruptedException { - PackageIdentifier key = (PackageIdentifier) skyKey.argument(); - PathFragment astFilePathFragment = key.getPackageFragment(); - FileLookupResult lookupResult = getASTFile(env, key); - if (lookupResult == null) { + Label fileLabel = (Label) skyKey.argument(); + PathFragment filePathFragment = fileLabel.toPathFragment(); + + /* + * Determine whether the package designated by fileLabel exists. + */ + SkyKey pkgSkyKey = PackageLookupValue.key(fileLabel.getPackageIdentifier()); + PackageLookupValue pkgLookupValue = null; + pkgLookupValue = (PackageLookupValue) env.getValue(pkgSkyKey); + if (pkgLookupValue == null) { return null; } - if (!lookupResult.lookupSuccessful()) { - return ASTFileLookupValue.noFile(); + if (!pkgLookupValue.packageExists()) { + return ASTFileLookupValue.forBadPackage(fileLabel, pkgLookupValue.getErrorMsg()); } + + /* + * Determine whether the file designated by fileLabel exists. + */ + Path packageRoot = pkgLookupValue.getRoot(); + RootedPath rootedPath = RootedPath.toRootedPath(packageRoot, filePathFragment); + SkyKey fileSkyKey = FileValue.key(rootedPath); + FileValue fileValue = null; + try { + fileValue = (FileValue) env.getValueOrThrow(fileSkyKey, IOException.class, + FileSymlinkException.class, InconsistentFilesystemException.class); + } catch (IOException | FileSymlinkException e) { + throw new ASTLookupFunctionException(new ErrorReadingSkylarkExtensionException( + e.getMessage()), Transience.PERSISTENT); + } catch (InconsistentFilesystemException e) { + throw new ASTLookupFunctionException(e, Transience.PERSISTENT); + } + if (fileValue == null) { + return null; + } + if (!fileValue.isFile()) { + return ASTFileLookupValue.forBadFile(fileLabel); + } + + /* + * Both the package and the file exist; load the file and parse it as an AST. + */ BuildFileAST ast = null; - Path path = lookupResult.rootedPath().asPath(); - long fileSize = lookupResult.fileSize(); - // Skylark files end with bzl. - boolean parseAsSkylark = astFilePathFragment.getPathString().endsWith(".bzl"); + Path path = rootedPath.asPath(); + // Skylark files end with bzl + boolean parseAsSkylark = filePathFragment.getPathString().endsWith(".bzl"); try { + long astFileSize = fileValue.getSize(); if (parseAsSkylark) { try (Mutability mutability = Mutability.create("validate")) { - ast = BuildFileAST.parseSkylarkFile(path, fileSize, env.getListener(), - new ValidationEnvironment( - ruleClassProvider.createSkylarkRuleClassEnvironment( - mutability, - env.getListener(), - // the two below don't matter for extracting the ValidationEnvironment: - /*astFileContentHashCode=*/null, - /*importMap=*/null) - .setupDynamic(Runtime.PKG_NAME, Runtime.NONE))); + ast = BuildFileAST.parseSkylarkFile(path, astFileSize, env.getListener(), + new ValidationEnvironment( + ruleClassProvider.createSkylarkRuleClassEnvironment( + mutability, + env.getListener(), + // the two below don't matter for extracting the ValidationEnvironment: + /*astFileContentHashCode=*/null, + /*importMap=*/null) + .setupDynamic(Runtime.PKG_NAME, Runtime.NONE))); } } else { - ast = BuildFileAST.parseBuildFile(path, fileSize, env.getListener(), false); + ast = BuildFileAST.parseBuildFile(path, astFileSize, env.getListener(), false); } } catch (IOException e) { - throw new ASTLookupFunctionException(new ErrorReadingSkylarkExtensionException( - e.getMessage()), Transience.TRANSIENT); - } - return ASTFileLookupValue.withFile(ast); - } - - private FileLookupResult getASTFile(Environment env, PackageIdentifier key) - throws ASTLookupFunctionException { - List<Path> candidateRoots; - if (!key.getRepository().isDefault()) { - RepositoryValue repository = - (RepositoryValue) env.getValue(RepositoryValue.key(key.getRepository())); - if (repository == null) { - return null; - } - - candidateRoots = ImmutableList.of(repository.getPath()); - } else { - candidateRoots = pkgLocator.get().getPathEntries(); + throw new ASTLookupFunctionException(new ErrorReadingSkylarkExtensionException( + e.getMessage()), Transience.TRANSIENT); } - for (Path root : candidateRoots) { - RootedPath rootedPath = RootedPath.toRootedPath(root, key.getPackageFragment()); - FileValue fileValue; - try { - fileValue = (FileValue) env.getValueOrThrow(FileValue.key(rootedPath), - IOException.class, FileSymlinkException.class, InconsistentFilesystemException.class); - } catch (IOException | FileSymlinkException e) { - throw new ASTLookupFunctionException(new ErrorReadingSkylarkExtensionException( - e.getMessage()), Transience.PERSISTENT); - } catch (InconsistentFilesystemException e) { - throw new ASTLookupFunctionException(e, Transience.PERSISTENT); - } - if (fileValue == null) { - return null; - } - if (fileValue.isFile()) { - return FileLookupResult.file(rootedPath, fileValue.getSize()); - } - } - return FileLookupResult.noFile(); + return ASTFileLookupValue.withFile(ast); } @Nullable diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupValue.java index 65fa3f0a96..29cc2e8f8a 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupValue.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/ASTFileLookupValue.java @@ -14,48 +14,90 @@ package com.google.devtools.build.lib.skyframe; -import com.google.devtools.build.lib.cmdline.PackageIdentifier; +import com.google.common.base.Preconditions; +import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.syntax.BuildFileAST; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; -import javax.annotation.Nullable; - /** - * A value that represents an AST file lookup result. + * A value that represents an AST file lookup result. There are two subclasses: one for the + * case where the file is found, and another for the case where the file is missing (but there + * are no other errors). */ -public class ASTFileLookupValue implements SkyValue { +public abstract class ASTFileLookupValue implements SkyValue { + public abstract boolean lookupSuccessful(); + public abstract BuildFileAST getAST(); + public abstract String getErrorMsg(); + + /* + * If the file is found, this class encapsulates the parsed AST. + */ + private static class ASTLookupWithFile extends ASTFileLookupValue { + private final BuildFileAST ast; - private static final ASTFileLookupValue NO_FILE = new ASTFileLookupValue(null); + private ASTLookupWithFile(BuildFileAST ast) { + Preconditions.checkNotNull(ast); + this.ast = ast; + } - @Nullable private final BuildFileAST ast; + @Override + public boolean lookupSuccessful() { + return true; + } - private ASTFileLookupValue(@Nullable BuildFileAST ast) { - this.ast = ast; - } + @Override + public BuildFileAST getAST() { + return this.ast; + } - public static ASTFileLookupValue noFile() { - return NO_FILE; + @Override + public String getErrorMsg() { + throw new IllegalStateException("can't retrieve error for successful lookup"); + } } + + /* + * If the file isn't found, this class encapsulates a message with the reason. + */ + private static class ASTLookupNoFile extends ASTFileLookupValue { + private final String errorMsg; - public static ASTFileLookupValue withFile(BuildFileAST ast) { - return new ASTFileLookupValue(ast); - } + private ASTLookupNoFile(String errorMsg) { + this.errorMsg = Preconditions.checkNotNull(errorMsg); + } - /** - * Returns the original AST file. - */ - @Nullable public BuildFileAST getAST() { - return ast; + @Override + public boolean lookupSuccessful() { + return false; + } + + @Override + public BuildFileAST getAST() { + throw new IllegalStateException("can't retrieve AST for unsuccessful lookup"); + } + + @Override + public String getErrorMsg() { + return this.errorMsg; + } } - static SkyKey key(PackageIdentifier astFileIdentifier) { - return new SkyKey(SkyFunctions.AST_FILE_LOOKUP, astFileIdentifier); + static ASTFileLookupValue forBadPackage(Label fileLabel, String reason) { + return new ASTLookupNoFile( + String.format("Unable to load package for '%s': %s", fileLabel, reason)); + } + + static ASTFileLookupValue forBadFile(Label fileLabel) { + return new ASTLookupNoFile( + String.format("Unable to load file '%s': file doesn't exist or isn't a file", fileLabel)); + } + + public static ASTFileLookupValue withFile(BuildFileAST ast) { + return new ASTLookupWithFile(ast); } - static final class ASTLookupInputException extends Exception { - ASTLookupInputException(String msg) { - super(msg); - } + static SkyKey key(Label astFileLabel) { + return new SkyKey(SkyFunctions.AST_FILE_LOOKUP, astFileLabel); } } diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java index 5e461fb113..10d06c493b 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/PackageFunction.java @@ -15,6 +15,7 @@ package com.google.devtools.build.lib.skyframe; import com.google.common.base.Preconditions; import com.google.common.cache.Cache; +import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -22,8 +23,8 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.cmdline.PackageIdentifier; -import com.google.devtools.build.lib.cmdline.PackageIdentifier.RepositoryName; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.events.Location; @@ -42,12 +43,12 @@ import com.google.devtools.build.lib.packages.RuleVisibility; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.profiler.Profiler; import com.google.devtools.build.lib.profiler.ProfilerTask; -import com.google.devtools.build.lib.skyframe.ASTFileLookupValue.ASTLookupInputException; import com.google.devtools.build.lib.skyframe.GlobValue.InvalidGlobPatternException; import com.google.devtools.build.lib.skyframe.SkylarkImportLookupFunction.SkylarkImportFailedException; import com.google.devtools.build.lib.syntax.BuildFileAST; import com.google.devtools.build.lib.syntax.Environment.Extension; import com.google.devtools.build.lib.syntax.EvalException; +import com.google.devtools.build.lib.syntax.LoadStatement; import com.google.devtools.build.lib.syntax.ParserInputSource; import com.google.devtools.build.lib.syntax.Statement; import com.google.devtools.build.lib.util.Pair; @@ -87,8 +88,7 @@ public class PackageFunction implements SkyFunction { private final AtomicBoolean showLoadingProgress; private final AtomicInteger numPackagesLoaded; private final Profiler profiler = Profiler.instance(); - - private final PathFragment preludePath; + private final Label preludeLabel; static final String DEFAULTS_PACKAGE_NAME = "tools/defaults"; public static final String EXTERNAL_PACKAGE_NAME = "external"; @@ -101,9 +101,9 @@ public class PackageFunction implements SkyFunction { this.reporter = reporter; // Can be null in tests. - this.preludePath = packageFactory == null + this.preludeLabel = packageFactory == null ? null - : packageFactory.getRuleClassProvider().getPreludePath(); + : packageFactory.getRuleClassProvider().getPreludeLabel(); this.packageFactory = packageFactory; this.packageLocator = pkgLocator; this.showLoadingProgress = showLoadingProgress; @@ -432,21 +432,21 @@ public class PackageFunction implements SkyFunction { return null; } + SkyKey astLookupKey = ASTFileLookupValue.key(preludeLabel); ASTFileLookupValue astLookupValue = null; - SkyKey astLookupKey = ASTFileLookupValue.key( - PackageIdentifier.createInDefaultRepo(preludePath)); try { astLookupValue = (ASTFileLookupValue) env.getValueOrThrow(astLookupKey, ErrorReadingSkylarkExtensionException.class, InconsistentFilesystemException.class); } catch (ErrorReadingSkylarkExtensionException | InconsistentFilesystemException e) { - throw new PackageFunctionException(new BadPreludeFileException(packageId, e.getMessage()), - Transience.PERSISTENT); + throw new PackageFunctionException( + new BadPreludeFileException(packageId, e.getMessage()), Transience.PERSISTENT); } if (astLookupValue == null) { return null; } - List<Statement> preludeStatements = astLookupValue.getAST() == null - ? ImmutableList.<Statement>of() : astLookupValue.getAST().getStatements(); + List<Statement> preludeStatements = + astLookupValue.lookupSuccessful() + ? astLookupValue.getAST().getStatements() : ImmutableList.<Statement>of(); // Load the BUILD file AST and handle Skylark dependencies. This way BUILD files are // only loaded twice if there are unavailable Skylark or package dependencies or an @@ -473,7 +473,6 @@ public class PackageFunction implements SkyFunction { replacementContents, packageId, buildFilePath, - buildFileFragment, defaultVisibility, preludeStatements, env); @@ -527,7 +526,6 @@ public class PackageFunction implements SkyFunction { @Nullable private SkylarkImportResult discoverSkylarkImports( Path buildFilePath, - PathFragment buildFileFragment, PackageIdentifier packageId, Environment env, ParserInputSource inputSource, @@ -547,8 +545,7 @@ public class PackageFunction implements SkyFunction { ImmutableMap.<PathFragment, Extension>of(), ImmutableList.<Label>of()); } else { - importResult = - fetchImportsFromBuildFile(buildFilePath, buildFileFragment, packageId, buildFileAST, env); + importResult = fetchImportsFromBuildFile(buildFilePath, packageId, buildFileAST, env); } return importResult; @@ -561,33 +558,33 @@ public class PackageFunction implements SkyFunction { @Nullable private SkylarkImportResult fetchImportsFromBuildFile( Path buildFilePath, - PathFragment buildFileFragment, PackageIdentifier packageId, BuildFileAST buildFileAST, Environment env) throws PackageFunctionException { - ImmutableMap<Location, PathFragment> imports = buildFileAST.getImports(); + ImmutableCollection<LoadStatement> imports = buildFileAST.getImports(); Map<PathFragment, Extension> importMap = new HashMap<>(); ImmutableList.Builder<SkylarkFileDependency> fileDependencies = ImmutableList.builder(); + Label buildFileLabel; + try { + buildFileLabel = Label.create(packageId, "BUILD"); + } catch (LabelSyntaxException e) { + // Shouldn't happen; the Label is well-formed by construction. + throw new IllegalStateException(e); + } try { - for (Map.Entry<Location, PathFragment> entry : imports.entrySet()) { - PathFragment importFile = entry.getValue(); - // HACK: The prelude sometimes contains load() statements, which need to be resolved - // relative to the prelude file. However, we don't have a good way to tell "this should come - // from the main repository" in a load() statement, and we don't have a good way to tell if - // a load() statement comes from the prelude, since we just prepend those statements before - // the actual BUILD file. So we use this evil .endsWith() statement to figure it out. - RepositoryName repository = - entry.getKey().getPath().endsWith(preludePath) - ? PackageIdentifier.DEFAULT_REPOSITORY_NAME : packageId.getRepository(); - SkyKey importsLookupKey = SkylarkImportLookupValue.key( - repository, buildFileFragment, importFile); - SkylarkImportLookupValue importLookupValue = (SkylarkImportLookupValue) - env.getValueOrThrow(importsLookupKey, SkylarkImportFailedException.class, - InconsistentFilesystemException.class, ASTLookupInputException.class, - BuildFileNotFoundException.class); + for (LoadStatement loadStmt : imports) { + Label importLabel = + SkylarkImportLookupFunction.findLabelForLoadStatement(loadStmt, buildFileLabel, env); + if (importLabel == null) { + return null; + } + SkyKey importsLookupKey = SkylarkImportLookupValue.key(importLabel); + SkylarkImportLookupValue importLookupValue = (SkylarkImportLookupValue) env.getValueOrThrow( + importsLookupKey, SkylarkImportFailedException.class, + InconsistentFilesystemException.class, BuildFileNotFoundException.class); if (importLookupValue != null) { - importMap.put(importFile, importLookupValue.getEnvironmentExtension()); + importMap.put(loadStmt.getImportPath(), importLookupValue.getEnvironmentExtension()); fileDependencies.add(importLookupValue.getDependency()); } } @@ -598,10 +595,6 @@ public class PackageFunction implements SkyFunction { } catch (InconsistentFilesystemException e) { throw new PackageFunctionException( new InternalInconsistentFilesystemException(packageId, e), Transience.PERSISTENT); - } catch (ASTLookupInputException e) { - // The load syntax is bad in the BUILD file so BuildFileContainsErrorsException is OK. - throw new PackageFunctionException( - new BuildFileContainsErrorsException(packageId, e.getMessage()), Transience.PERSISTENT); } catch (BuildFileNotFoundException e) { throw new PackageFunctionException(e, Transience.PERSISTENT); } @@ -770,7 +763,6 @@ public class PackageFunction implements SkyFunction { @Nullable String replacementContents, PackageIdentifier packageId, Path buildFilePath, - PathFragment buildFileFragment, RuleVisibility defaultVisibility, List<Statement> preludeStatements, Environment env) @@ -807,7 +799,6 @@ public class PackageFunction implements SkyFunction { SkylarkImportResult importResult = discoverSkylarkImports( buildFilePath, - buildFileFragment, packageId, env, preprocessingResult.result, diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java index 443a444452..ced28051e3 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java @@ -312,7 +312,7 @@ public abstract class SkyframeExecutor implements WalkableGraphFactory { map.put(SkyFunctions.DIRECTORY_LISTING, new DirectoryListingFunction()); map.put(SkyFunctions.PACKAGE_LOOKUP, new PackageLookupFunction(deletedPackages)); map.put(SkyFunctions.CONTAINING_PACKAGE_LOOKUP, new ContainingPackageLookupFunction()); - map.put(SkyFunctions.AST_FILE_LOOKUP, new ASTFileLookupFunction(pkgLocator, ruleClassProvider)); + map.put(SkyFunctions.AST_FILE_LOOKUP, new ASTFileLookupFunction(ruleClassProvider)); map.put(SkyFunctions.SKYLARK_IMPORTS_LOOKUP, new SkylarkImportLookupFunction( ruleClassProvider, pkgFactory)); map.put(SkyFunctions.GLOB, newGlobFunction()); diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java index 29318f359c..d9180038eb 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java @@ -17,17 +17,15 @@ import com.google.common.collect.ImmutableList; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.cmdline.PackageIdentifier; -import com.google.devtools.build.lib.cmdline.PackageIdentifier.RepositoryName; import com.google.devtools.build.lib.events.Event; -import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.events.StoredEventHandler; import com.google.devtools.build.lib.packages.BuildFileNotFoundException; import com.google.devtools.build.lib.packages.PackageFactory; import com.google.devtools.build.lib.packages.RuleClassProvider; import com.google.devtools.build.lib.rules.SkylarkRuleClassFunctions; -import com.google.devtools.build.lib.skyframe.ASTFileLookupValue.ASTLookupInputException; import com.google.devtools.build.lib.syntax.BuildFileAST; import com.google.devtools.build.lib.syntax.Environment.Extension; +import com.google.devtools.build.lib.syntax.LoadStatement; import com.google.devtools.build.lib.syntax.Mutability; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.skyframe.SkyFunction; @@ -39,6 +37,8 @@ import com.google.devtools.build.skyframe.SkyValue; import java.util.HashMap; import java.util.Map; +import javax.annotation.Nullable; + /** * A Skyframe function to look up and import a single Skylark extension. */ @@ -56,111 +56,117 @@ public class SkylarkImportLookupFunction implements SkyFunction { @Override public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException, InterruptedException { - PackageIdentifier arg = (PackageIdentifier) skyKey.argument(); - PathFragment file = arg.getPackageFragment(); + Label fileLabel = (Label) skyKey.argument(); + PathFragment importPath = fileLabel.toPathFragment(); ASTFileLookupValue astLookupValue = null; try { - SkyKey astLookupKey = ASTFileLookupValue.key(arg); + SkyKey astLookupKey = ASTFileLookupValue.key(fileLabel); astLookupValue = (ASTFileLookupValue) env.getValueOrThrow(astLookupKey, ErrorReadingSkylarkExtensionException.class, InconsistentFilesystemException.class); } catch (ErrorReadingSkylarkExtensionException e) { throw new SkylarkImportLookupFunctionException(SkylarkImportFailedException.errorReadingFile( - file, e.getMessage())); + importPath, e.getMessage())); } catch (InconsistentFilesystemException e) { throw new SkylarkImportLookupFunctionException(e, Transience.PERSISTENT); } if (astLookupValue == null) { return null; } - if (astLookupValue.getAST() == null) { + if (!astLookupValue.lookupSuccessful()) { // Skylark import files have to exist. - throw new SkylarkImportLookupFunctionException(SkylarkImportFailedException.noFile(file)); + throw new SkylarkImportLookupFunctionException( + SkylarkImportFailedException.noFile(astLookupValue.getErrorMsg())); } Map<PathFragment, Extension> importMap = new HashMap<>(); ImmutableList.Builder<SkylarkFileDependency> fileDependencies = ImmutableList.builder(); BuildFileAST ast = astLookupValue.getAST(); - // TODO(bazel-team): Refactor this code and PackageFunction to reduce code duplications. - for (Map.Entry<Location, PathFragment> entry : ast.getImports().entrySet()) { + for (LoadStatement loadStmt : ast.getImports()) { try { - PathFragment importFile = entry.getValue(); - // HACK: The prelude sometimes contains load() statements, which need to be resolved - // relative to the prelude file. However, we don't have a good way to tell "this should come - // from the main repository" in a load() statement, and we don't have a good way to tell if - // a load() statement comes from the prelude, since we just prepend those statements before - // the actual BUILD file. So we use this evil .endsWith() statement to figure it out. - RepositoryName repository = - entry.getKey().getPath().endsWith(ruleClassProvider.getPreludePath()) - ? PackageIdentifier.DEFAULT_REPOSITORY_NAME : arg.getRepository(); - SkyKey importsLookupKey = SkylarkImportLookupValue.key(repository, file, importFile); + Label importLabel = findLabelForLoadStatement(loadStmt, fileLabel, env); + if (importLabel == null) { + return null; + } + SkyKey importsLookupKey = SkylarkImportLookupValue.key(importLabel); SkylarkImportLookupValue importsLookupValue; - importsLookupValue = (SkylarkImportLookupValue) env.getValueOrThrow( - importsLookupKey, ASTLookupInputException.class); + importsLookupValue = (SkylarkImportLookupValue) env.getValue(importsLookupKey); if (importsLookupValue != null) { - importMap.put(importFile, importsLookupValue.getEnvironmentExtension()); + importMap.put(loadStmt.getImportPath(), importsLookupValue.getEnvironmentExtension()); fileDependencies.add(importsLookupValue.getDependency()); } - } catch (ASTLookupInputException e) { - throw new SkylarkImportLookupFunctionException(e, Transience.PERSISTENT); + } catch (SkylarkImportFailedException e) { + throw new SkylarkImportLookupFunctionException(e); } } - Label label = pathFragmentToLabel(arg.getRepository(), file, env); if (env.valuesMissing()) { // This means some imports are unavailable. return null; } if (ast.containsErrors()) { - throw new SkylarkImportLookupFunctionException(SkylarkImportFailedException.skylarkErrors( - file)); + throw new SkylarkImportLookupFunctionException( + SkylarkImportFailedException.skylarkErrors(importPath)); } // Skylark UserDefinedFunction-s in that file will share this function definition Environment, // which will be frozen by the time it is returned by createExtension. Extension extension = - createExtension(ast, file, importMap, env); + createExtension(ast, importPath, importMap, env); return new SkylarkImportLookupValue( - extension, new SkylarkFileDependency(label, fileDependencies.build())); + extension, new SkylarkFileDependency(fileLabel, fileDependencies.build())); } - /** - * Converts the PathFragment of the Skylark file to a Label using the BUILD file closest to the - * Skylark file in its directory hierarchy - finds the package to which the Skylark file belongs. - * Throws an exception if no such BUILD file exists. - */ - private Label pathFragmentToLabel(RepositoryName repo, PathFragment file, Environment env) - throws SkylarkImportLookupFunctionException { - ContainingPackageLookupValue containingPackageLookupValue = null; - try { - PackageIdentifier newPkgId = new PackageIdentifier(repo, file.getParentDirectory()); - containingPackageLookupValue = (ContainingPackageLookupValue) env.getValueOrThrow( - ContainingPackageLookupValue.key(newPkgId), - BuildFileNotFoundException.class, InconsistentFilesystemException.class); - } catch (BuildFileNotFoundException e) { - // Thrown when there are IO errors looking for BUILD files. - throw new SkylarkImportLookupFunctionException(e, Transience.PERSISTENT); - } catch (InconsistentFilesystemException e) { - throw new SkylarkImportLookupFunctionException(e, Transience.PERSISTENT); - } + /** + * Given a Skylark {@link LoadStatement} and the {@link Label} of the file containing the load + * statement, returns a canonical {@link Label} corresponding to the load path in the statement. + * If no package can be found that contains the loaded file, throws + * {@link SkylarkImportFailedException}. Returns null if Skyframe dependencies are unavailable. + */ + @Nullable + static Label findLabelForLoadStatement(LoadStatement loadStmt, Label containingFileLabel, + Environment env) + throws SkylarkImportFailedException { + PathFragment importPath = loadStmt.getImportPath(); + PackageIdentifier pkgIdForImport; + String targetNameForImport; + if (loadStmt.isAbsolute()) { + PathFragment relativeImportPath = importPath.toRelative(); + PackageIdentifier pkgToLookUp = + PackageIdentifier.createInDefaultRepo(relativeImportPath.getParentDirectory()); + ContainingPackageLookupValue containingPackageLookupValue = null; + try { + containingPackageLookupValue = (ContainingPackageLookupValue) env.getValueOrThrow( + ContainingPackageLookupValue.key(pkgToLookUp), + BuildFileNotFoundException.class, InconsistentFilesystemException.class); + } catch (BuildFileNotFoundException e) { + // Thrown when there are IO errors looking for BUILD files. + throw new SkylarkImportFailedException(e); + } catch (InconsistentFilesystemException e) { + throw new SkylarkImportFailedException(e); + } - if (containingPackageLookupValue == null) { - return null; - } + if (containingPackageLookupValue == null) { + return null; + } - if (!containingPackageLookupValue.hasContainingPackage()) { - throw new SkylarkImportLookupFunctionException( - SkylarkImportFailedException.noBuildFile(file)); + if (!containingPackageLookupValue.hasContainingPackage()) { + throw SkylarkImportFailedException.noBuildFile(importPath); + } + pkgIdForImport = containingPackageLookupValue.getContainingPackageName(); + PathFragment containingPkgPath = pkgIdForImport.getPackageFragment(); + targetNameForImport = relativeImportPath.relativeTo(containingPkgPath).toString(); + } else { + // The load statement has a relative path + pkgIdForImport = containingFileLabel.getPackageIdentifier(); + PathFragment containingDir = + (new PathFragment(containingFileLabel.getName())).getParentDirectory(); + targetNameForImport = importPath.getRelative(containingDir).toString(); } - - PathFragment pkgName = - containingPackageLookupValue.getContainingPackageName().getPackageFragment(); - PathFragment fileInPkg = file.relativeTo(pkgName); - try { - // This code relies on PackageIdentifier.RepositoryName.toString() - return Label.parseAbsolute(repo + "//" + pkgName.getPathString() + ":" + fileInPkg); + return Label.create(pkgIdForImport, targetNameForImport); } catch (LabelSyntaxException e) { + // Shouldn't happen; the Label is well-formed by construction. throw new IllegalStateException(e); } } @@ -205,6 +211,14 @@ public class SkylarkImportLookupFunction implements SkyFunction { super(errorMessage); } + private SkylarkImportFailedException(InconsistentFilesystemException e) { + super(e.getMessage()); + } + + private SkylarkImportFailedException(BuildFileNotFoundException e) { + super(e.getMessage()); + } + static SkylarkImportFailedException errors(PathFragment file) { return new SkylarkImportFailedException( String.format("Extension file '%s' has errors", file)); @@ -215,9 +229,9 @@ public class SkylarkImportLookupFunction implements SkyFunction { String.format("Encountered error while reading extension file '%s': %s", file, error)); } - static SkylarkImportFailedException noFile(PathFragment file) { + static SkylarkImportFailedException noFile(String reason) { return new SkylarkImportFailedException( - String.format("Extension file not found: '%s'", file)); + String.format("Extension file not found. %s", reason)); } static SkylarkImportFailedException noBuildFile(PathFragment file) { @@ -242,11 +256,6 @@ public class SkylarkImportLookupFunction implements SkyFunction { super(e, transience); } - private SkylarkImportLookupFunctionException(ASTLookupInputException e, - Transience transience) { - super(e, transience); - } - private SkylarkImportLookupFunctionException(BuildFileNotFoundException e, Transience transience) { super(e, transience); diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupValue.java index 0ac9fd4402..e9d050d300 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupValue.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupValue.java @@ -13,14 +13,9 @@ // limitations under the License. package com.google.devtools.build.lib.skyframe; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; -import com.google.devtools.build.lib.cmdline.PackageIdentifier; -import com.google.devtools.build.lib.cmdline.PackageIdentifier.RepositoryName; -import com.google.devtools.build.lib.skyframe.ASTFileLookupValue.ASTLookupInputException; +import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.syntax.Environment.Extension; -import com.google.devtools.build.lib.syntax.LoadStatement; -import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; @@ -58,38 +53,7 @@ public class SkylarkImportLookupValue implements SkyValue { return dependency; } - private static void checkInputArgument(PathFragment astFilePathFragment) - throws ASTLookupInputException { - if (astFilePathFragment.isAbsolute()) { - throw new ASTLookupInputException(String.format( - "Input file '%s' cannot be an absolute path.", astFilePathFragment)); - } - } - - @VisibleForTesting - static SkyKey key(PackageIdentifier pkgIdentifier) throws ASTLookupInputException { - return key(pkgIdentifier.getRepository(), pkgIdentifier.getPackageFragment()); - } - - static SkyKey key(RepositoryName repo, PathFragment fromFile, PathFragment fileToImport) - throws ASTLookupInputException { - PathFragment computedPath; - if (fileToImport.isAbsolute()) { - computedPath = fileToImport.toRelative(); - } else if (fileToImport.segmentCount() == 1) { - computedPath = fromFile.getParentDirectory().getRelative(fileToImport); - } else { - throw new ASTLookupInputException(String.format(LoadStatement.PATH_ERROR_MSG, fileToImport)); - } - return key(repo, computedPath); - } - - private static SkyKey key(RepositoryName repo, PathFragment fileToImport) - throws ASTLookupInputException { - // Skylark import lookup keys need to be valid AST file lookup keys. - checkInputArgument(fileToImport); - return new SkyKey( - SkyFunctions.SKYLARK_IMPORTS_LOOKUP, - new PackageIdentifier(repo, fileToImport)); + static SkyKey key(Label importLabel) { + return new SkyKey(SkyFunctions.SKYLARK_IMPORTS_LOOKUP, importLabel); } } diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkModuleCycleReporter.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkModuleCycleReporter.java index 5043879c80..902234ed3a 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkModuleCycleReporter.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkModuleCycleReporter.java @@ -18,7 +18,7 @@ import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; -import com.google.devtools.build.lib.cmdline.PackageIdentifier; +import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.skyframe.CycleInfo; @@ -58,7 +58,7 @@ public class SkylarkModuleCycleReporter implements CyclesReporter.SingleCycleRep new Function<SkyKey, String>() { @Override public String apply(SkyKey input) { - return ((PackageIdentifier) input.argument()).toString(); + return ((Label) input.argument()).toString(); } }); diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java b/src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java index e6d00ef0bc..2faab99381 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/BuildFileAST.java @@ -14,13 +14,11 @@ package com.google.devtools.build.lib.syntax; import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableMap; import com.google.common.hash.HashCode; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.vfs.Path; -import com.google.devtools.build.lib.vfs.PathFragment; import java.io.IOException; import java.util.List; @@ -36,7 +34,7 @@ public class BuildFileAST extends ASTNode { private final ImmutableList<Comment> comments; - private ImmutableMap<Location, PathFragment> loads; + private ImmutableList<LoadStatement> loads; /** * Whether any errors were encountered during scanning or parsing. @@ -61,13 +59,13 @@ public class BuildFileAST extends ASTNode { setLocation(result.location); } - /** Collects paths from all load statements */ - private ImmutableMap<Location, PathFragment> fetchLoads(List<Statement> stmts) { - ImmutableMap.Builder<Location, PathFragment> loads = ImmutableMap.builder(); + /** Collects all load statements */ + private ImmutableList<LoadStatement> fetchLoads(List<Statement> stmts) { + ImmutableList.Builder<LoadStatement> loads = new ImmutableList.Builder<>(); for (Statement stmt : stmts) { if (stmt instanceof LoadStatement) { LoadStatement imp = (LoadStatement) stmt; - loads.put(imp.getLocation(), imp.getImportPath()); + loads.add(imp); } } return loads.build(); @@ -97,9 +95,9 @@ public class BuildFileAST extends ASTNode { } /** - * Returns a set of loads in this BUILD file. + * Returns a list of loads in this BUILD file. */ - public synchronized ImmutableMap<Location, PathFragment> getImports() { + public synchronized ImmutableList<LoadStatement> getImports() { if (loads == null) { loads = fetchLoads(stmts); } diff --git a/src/main/java/com/google/devtools/build/lib/syntax/LoadStatement.java b/src/main/java/com/google/devtools/build/lib/syntax/LoadStatement.java index fc6fa77045..e4ede569d6 100644 --- a/src/main/java/com/google/devtools/build/lib/syntax/LoadStatement.java +++ b/src/main/java/com/google/devtools/build/lib/syntax/LoadStatement.java @@ -25,8 +25,6 @@ import java.util.Map; */ public final class LoadStatement extends Statement { - public static final String PATH_ERROR_MSG = "Path '%s' is not valid. " - + "It should either start with a slash or refer to a file in the current directory."; private final ImmutableMap<Identifier, String> symbols; private final ImmutableList<Identifier> cachedSymbols; // to save time private final PathFragment importPath; @@ -35,10 +33,9 @@ public final class LoadStatement extends Statement { /** * Constructs an import statement. * - * <p>Symbols maps a symbol to its original name under which it was defined in - * the bzl file that should be loaded. - * If aliasing is used, the value differs from it's key's symbol#getName(). - * Otherwise, both values are identical. + * <p>{@code symbols} maps a symbol to the original name under which it was defined in + * the bzl file that should be loaded. If aliasing is used, the value differs from its key's + * {@code symbol.getName()}. Otherwise, both values are identical. */ LoadStatement(StringLiteral path, Map<Identifier, String> symbols) { this.symbols = ImmutableMap.copyOf(symbols); @@ -71,7 +68,7 @@ public final class LoadStatement extends Statement { getLocation(), "symbol '" + current + "' is private and cannot be imported"); } // The key is the original name that was used to define the symbol - // in the loaded bzl file + // in the loaded bzl file. env.importSymbol(getImportPath(), current, entry.getValue()); } catch (Environment.NoSuchVariableException | Environment.LoadFailedException e) { throw new EvalException(getLocation(), e.getMessage()); @@ -87,10 +84,6 @@ public final class LoadStatement extends Statement { @Override void validate(ValidationEnvironment env) throws EvalException { validatePath(); - - if (!importPath.isAbsolute() && importPath.segmentCount() > 1) { - throw new EvalException(getLocation(), String.format(PATH_ERROR_MSG, importPath)); - } for (Identifier symbol : cachedSymbols) { env.declare(symbol.getName(), getLocation()); } @@ -113,10 +106,19 @@ public final class LoadStatement extends Statement { error = "First argument of load() is a path, not a label. " + "It should start with a single slash if it is an absolute path."; + } else if (!importPath.isAbsolute() && importPath.segmentCount() > 1) { + error = String.format( + "Path '%s' is not valid. " + + "It should either start with a slash or refer to a file in the current directory.", + importPath); } if (error != null) { throw new EvalException(getLocation(), error); } } + + public boolean isAbsolute() { + return getImportPath().isAbsolute(); + } } diff --git a/src/main/java/com/google/devtools/build/skyframe/SkyFunctionException.java b/src/main/java/com/google/devtools/build/skyframe/SkyFunctionException.java index 09d2c4c49e..30d668ae82 100644 --- a/src/main/java/com/google/devtools/build/skyframe/SkyFunctionException.java +++ b/src/main/java/com/google/devtools/build/skyframe/SkyFunctionException.java @@ -22,7 +22,7 @@ import javax.annotation.Nullable; * Base class of exceptions thrown by {@link SkyFunction#compute} on failure. * * SkyFunctions should declare a subclass {@code C} of {@link SkyFunctionException} whose - * constructors forward fine-grained exception types (e.g. {@link IOException}) to + * constructors forward fine-grained exception types (e.g. {@code IOException}) to * {@link SkyFunctionException}'s constructor, and they should also declare * {@link SkyFunction#compute} to throw {@code C}. This way the type system checks that no * unexpected exceptions are thrown by the {@link SkyFunction}. |