// Copyright 2014 Google Inc. 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.skyframe; import com.google.common.base.Preconditions; import com.google.common.cache.Cache; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; 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.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; import com.google.devtools.build.lib.events.Reporter; import com.google.devtools.build.lib.events.StoredEventHandler; import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException; import com.google.devtools.build.lib.packages.BuildFileNotFoundException; import com.google.devtools.build.lib.packages.CachingPackageLocator; import com.google.devtools.build.lib.packages.ExternalPackage; import com.google.devtools.build.lib.packages.InvalidPackageNameException; import com.google.devtools.build.lib.packages.NoSuchPackageException; import com.google.devtools.build.lib.packages.Package; import com.google.devtools.build.lib.packages.PackageFactory; import com.google.devtools.build.lib.packages.PackageFactory.Globber; import com.google.devtools.build.lib.packages.Preprocessor; 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.Label; import com.google.devtools.build.lib.syntax.ParserInputSource; import com.google.devtools.build.lib.syntax.Statement; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.vfs.RootedPath; import com.google.devtools.build.skyframe.SkyFunction; import com.google.devtools.build.skyframe.SkyFunctionException; import com.google.devtools.build.skyframe.SkyFunctionException.Transience; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; import com.google.devtools.build.skyframe.ValueOrException3; import com.google.devtools.build.skyframe.ValueOrException4; import java.io.IOException; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nullable; /** * A SkyFunction for {@link PackageValue}s. */ public class PackageFunction implements SkyFunction { private final EventHandler reporter; private final PackageFactory packageFactory; private final CachingPackageLocator packageLocator; private final Cache packageFunctionCache; private final Cache preprocessCache; private final AtomicBoolean showLoadingProgress; private final AtomicInteger numPackagesLoaded; private final Profiler profiler = Profiler.instance(); private final PathFragment preludePath; static final String DEFAULTS_PACKAGE_NAME = "tools/defaults"; public static final String EXTERNAL_PACKAGE_NAME = "external"; public PackageFunction(Reporter reporter, PackageFactory packageFactory, CachingPackageLocator pkgLocator, AtomicBoolean showLoadingProgress, Cache packageFunctionCache, Cache preprocessCache, AtomicInteger numPackagesLoaded) { this.reporter = reporter; // Can be null in tests. this.preludePath = packageFactory == null ? null : packageFactory.getRuleClassProvider().getPreludePath(); this.packageFactory = packageFactory; this.packageLocator = pkgLocator; this.showLoadingProgress = showLoadingProgress; this.packageFunctionCache = packageFunctionCache; this.preprocessCache = preprocessCache; this.numPackagesLoaded = numPackagesLoaded; } private static void maybeThrowFilesystemInconsistency(PackageIdentifier packageIdentifier, Exception skyframeException, boolean packageWasInError) throws InternalInconsistentFilesystemException { if (!packageWasInError) { throw new InternalInconsistentFilesystemException(packageIdentifier, "Encountered error '" + skyframeException.getMessage() + "' but didn't encounter it when doing the same thing " + "earlier in the build"); } } /** * Marks the given dependencies, and returns those already present. Ignores any exception * thrown while building the dependency, except for filesystem inconsistencies. * *

We need to mark dependencies implicitly used by the legacy package loading code, but we * don't care about any skyframe errors since the package knows whether it's in error or not. */ private static Pair, Boolean> getPackageLookupDepsAndPropagateInconsistentFilesystemExceptions( PackageIdentifier packageIdentifier, Iterable depKeys, Environment env, boolean packageWasInError) throws InternalInconsistentFilesystemException { Preconditions.checkState( Iterables.all(depKeys, SkyFunctions.isSkyFunction(SkyFunctions.PACKAGE_LOOKUP)), depKeys); boolean packageShouldBeInError = packageWasInError; ImmutableMap.Builder builder = ImmutableMap.builder(); for (Map.Entry> entry : env.getValuesOrThrow(depKeys, BuildFileNotFoundException.class, InconsistentFilesystemException.class, FileSymlinkException.class).entrySet()) { PathFragment pkgName = ((PackageIdentifier) entry.getKey().argument()).getPackageFragment(); try { PackageLookupValue value = (PackageLookupValue) entry.getValue().get(); if (value != null) { builder.put(pkgName, value); } } catch (BuildFileNotFoundException e) { maybeThrowFilesystemInconsistency(packageIdentifier, e, packageWasInError); } catch (InconsistentFilesystemException e) { throw new InternalInconsistentFilesystemException(packageIdentifier, e); } catch (FileSymlinkException e) { // Legacy doesn't detect symlink cycles. packageShouldBeInError = true; } } return Pair.of(builder.build(), packageShouldBeInError); } private static boolean markFileDepsAndPropagateInconsistentFilesystemExceptions( PackageIdentifier packageIdentifier, Iterable depKeys, Environment env, boolean packageWasInError) throws InternalInconsistentFilesystemException { Preconditions.checkState( Iterables.all(depKeys, SkyFunctions.isSkyFunction(SkyFunctions.FILE)), depKeys); boolean packageShouldBeInError = packageWasInError; for (Map.Entry> entry : env.getValuesOrThrow(depKeys, IOException.class, FileSymlinkException.class, InconsistentFilesystemException.class).entrySet()) { try { entry.getValue().get(); } catch (IOException e) { maybeThrowFilesystemInconsistency(packageIdentifier, e, packageWasInError); } catch (FileSymlinkException e) { // Legacy doesn't detect symlink cycles. packageShouldBeInError = true; } catch (InconsistentFilesystemException e) { throw new InternalInconsistentFilesystemException(packageIdentifier, e); } } return packageShouldBeInError; } private static boolean markGlobDepsAndPropagateInconsistentFilesystemExceptions( PackageIdentifier packageIdentifier, Iterable depKeys, Environment env, boolean packageWasInError) throws InternalInconsistentFilesystemException { Preconditions.checkState( Iterables.all(depKeys, SkyFunctions.isSkyFunction(SkyFunctions.GLOB)), depKeys); boolean packageShouldBeInError = packageWasInError; for (Map.Entry> entry : env.getValuesOrThrow(depKeys, IOException.class, BuildFileNotFoundException.class, FileSymlinkException.class, InconsistentFilesystemException.class).entrySet()) { try { entry.getValue().get(); } catch (IOException | BuildFileNotFoundException e) { maybeThrowFilesystemInconsistency(packageIdentifier, e, packageWasInError); } catch (FileSymlinkException e) { // Legacy doesn't detect symlink cycles. packageShouldBeInError = true; } catch (InconsistentFilesystemException e) { throw new InternalInconsistentFilesystemException(packageIdentifier, e); } } return packageShouldBeInError; } /** * Marks dependencies implicitly used by legacy package loading code, after the fact. Note that * the given package might already be in error. * *

Any skyframe exceptions encountered here are ignored, as similar errors should have * already been encountered by legacy package loading (if not, then the filesystem is * inconsistent). */ private static boolean markDependenciesAndPropagateInconsistentFilesystemExceptions( Package pkg, Environment env, Collection> globPatterns, Map subincludes) throws InternalInconsistentFilesystemException { boolean packageWasOriginallyInError = pkg.containsErrors(); boolean packageShouldBeInError = packageWasOriginallyInError; // TODO(bazel-team): This means that many packages will have to be preprocessed twice. Ouch! // We need a better continuation mechanism to avoid repeating work. [skyframe-loading] // TODO(bazel-team): It would be preferable to perform I/O from the package preprocessor via // Skyframe rather than add (potentially incomplete) dependencies after the fact. // [skyframe-loading] Set subincludePackageLookupDepKeys = Sets.newHashSet(); for (Label label : pkg.getSubincludeLabels()) { // Declare a dependency on the package lookup for the package giving access to the label. subincludePackageLookupDepKeys.add(PackageLookupValue.key(label.getPackageIdentifier())); } Pair, Boolean> subincludePackageLookupResult = getPackageLookupDepsAndPropagateInconsistentFilesystemExceptions( pkg.getPackageIdentifier(), subincludePackageLookupDepKeys, env, packageWasOriginallyInError); Map subincludePackageLookupDeps = subincludePackageLookupResult.getFirst(); packageShouldBeInError |= subincludePackageLookupResult.getSecond(); List subincludeFileDepKeys = Lists.newArrayList(); for (Entry subincludeEntry : subincludes.entrySet()) { // Ideally, we would have a direct dependency on the target with the given label, but then // subincluding a file from the same package will cause a dependency cycle, since targets // depend on their containing packages. Label label = subincludeEntry.getKey(); PackageLookupValue subincludePackageLookupValue = subincludePackageLookupDeps.get(label.getPackageFragment()); if (subincludePackageLookupValue != null) { // Declare a dependency on the actual file that was subincluded. Path subincludeFilePath = subincludeEntry.getValue(); if (subincludeFilePath != null) { if (!subincludePackageLookupValue.packageExists()) { // Legacy blaze puts a non-null path when only when the package does indeed exist. throw new InternalInconsistentFilesystemException(pkg.getPackageIdentifier(), String.format("Unexpected package in %s. Was it modified during the build?", subincludeFilePath)); } // Sanity check for consistency of Skyframe and legacy blaze. Path subincludeFilePathSkyframe = subincludePackageLookupValue.getRoot().getRelative(label.toPathFragment()); if (!subincludeFilePathSkyframe.equals(subincludeFilePath)) { throw new InternalInconsistentFilesystemException(pkg.getPackageIdentifier(), String.format("Inconsistent package location for %s: '%s' vs '%s'. " + "Was the source tree modified during the build?", label.getPackageFragment(), subincludeFilePathSkyframe, subincludeFilePath)); } // The actual file may be under a different package root than the package being // constructed. SkyKey subincludeSkyKey = FileValue.key(RootedPath.toRootedPath(subincludePackageLookupValue.getRoot(), subincludeFilePath)); subincludeFileDepKeys.add(subincludeSkyKey); } } } packageShouldBeInError |= markFileDepsAndPropagateInconsistentFilesystemExceptions( pkg.getPackageIdentifier(), subincludeFileDepKeys, env, packageWasOriginallyInError); // TODO(bazel-team): In the long term, we want to actually resolve the glob patterns within // Skyframe. For now, just logging the glob requests provides correct incrementality and // adequate performance. PackageIdentifier packageId = pkg.getPackageIdentifier(); List globDepKeys = Lists.newArrayList(); for (Pair globPattern : globPatterns) { String pattern = globPattern.getFirst(); boolean excludeDirs = globPattern.getSecond(); SkyKey globSkyKey; try { globSkyKey = GlobValue.key(packageId, pattern, excludeDirs, PathFragment.EMPTY_FRAGMENT); } catch (InvalidGlobPatternException e) { // Globs that make it to pkg.getGlobPatterns() should already be filtered for errors. throw new IllegalStateException(e); } globDepKeys.add(globSkyKey); } packageShouldBeInError |= markGlobDepsAndPropagateInconsistentFilesystemExceptions( pkg.getPackageIdentifier(), globDepKeys, env, packageWasOriginallyInError); return packageShouldBeInError; } /** * Adds a dependency on the WORKSPACE file, representing it as a special type of package. * @throws PackageFunctionException if there is an error computing the workspace file or adding * its rules to the //external package. */ private SkyValue getExternalPackage(Environment env, Path packageLookupPath) throws PackageFunctionException { RootedPath workspacePath = RootedPath.toRootedPath( packageLookupPath, new PathFragment("WORKSPACE")); SkyKey workspaceKey = PackageValue.workspaceKey(workspacePath); PackageValue workspace = null; try { workspace = (PackageValue) env.getValueOrThrow(workspaceKey, IOException.class, FileSymlinkException.class, InconsistentFilesystemException.class, EvalException.class); } catch (IOException | FileSymlinkException | InconsistentFilesystemException | EvalException e) { throw new PackageFunctionException(new BadWorkspaceFileException(e.getMessage()), Transience.PERSISTENT); } if (workspace == null) { return null; } Package pkg = workspace.getPackage(); Event.replayEventsOn(env.getListener(), pkg.getEvents()); if (pkg.containsErrors()) { throw new PackageFunctionException(new BuildFileContainsErrorsException( ExternalPackage.PACKAGE_IDENTIFIER, "Package 'external' contains errors"), pkg.containsTemporaryErrors() ? Transience.TRANSIENT : Transience.PERSISTENT); } return new PackageValue(pkg); } @Override public SkyValue compute(SkyKey key, Environment env) throws PackageFunctionException, InterruptedException { PackageIdentifier packageId = (PackageIdentifier) key.argument(); PathFragment packageNameFragment = packageId.getPackageFragment(); String packageName = packageNameFragment.getPathString(); SkyKey packageLookupKey = PackageLookupValue.key(packageId); PackageLookupValue packageLookupValue; try { packageLookupValue = (PackageLookupValue) env.getValueOrThrow(packageLookupKey, BuildFileNotFoundException.class, InconsistentFilesystemException.class); } catch (BuildFileNotFoundException e) { throw new PackageFunctionException(e, Transience.PERSISTENT); } catch (InconsistentFilesystemException e) { // This error is not transient from the perspective of the PackageFunction. throw new PackageFunctionException( new InternalInconsistentFilesystemException(packageId, e), Transience.PERSISTENT); } if (packageLookupValue == null) { return null; } if (!packageLookupValue.packageExists()) { switch (packageLookupValue.getErrorReason()) { case NO_BUILD_FILE: case DELETED_PACKAGE: case NO_EXTERNAL_PACKAGE: throw new PackageFunctionException(new BuildFileNotFoundException(packageId, packageLookupValue.getErrorMsg()), Transience.PERSISTENT); case INVALID_PACKAGE_NAME: throw new PackageFunctionException(new InvalidPackageNameException(packageId, packageLookupValue.getErrorMsg()), Transience.PERSISTENT); default: // We should never get here. throw new IllegalStateException(); } } if (packageName.equals(EXTERNAL_PACKAGE_NAME)) { return getExternalPackage(env, packageLookupValue.getRoot()); } PackageValue externalPackage = (PackageValue) env.getValue( PackageValue.key(PackageIdentifier.createInDefaultRepo(EXTERNAL_PACKAGE_NAME))); if (externalPackage == null) { return null; } Package externalPkg = externalPackage.getPackage(); PathFragment buildFileFragment = packageNameFragment.getChild("BUILD"); RootedPath buildFileRootedPath = RootedPath.toRootedPath(packageLookupValue.getRoot(), buildFileFragment); FileValue buildFileValue; try { buildFileValue = (FileValue) env.getValueOrThrow(FileValue.key(buildFileRootedPath), IOException.class, FileSymlinkException.class, InconsistentFilesystemException.class); } catch (IOException | FileSymlinkException | InconsistentFilesystemException e) { throw new IllegalStateException("Package lookup succeeded but encountered error when " + "getting FileValue for BUILD file directly.", e); } if (buildFileValue == null) { return null; } Preconditions.checkState(buildFileValue.exists(), "Package lookup succeeded but BUILD file doesn't exist"); Path buildFilePath = buildFileRootedPath.asPath(); String replacementContents = null; if (packageName.equals(DEFAULTS_PACKAGE_NAME)) { replacementContents = PrecomputedValue.DEFAULTS_PACKAGE_CONTENTS.get(env); if (replacementContents == null) { return null; } } RuleVisibility defaultVisibility = PrecomputedValue.DEFAULT_VISIBILITY.get(env); if (defaultVisibility == null) { return null; } 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); } if (astLookupValue == null) { return null; } List preludeStatements = astLookupValue.getAST() == null ? ImmutableList.of() : astLookupValue.getAST().getStatements(); // 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 // IOException occurs. Note that the BUILD files are still parsed two times. ParserInputSource inputSource; try { if (showLoadingProgress.get() && packageFunctionCache.getIfPresent(packageId) == null) { // TODO(bazel-team): don't duplicate the loading message if there are unavailable // Skylark dependencies. reporter.handle(Event.progress("Loading package: " + packageName)); } inputSource = ParserInputSource.create(buildFilePath, buildFileValue.getSize()); } catch (IOException e) { env.getListener().handle(Event.error(Location.fromFile(buildFilePath), e.getMessage())); // Note that we did this work, so we should conservatively report this error as transient. throw new PackageFunctionException(new BuildFileContainsErrorsException( packageId, e.getMessage()), Transience.TRANSIENT); } Package.LegacyBuilder legacyPkgBuilder = loadPackage( externalPkg, inputSource, replacementContents, packageId, buildFilePath, buildFileFragment, defaultVisibility, preludeStatements, env); if (legacyPkgBuilder == null) { return null; } legacyPkgBuilder.buildPartial(); try { handleLabelsCrossingSubpackagesAndPropagateInconsistentFilesystemExceptions( packageLookupValue.getRoot(), packageId, legacyPkgBuilder, env); } catch (InternalInconsistentFilesystemException e) { packageFunctionCache.invalidate(packageId); throw new PackageFunctionException(e, e.isTransient() ? Transience.TRANSIENT : Transience.PERSISTENT); } if (env.valuesMissing()) { // The package we just loaded will be in the {@code packageFunctionCache} next when this // SkyFunction is called again. return null; } Collection> globPatterns = legacyPkgBuilder.getGlobPatterns(); Map subincludes = legacyPkgBuilder.getSubincludes(); Package pkg = legacyPkgBuilder.finishBuild(); Event.replayEventsOn(env.getListener(), pkg.getEvents()); boolean packageShouldBeConsideredInError; try { packageShouldBeConsideredInError = markDependenciesAndPropagateInconsistentFilesystemExceptions(pkg, env, globPatterns, subincludes); } catch (InternalInconsistentFilesystemException e) { packageFunctionCache.invalidate(packageId); throw new PackageFunctionException(e, e.isTransient() ? Transience.TRANSIENT : Transience.PERSISTENT); } if (env.valuesMissing()) { return null; } // We know this SkyFunction will not be called again, so we can remove the cache entry. packageFunctionCache.invalidate(packageId); if (packageShouldBeConsideredInError) { throw new PackageFunctionException(new BuildFileContainsErrorsException(pkg, "Package '" + packageName + "' contains errors"), pkg.containsTemporaryErrors() ? Transience.TRANSIENT : Transience.PERSISTENT); } return new PackageValue(pkg); } /** * Returns true if includes referencing a different repository have already been computed. */ private boolean fetchIncludeRepositoryDeps(Environment env, BuildFileAST ast) { boolean ok = true; for (Label label : ast.getIncludes()) { if (!label.getPackageIdentifier().getRepository().isDefault()) { // If this is the default repository, the include refers to the same repository, whose // RepositoryValue is already a dependency of this PackageValue. if (env.getValue(RepositoryValue.key( label.getPackageIdentifier().getRepository())) == null) { ok = false; } } } return ok; } // TODO(bazel-team): this should take the AST so we don't parse the file twice. @Nullable private SkylarkImportResult discoverSkylarkImports( Path buildFilePath, PathFragment buildFileFragment, PackageIdentifier packageId, Environment env, ParserInputSource inputSource, List preludeStatements) throws PackageFunctionException { StoredEventHandler eventHandler = new StoredEventHandler(); BuildFileAST buildFileAST = BuildFileAST.parseBuildFile( inputSource, preludeStatements, eventHandler, /* package locator */ null, /* parse python */ false); SkylarkImportResult importResult; boolean includeRepositoriesFetched; if (eventHandler.hasErrors()) { importResult = new SkylarkImportResult( ImmutableMap.of(), ImmutableList.

May return null if the computation has to be restarted. */ @Nullable private Package.LegacyBuilder loadPackage( Package externalPkg, ParserInputSource inputSource, @Nullable String replacementContents, PackageIdentifier packageId, Path buildFilePath, PathFragment buildFileFragment, RuleVisibility defaultVisibility, List preludeStatements, Environment env) throws InterruptedException, PackageFunctionException { ParserInputSource replacementSource = replacementContents == null ? null : ParserInputSource.create(replacementContents, buildFilePath.asFragment()); Package.LegacyBuilder pkgBuilder = packageFunctionCache.getIfPresent(packageId); if (pkgBuilder == null) { profiler.startTask(ProfilerTask.CREATE_PACKAGE, packageId.toString()); try { Globber globber = packageFactory.createLegacyGlobber(buildFilePath.getParentDirectory(), packageId, packageLocator); Preprocessor.Result preprocessingResult = preprocessCache.getIfPresent(packageId); if (preprocessingResult == null) { preprocessingResult = replacementSource == null ? packageFactory.preprocess(packageId, buildFilePath, inputSource, globber) : Preprocessor.Result.noPreprocessing(replacementSource); preprocessCache.put(packageId, preprocessingResult); } SkylarkImportResult importResult = discoverSkylarkImports( buildFilePath, buildFileFragment, packageId, env, preprocessingResult.result, preludeStatements); if (importResult == null) { return null; } preprocessCache.invalidate(packageId); pkgBuilder = packageFactory.createPackageFromPreprocessingResult(externalPkg, packageId, buildFilePath, preprocessingResult, preprocessingResult.events, preludeStatements, importResult.importMap, importResult.fileDependencies, packageLocator, defaultVisibility, globber); numPackagesLoaded.incrementAndGet(); packageFunctionCache.put(packageId, pkgBuilder); } finally { profiler.completeTask(ProfilerTask.CREATE_PACKAGE); } } return pkgBuilder; } private static class InternalInconsistentFilesystemException extends NoSuchPackageException { private boolean isTransient; /** * Used to represent a filesystem inconsistency discovered outside the * {@link PackageFunction}. */ public InternalInconsistentFilesystemException(PackageIdentifier packageIdentifier, InconsistentFilesystemException e) { super(packageIdentifier, e.getMessage(), e); // This is not a transient error from the perspective of the PackageFunction. this.isTransient = false; } /** Used to represent a filesystem inconsistency discovered by the {@link PackageFunction}. */ public InternalInconsistentFilesystemException(PackageIdentifier packageIdentifier, String inconsistencyMessage) { this(packageIdentifier, new InconsistentFilesystemException(inconsistencyMessage)); this.isTransient = true; } public boolean isTransient() { return isTransient; } } private static class BadWorkspaceFileException extends NoSuchPackageException { private BadWorkspaceFileException(String message) { super(ExternalPackage.PACKAGE_IDENTIFIER, "Error encountered while dealing with the WORKSPACE file: " + message); } } private static class BadPreludeFileException extends NoSuchPackageException { private BadPreludeFileException(PackageIdentifier packageIdentifier, String message) { super(packageIdentifier, "Error encountered while reading the prelude file: " + message); } } /** * Used to declare all the exception types that can be wrapped in the exception thrown by * {@link PackageFunction#compute}. */ private static class PackageFunctionException extends SkyFunctionException { public PackageFunctionException(NoSuchPackageException e, Transience transience) { super(e, transience); } } /** A simple value class to store the result of the Skylark imports.*/ private static final class SkylarkImportResult { private final Map importMap; private final ImmutableList