blob: 427e758b29924806dd3a80aff281ed12270d2133 [file] [log] [blame]
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001// Copyright 2014 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.skyframe;
15
16import com.google.common.base.Preconditions;
Eric Fellheimer35e57cf2015-05-27 14:31:11 +000017import com.google.common.cache.Cache;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010018import com.google.common.collect.ImmutableList;
19import com.google.common.collect.ImmutableMap;
20import com.google.common.collect.ImmutableSet;
21import com.google.common.collect.Iterables;
22import com.google.common.collect.Lists;
23import com.google.common.collect.Sets;
Kristina Chodorow73fa2032015-08-28 17:57:46 +000024import com.google.devtools.build.lib.cmdline.PackageIdentifier;
25import com.google.devtools.build.lib.cmdline.PackageIdentifier.RepositoryName;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010026import com.google.devtools.build.lib.events.Event;
27import com.google.devtools.build.lib.events.EventHandler;
28import com.google.devtools.build.lib.events.Location;
29import com.google.devtools.build.lib.events.Reporter;
30import com.google.devtools.build.lib.events.StoredEventHandler;
31import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException;
32import com.google.devtools.build.lib.packages.BuildFileNotFoundException;
33import com.google.devtools.build.lib.packages.CachingPackageLocator;
Kristina Chodorowe121dd92015-06-17 14:24:35 +000034import com.google.devtools.build.lib.packages.ExternalPackage;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010035import com.google.devtools.build.lib.packages.InvalidPackageNameException;
36import com.google.devtools.build.lib.packages.NoSuchPackageException;
37import com.google.devtools.build.lib.packages.Package;
38import com.google.devtools.build.lib.packages.PackageFactory;
39import com.google.devtools.build.lib.packages.PackageFactory.Globber;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010040import com.google.devtools.build.lib.packages.Preprocessor;
41import com.google.devtools.build.lib.packages.RuleVisibility;
42import com.google.devtools.build.lib.packages.Target;
43import com.google.devtools.build.lib.profiler.Profiler;
44import com.google.devtools.build.lib.profiler.ProfilerTask;
45import com.google.devtools.build.lib.skyframe.ASTFileLookupValue.ASTLookupInputException;
46import com.google.devtools.build.lib.skyframe.GlobValue.InvalidGlobPatternException;
47import com.google.devtools.build.lib.skyframe.SkylarkImportLookupFunction.SkylarkImportFailedException;
48import com.google.devtools.build.lib.syntax.BuildFileAST;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000049import com.google.devtools.build.lib.syntax.Environment.Extension;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010050import com.google.devtools.build.lib.syntax.EvalException;
51import com.google.devtools.build.lib.syntax.Label;
52import com.google.devtools.build.lib.syntax.ParserInputSource;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010053import com.google.devtools.build.lib.syntax.Statement;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010054import com.google.devtools.build.lib.util.Pair;
55import com.google.devtools.build.lib.vfs.Path;
56import com.google.devtools.build.lib.vfs.PathFragment;
57import com.google.devtools.build.lib.vfs.RootedPath;
58import com.google.devtools.build.skyframe.SkyFunction;
59import com.google.devtools.build.skyframe.SkyFunctionException;
60import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
61import com.google.devtools.build.skyframe.SkyKey;
62import com.google.devtools.build.skyframe.SkyValue;
63import com.google.devtools.build.skyframe.ValueOrException3;
64import com.google.devtools.build.skyframe.ValueOrException4;
65
66import java.io.IOException;
67import java.util.Collection;
68import java.util.HashMap;
69import java.util.List;
70import java.util.Map;
71import java.util.Map.Entry;
72import java.util.Set;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010073import java.util.concurrent.atomic.AtomicBoolean;
74import java.util.concurrent.atomic.AtomicInteger;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010075
76import javax.annotation.Nullable;
77
78/**
79 * A SkyFunction for {@link PackageValue}s.
80 */
81public class PackageFunction implements SkyFunction {
82
83 private final EventHandler reporter;
84 private final PackageFactory packageFactory;
85 private final CachingPackageLocator packageLocator;
Eric Fellheimer35e57cf2015-05-27 14:31:11 +000086 private final Cache<PackageIdentifier, Package.LegacyBuilder> packageFunctionCache;
Ulf Adamsd9b51212015-09-04 15:14:22 +000087 private final Cache<PackageIdentifier, Preprocessor.Result> preprocessCache;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010088 private final AtomicBoolean showLoadingProgress;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010089 private final AtomicInteger numPackagesLoaded;
90 private final Profiler profiler = Profiler.instance();
91
Ulf Adamsfdfdd922015-09-01 08:36:29 +000092 private final PathFragment preludePath;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010093
94 static final String DEFAULTS_PACKAGE_NAME = "tools/defaults";
95 public static final String EXTERNAL_PACKAGE_NAME = "external";
96
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010097 public PackageFunction(Reporter reporter, PackageFactory packageFactory,
98 CachingPackageLocator pkgLocator, AtomicBoolean showLoadingProgress,
Eric Fellheimer35e57cf2015-05-27 14:31:11 +000099 Cache<PackageIdentifier, Package.LegacyBuilder> packageFunctionCache,
Ulf Adamsd9b51212015-09-04 15:14:22 +0000100 Cache<PackageIdentifier, Preprocessor.Result> preprocessCache,
Eric Fellheimer670a75a2015-05-18 13:18:58 +0000101 AtomicInteger numPackagesLoaded) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100102 this.reporter = reporter;
103
Ulf Adamsfdfdd922015-09-01 08:36:29 +0000104 // Can be null in tests.
105 this.preludePath = packageFactory == null
106 ? null
107 : packageFactory.getRuleClassProvider().getPreludePath();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100108 this.packageFactory = packageFactory;
109 this.packageLocator = pkgLocator;
110 this.showLoadingProgress = showLoadingProgress;
111 this.packageFunctionCache = packageFunctionCache;
Ulf Adamsd9b51212015-09-04 15:14:22 +0000112 this.preprocessCache = preprocessCache;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100113 this.numPackagesLoaded = numPackagesLoaded;
114 }
115
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000116 private static void maybeThrowFilesystemInconsistency(PackageIdentifier packageIdentifier,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100117 Exception skyframeException, boolean packageWasInError)
118 throws InternalInconsistentFilesystemException {
119 if (!packageWasInError) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000120 throw new InternalInconsistentFilesystemException(packageIdentifier, "Encountered error '"
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100121 + skyframeException.getMessage() + "' but didn't encounter it when doing the same thing "
122 + "earlier in the build");
123 }
124 }
125
126 /**
127 * Marks the given dependencies, and returns those already present. Ignores any exception
128 * thrown while building the dependency, except for filesystem inconsistencies.
129 *
130 * <p>We need to mark dependencies implicitly used by the legacy package loading code, but we
131 * don't care about any skyframe errors since the package knows whether it's in error or not.
132 */
133 private static Pair<? extends Map<PathFragment, PackageLookupValue>, Boolean>
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000134 getPackageLookupDepsAndPropagateInconsistentFilesystemExceptions(
135 PackageIdentifier packageIdentifier,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100136 Iterable<SkyKey> depKeys, Environment env, boolean packageWasInError)
137 throws InternalInconsistentFilesystemException {
138 Preconditions.checkState(
139 Iterables.all(depKeys, SkyFunctions.isSkyFunction(SkyFunctions.PACKAGE_LOOKUP)), depKeys);
140 boolean packageShouldBeInError = packageWasInError;
141 ImmutableMap.Builder<PathFragment, PackageLookupValue> builder = ImmutableMap.builder();
142 for (Map.Entry<SkyKey, ValueOrException3<BuildFileNotFoundException,
Nathan Harmataad810502015-07-29 01:33:49 +0000143 InconsistentFilesystemException, FileSymlinkException>> entry :
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100144 env.getValuesOrThrow(depKeys, BuildFileNotFoundException.class,
145 InconsistentFilesystemException.class,
Nathan Harmataad810502015-07-29 01:33:49 +0000146 FileSymlinkException.class).entrySet()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100147 PathFragment pkgName = ((PackageIdentifier) entry.getKey().argument()).getPackageFragment();
148 try {
149 PackageLookupValue value = (PackageLookupValue) entry.getValue().get();
150 if (value != null) {
151 builder.put(pkgName, value);
152 }
153 } catch (BuildFileNotFoundException e) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000154 maybeThrowFilesystemInconsistency(packageIdentifier, e, packageWasInError);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100155 } catch (InconsistentFilesystemException e) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000156 throw new InternalInconsistentFilesystemException(packageIdentifier, e);
Nathan Harmataad810502015-07-29 01:33:49 +0000157 } catch (FileSymlinkException e) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100158 // Legacy doesn't detect symlink cycles.
159 packageShouldBeInError = true;
160 }
161 }
162 return Pair.of(builder.build(), packageShouldBeInError);
163 }
164
165 private static boolean markFileDepsAndPropagateInconsistentFilesystemExceptions(
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000166 PackageIdentifier packageIdentifier, Iterable<SkyKey> depKeys, Environment env,
167 boolean packageWasInError) throws InternalInconsistentFilesystemException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100168 Preconditions.checkState(
169 Iterables.all(depKeys, SkyFunctions.isSkyFunction(SkyFunctions.FILE)), depKeys);
170 boolean packageShouldBeInError = packageWasInError;
Nathan Harmataad810502015-07-29 01:33:49 +0000171 for (Map.Entry<SkyKey, ValueOrException3<IOException, FileSymlinkException,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100172 InconsistentFilesystemException>> entry : env.getValuesOrThrow(depKeys, IOException.class,
Nathan Harmataad810502015-07-29 01:33:49 +0000173 FileSymlinkException.class, InconsistentFilesystemException.class).entrySet()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100174 try {
175 entry.getValue().get();
176 } catch (IOException e) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000177 maybeThrowFilesystemInconsistency(packageIdentifier, e, packageWasInError);
Nathan Harmataad810502015-07-29 01:33:49 +0000178 } catch (FileSymlinkException e) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100179 // Legacy doesn't detect symlink cycles.
180 packageShouldBeInError = true;
181 } catch (InconsistentFilesystemException e) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000182 throw new InternalInconsistentFilesystemException(packageIdentifier, e);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100183 }
184 }
185 return packageShouldBeInError;
186 }
187
188 private static boolean markGlobDepsAndPropagateInconsistentFilesystemExceptions(
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000189 PackageIdentifier packageIdentifier, Iterable<SkyKey> depKeys, Environment env,
190 boolean packageWasInError) throws InternalInconsistentFilesystemException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100191 Preconditions.checkState(
192 Iterables.all(depKeys, SkyFunctions.isSkyFunction(SkyFunctions.GLOB)), depKeys);
193 boolean packageShouldBeInError = packageWasInError;
194 for (Map.Entry<SkyKey, ValueOrException4<IOException, BuildFileNotFoundException,
Nathan Harmataad810502015-07-29 01:33:49 +0000195 FileSymlinkException, InconsistentFilesystemException>> entry :
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100196 env.getValuesOrThrow(depKeys, IOException.class, BuildFileNotFoundException.class,
Nathan Harmataad810502015-07-29 01:33:49 +0000197 FileSymlinkException.class, InconsistentFilesystemException.class).entrySet()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100198 try {
199 entry.getValue().get();
200 } catch (IOException | BuildFileNotFoundException e) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000201 maybeThrowFilesystemInconsistency(packageIdentifier, e, packageWasInError);
Nathan Harmataad810502015-07-29 01:33:49 +0000202 } catch (FileSymlinkException e) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100203 // Legacy doesn't detect symlink cycles.
204 packageShouldBeInError = true;
205 } catch (InconsistentFilesystemException e) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000206 throw new InternalInconsistentFilesystemException(packageIdentifier, e);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100207 }
208 }
209 return packageShouldBeInError;
210 }
211
212 /**
213 * Marks dependencies implicitly used by legacy package loading code, after the fact. Note that
214 * the given package might already be in error.
215 *
216 * <p>Any skyframe exceptions encountered here are ignored, as similar errors should have
217 * already been encountered by legacy package loading (if not, then the filesystem is
218 * inconsistent).
219 */
220 private static boolean markDependenciesAndPropagateInconsistentFilesystemExceptions(
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000221 Environment env,
222 Collection<Pair<String, Boolean>> globPatterns,
223 Map<Label, Path> subincludes,
224 PackageIdentifier packageIdentifier,
225 boolean containsErrors)
226 throws InternalInconsistentFilesystemException {
227 boolean packageShouldBeInError = containsErrors;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100228
229 // TODO(bazel-team): This means that many packages will have to be preprocessed twice. Ouch!
230 // We need a better continuation mechanism to avoid repeating work. [skyframe-loading]
231
232 // TODO(bazel-team): It would be preferable to perform I/O from the package preprocessor via
233 // Skyframe rather than add (potentially incomplete) dependencies after the fact.
234 // [skyframe-loading]
235
236 Set<SkyKey> subincludePackageLookupDepKeys = Sets.newHashSet();
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000237 for (Label label : subincludes.keySet()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100238 // Declare a dependency on the package lookup for the package giving access to the label.
Lukacs Berkif445ea12015-07-09 07:16:41 +0000239 subincludePackageLookupDepKeys.add(PackageLookupValue.key(label.getPackageIdentifier()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100240 }
241 Pair<? extends Map<PathFragment, PackageLookupValue>, Boolean> subincludePackageLookupResult =
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000242 getPackageLookupDepsAndPropagateInconsistentFilesystemExceptions(
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000243 packageIdentifier, subincludePackageLookupDepKeys, env, containsErrors);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100244 Map<PathFragment, PackageLookupValue> subincludePackageLookupDeps =
245 subincludePackageLookupResult.getFirst();
Mark Schaller6fc9f882015-06-12 17:53:57 +0000246 packageShouldBeInError |= subincludePackageLookupResult.getSecond();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100247 List<SkyKey> subincludeFileDepKeys = Lists.newArrayList();
248 for (Entry<Label, Path> subincludeEntry : subincludes.entrySet()) {
249 // Ideally, we would have a direct dependency on the target with the given label, but then
250 // subincluding a file from the same package will cause a dependency cycle, since targets
251 // depend on their containing packages.
252 Label label = subincludeEntry.getKey();
253 PackageLookupValue subincludePackageLookupValue =
254 subincludePackageLookupDeps.get(label.getPackageFragment());
255 if (subincludePackageLookupValue != null) {
256 // Declare a dependency on the actual file that was subincluded.
257 Path subincludeFilePath = subincludeEntry.getValue();
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000258 if (subincludeFilePath != null && !subincludePackageLookupValue.packageExists()) {
259 // Legacy blaze puts a non-null path when only when the package does indeed exist.
260 throw new InternalInconsistentFilesystemException(
261 packageIdentifier,
262 String.format(
263 "Unexpected package in %s. Was it modified during the build?",
264 subincludeFilePath));
265 }
266 if (subincludePackageLookupValue.packageExists()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100267 // Sanity check for consistency of Skyframe and legacy blaze.
268 Path subincludeFilePathSkyframe =
269 subincludePackageLookupValue.getRoot().getRelative(label.toPathFragment());
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000270 if (subincludeFilePath != null
271 && !subincludeFilePathSkyframe.equals(subincludeFilePath)) {
272 throw new InternalInconsistentFilesystemException(
273 packageIdentifier,
274 String.format(
275 "Inconsistent package location for %s: '%s' vs '%s'. "
276 + "Was the source tree modified during the build?",
277 label.getPackageFragment(),
278 subincludeFilePathSkyframe,
279 subincludeFilePath));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100280 }
281 // The actual file may be under a different package root than the package being
282 // constructed.
283 SkyKey subincludeSkyKey =
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000284 FileValue.key(
285 RootedPath.toRootedPath(
286 subincludePackageLookupValue.getRoot(),
287 label.getPackageFragment().getRelative(label.getName())));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100288 subincludeFileDepKeys.add(subincludeSkyKey);
289 }
290 }
291 }
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000292 packageShouldBeInError |=
293 markFileDepsAndPropagateInconsistentFilesystemExceptions(
294 packageIdentifier, subincludeFileDepKeys, env, containsErrors);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100295
296 // TODO(bazel-team): In the long term, we want to actually resolve the glob patterns within
297 // Skyframe. For now, just logging the glob requests provides correct incrementality and
298 // adequate performance.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100299 List<SkyKey> globDepKeys = Lists.newArrayList();
300 for (Pair<String, Boolean> globPattern : globPatterns) {
301 String pattern = globPattern.getFirst();
302 boolean excludeDirs = globPattern.getSecond();
303 SkyKey globSkyKey;
304 try {
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000305 globSkyKey =
306 GlobValue.key(packageIdentifier, pattern, excludeDirs, PathFragment.EMPTY_FRAGMENT);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100307 } catch (InvalidGlobPatternException e) {
308 // Globs that make it to pkg.getGlobPatterns() should already be filtered for errors.
309 throw new IllegalStateException(e);
310 }
311 globDepKeys.add(globSkyKey);
312 }
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000313 packageShouldBeInError |=
314 markGlobDepsAndPropagateInconsistentFilesystemExceptions(
315 packageIdentifier, globDepKeys, env, containsErrors);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100316 return packageShouldBeInError;
317 }
318
319 /**
320 * Adds a dependency on the WORKSPACE file, representing it as a special type of package.
321 * @throws PackageFunctionException if there is an error computing the workspace file or adding
322 * its rules to the //external package.
323 */
324 private SkyValue getExternalPackage(Environment env, Path packageLookupPath)
325 throws PackageFunctionException {
326 RootedPath workspacePath = RootedPath.toRootedPath(
327 packageLookupPath, new PathFragment("WORKSPACE"));
Kristina Chodorow91876f02015-02-27 17:14:12 +0000328 SkyKey workspaceKey = PackageValue.workspaceKey(workspacePath);
329 PackageValue workspace = null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100330 try {
Kristina Chodorow91876f02015-02-27 17:14:12 +0000331 workspace = (PackageValue) env.getValueOrThrow(workspaceKey, IOException.class,
Nathan Harmataad810502015-07-29 01:33:49 +0000332 FileSymlinkException.class, InconsistentFilesystemException.class,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100333 EvalException.class);
Nathan Harmataad810502015-07-29 01:33:49 +0000334 } catch (IOException | FileSymlinkException | InconsistentFilesystemException
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100335 | EvalException e) {
336 throw new PackageFunctionException(new BadWorkspaceFileException(e.getMessage()),
337 Transience.PERSISTENT);
338 }
339 if (workspace == null) {
340 return null;
341 }
342
343 Package pkg = workspace.getPackage();
344 Event.replayEventsOn(env.getListener(), pkg.getEvents());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100345
346 return new PackageValue(pkg);
347 }
348
349 @Override
350 public SkyValue compute(SkyKey key, Environment env) throws PackageFunctionException,
351 InterruptedException {
352 PackageIdentifier packageId = (PackageIdentifier) key.argument();
353 PathFragment packageNameFragment = packageId.getPackageFragment();
354 String packageName = packageNameFragment.getPathString();
355
356 SkyKey packageLookupKey = PackageLookupValue.key(packageId);
357 PackageLookupValue packageLookupValue;
358 try {
359 packageLookupValue = (PackageLookupValue)
360 env.getValueOrThrow(packageLookupKey, BuildFileNotFoundException.class,
361 InconsistentFilesystemException.class);
362 } catch (BuildFileNotFoundException e) {
363 throw new PackageFunctionException(e, Transience.PERSISTENT);
364 } catch (InconsistentFilesystemException e) {
365 // This error is not transient from the perspective of the PackageFunction.
366 throw new PackageFunctionException(
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000367 new InternalInconsistentFilesystemException(packageId, e), Transience.PERSISTENT);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100368 }
369 if (packageLookupValue == null) {
370 return null;
371 }
372
373 if (!packageLookupValue.packageExists()) {
374 switch (packageLookupValue.getErrorReason()) {
375 case NO_BUILD_FILE:
376 case DELETED_PACKAGE:
377 case NO_EXTERNAL_PACKAGE:
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000378 throw new PackageFunctionException(new BuildFileNotFoundException(packageId,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100379 packageLookupValue.getErrorMsg()), Transience.PERSISTENT);
380 case INVALID_PACKAGE_NAME:
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000381 throw new PackageFunctionException(new InvalidPackageNameException(packageId,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100382 packageLookupValue.getErrorMsg()), Transience.PERSISTENT);
383 default:
384 // We should never get here.
Ulf Adams07dba942015-03-05 14:47:37 +0000385 throw new IllegalStateException();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100386 }
387 }
388
Lukacs Berkia6434362015-09-15 13:56:14 +0000389 if (packageId.equals(ExternalPackage.PACKAGE_IDENTIFIER)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100390 return getExternalPackage(env, packageLookupValue.getRoot());
391 }
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000392 SkyKey externalPackageKey = PackageValue.key(ExternalPackage.PACKAGE_IDENTIFIER);
393 PackageValue externalPackage = (PackageValue) env.getValue(externalPackageKey);
Kristina Chodorow91876f02015-02-27 17:14:12 +0000394 if (externalPackage == null) {
395 return null;
396 }
397 Package externalPkg = externalPackage.getPackage();
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000398 if (externalPkg.containsErrors()) {
399 throw new PackageFunctionException(
400 new BuildFileContainsErrorsException(ExternalPackage.PACKAGE_IDENTIFIER),
401 Transience.PERSISTENT);
402 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100403
Googler9f212f72015-02-09 14:42:21 +0000404 PathFragment buildFileFragment = packageNameFragment.getChild("BUILD");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100405 RootedPath buildFileRootedPath = RootedPath.toRootedPath(packageLookupValue.getRoot(),
Googler9f212f72015-02-09 14:42:21 +0000406 buildFileFragment);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100407 FileValue buildFileValue;
408 try {
409 buildFileValue = (FileValue) env.getValueOrThrow(FileValue.key(buildFileRootedPath),
Nathan Harmataad810502015-07-29 01:33:49 +0000410 IOException.class, FileSymlinkException.class,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100411 InconsistentFilesystemException.class);
Nathan Harmataad810502015-07-29 01:33:49 +0000412 } catch (IOException | FileSymlinkException | InconsistentFilesystemException e) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100413 throw new IllegalStateException("Package lookup succeeded but encountered error when "
414 + "getting FileValue for BUILD file directly.", e);
415 }
416 if (buildFileValue == null) {
417 return null;
418 }
419 Preconditions.checkState(buildFileValue.exists(),
420 "Package lookup succeeded but BUILD file doesn't exist");
421 Path buildFilePath = buildFileRootedPath.asPath();
422
423 String replacementContents = null;
424 if (packageName.equals(DEFAULTS_PACKAGE_NAME)) {
425 replacementContents = PrecomputedValue.DEFAULTS_PACKAGE_CONTENTS.get(env);
426 if (replacementContents == null) {
427 return null;
428 }
429 }
430
431 RuleVisibility defaultVisibility = PrecomputedValue.DEFAULT_VISIBILITY.get(env);
432 if (defaultVisibility == null) {
433 return null;
434 }
435
436 ASTFileLookupValue astLookupValue = null;
Ulf Adams3ab82f72015-09-04 12:10:53 +0000437 SkyKey astLookupKey = ASTFileLookupValue.key(
438 PackageIdentifier.createInDefaultRepo(preludePath));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100439 try {
440 astLookupValue = (ASTFileLookupValue) env.getValueOrThrow(astLookupKey,
441 ErrorReadingSkylarkExtensionException.class, InconsistentFilesystemException.class);
442 } catch (ErrorReadingSkylarkExtensionException | InconsistentFilesystemException e) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000443 throw new PackageFunctionException(new BadPreludeFileException(packageId, e.getMessage()),
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100444 Transience.PERSISTENT);
445 }
446 if (astLookupValue == null) {
447 return null;
448 }
Nathan Harmata8da263c2015-02-24 01:10:22 +0000449 List<Statement> preludeStatements = astLookupValue.getAST() == null
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100450 ? ImmutableList.<Statement>of() : astLookupValue.getAST().getStatements();
451
452 // Load the BUILD file AST and handle Skylark dependencies. This way BUILD files are
453 // only loaded twice if there are unavailable Skylark or package dependencies or an
454 // IOException occurs. Note that the BUILD files are still parsed two times.
455 ParserInputSource inputSource;
456 try {
Eric Fellheimer35e57cf2015-05-27 14:31:11 +0000457 if (showLoadingProgress.get() && packageFunctionCache.getIfPresent(packageId) == null) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100458 // TODO(bazel-team): don't duplicate the loading message if there are unavailable
459 // Skylark dependencies.
460 reporter.handle(Event.progress("Loading package: " + packageName));
461 }
Nathan Harmata7f240222015-09-13 02:03:26 +0000462 inputSource = ParserInputSource.create(buildFilePath, buildFileValue.getSize());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100463 } catch (IOException e) {
464 env.getListener().handle(Event.error(Location.fromFile(buildFilePath), e.getMessage()));
465 // Note that we did this work, so we should conservatively report this error as transient.
466 throw new PackageFunctionException(new BuildFileContainsErrorsException(
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000467 packageId, e.getMessage()), Transience.TRANSIENT);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100468 }
Lukacs Berkif445ea12015-07-09 07:16:41 +0000469
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000470 Package.LegacyBuilder legacyPkgBuilder =
471 loadPackage(
472 externalPkg,
473 inputSource,
474 replacementContents,
475 packageId,
476 buildFilePath,
Han-Wen Nienhuys06480182015-08-07 15:22:35 +0000477 buildFileFragment,
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000478 defaultVisibility,
479 preludeStatements,
Han-Wen Nienhuys06480182015-08-07 15:22:35 +0000480 env);
481 if (legacyPkgBuilder == null) {
482 return null;
483 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100484 legacyPkgBuilder.buildPartial();
485 try {
486 handleLabelsCrossingSubpackagesAndPropagateInconsistentFilesystemExceptions(
487 packageLookupValue.getRoot(), packageId, legacyPkgBuilder, env);
488 } catch (InternalInconsistentFilesystemException e) {
Eric Fellheimer35e57cf2015-05-27 14:31:11 +0000489 packageFunctionCache.invalidate(packageId);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100490 throw new PackageFunctionException(e,
491 e.isTransient() ? Transience.TRANSIENT : Transience.PERSISTENT);
492 }
493 if (env.valuesMissing()) {
494 // The package we just loaded will be in the {@code packageFunctionCache} next when this
495 // SkyFunction is called again.
496 return null;
497 }
498 Collection<Pair<String, Boolean>> globPatterns = legacyPkgBuilder.getGlobPatterns();
499 Map<Label, Path> subincludes = legacyPkgBuilder.getSubincludes();
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000500 Event.replayEventsOn(env.getListener(), legacyPkgBuilder.getEvents());
Mark Schaller6fc9f882015-06-12 17:53:57 +0000501 boolean packageShouldBeConsideredInError;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100502 try {
503 packageShouldBeConsideredInError =
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000504 markDependenciesAndPropagateInconsistentFilesystemExceptions(
505 env, globPatterns, subincludes, packageId, legacyPkgBuilder.containsErrors());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100506 } catch (InternalInconsistentFilesystemException e) {
Eric Fellheimer35e57cf2015-05-27 14:31:11 +0000507 packageFunctionCache.invalidate(packageId);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100508 throw new PackageFunctionException(e,
509 e.isTransient() ? Transience.TRANSIENT : Transience.PERSISTENT);
510 }
511
512 if (env.valuesMissing()) {
513 return null;
514 }
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000515
516 if (packageShouldBeConsideredInError) {
517 legacyPkgBuilder.setContainsErrors();
518 }
519 Package pkg = legacyPkgBuilder.finishBuild();
520
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100521 // We know this SkyFunction will not be called again, so we can remove the cache entry.
Eric Fellheimer35e57cf2015-05-27 14:31:11 +0000522 packageFunctionCache.invalidate(packageId);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100523
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100524 return new PackageValue(pkg);
525 }
526
Han-Wen Nienhuys06480182015-08-07 15:22:35 +0000527 // TODO(bazel-team): this should take the AST so we don't parse the file twice.
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000528 @Nullable
529 private SkylarkImportResult discoverSkylarkImports(
530 Path buildFilePath,
531 PathFragment buildFileFragment,
532 PackageIdentifier packageId,
533 Environment env,
534 ParserInputSource inputSource,
535 List<Statement> preludeStatements)
536 throws PackageFunctionException {
537 StoredEventHandler eventHandler = new StoredEventHandler();
538 BuildFileAST buildFileAST =
Han-Wen Nienhuys06480182015-08-07 15:22:35 +0000539 BuildFileAST.parseBuildFile(
540 inputSource,
541 preludeStatements,
542 eventHandler,
Han-Wen Nienhuys06480182015-08-07 15:22:35 +0000543 /* parse python */ false);
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000544 SkylarkImportResult importResult;
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000545 if (eventHandler.hasErrors()) {
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000546 importResult =
547 new SkylarkImportResult(
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000548 ImmutableMap.<PathFragment, Extension>of(),
549 ImmutableList.<Label>of());
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000550 } else {
551 importResult =
552 fetchImportsFromBuildFile(buildFilePath, buildFileFragment, packageId, buildFileAST, env);
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000553 }
554
555 return importResult;
556 }
557
Han-Wen Nienhuys06480182015-08-07 15:22:35 +0000558 /**
559 * Fetch the skylark loads for this BUILD file. If any of them haven't been computed yet,
560 * returns null.
561 */
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000562 @Nullable
563 private SkylarkImportResult fetchImportsFromBuildFile(
564 Path buildFilePath,
565 PathFragment buildFileFragment,
566 PackageIdentifier packageId,
567 BuildFileAST buildFileAST,
568 Environment env)
Lukacs Berkif445ea12015-07-09 07:16:41 +0000569 throws PackageFunctionException {
Lukacs Berki145b7012015-08-27 14:20:57 +0000570 ImmutableMap<Location, PathFragment> imports = buildFileAST.getImports();
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000571 Map<PathFragment, Extension> importMap = new HashMap<>();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100572 ImmutableList.Builder<SkylarkFileDependency> fileDependencies = ImmutableList.builder();
573 try {
Lukacs Berki145b7012015-08-27 14:20:57 +0000574 for (Map.Entry<Location, PathFragment> entry : imports.entrySet()) {
575 PathFragment importFile = entry.getValue();
576 // HACK: The prelude sometimes contains load() statements, which need to be resolved
577 // relative to the prelude file. However, we don't have a good way to tell "this should come
578 // from the main repository" in a load() statement, and we don't have a good way to tell if
579 // a load() statement comes from the prelude, since we just prepend those statements before
580 // the actual BUILD file. So we use this evil .endsWith() statement to figure it out.
581 RepositoryName repository =
Ulf Adamsfdfdd922015-09-01 08:36:29 +0000582 entry.getKey().getPath().endsWith(preludePath)
Lukacs Berki145b7012015-08-27 14:20:57 +0000583 ? PackageIdentifier.DEFAULT_REPOSITORY_NAME : packageId.getRepository();
584 SkyKey importsLookupKey = SkylarkImportLookupValue.key(
585 repository, buildFileFragment, importFile);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100586 SkylarkImportLookupValue importLookupValue = (SkylarkImportLookupValue)
587 env.getValueOrThrow(importsLookupKey, SkylarkImportFailedException.class,
588 InconsistentFilesystemException.class, ASTLookupInputException.class,
589 BuildFileNotFoundException.class);
590 if (importLookupValue != null) {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000591 importMap.put(importFile, importLookupValue.getEnvironmentExtension());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100592 fileDependencies.add(importLookupValue.getDependency());
593 }
594 }
595 } catch (SkylarkImportFailedException e) {
596 env.getListener().handle(Event.error(Location.fromFile(buildFilePath), e.getMessage()));
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000597 throw new PackageFunctionException(
598 new BuildFileContainsErrorsException(packageId, e.getMessage()), Transience.PERSISTENT);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100599 } catch (InconsistentFilesystemException e) {
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000600 throw new PackageFunctionException(
601 new InternalInconsistentFilesystemException(packageId, e), Transience.PERSISTENT);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100602 } catch (ASTLookupInputException e) {
603 // The load syntax is bad in the BUILD file so BuildFileContainsErrorsException is OK.
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000604 throw new PackageFunctionException(
605 new BuildFileContainsErrorsException(packageId, e.getMessage()), Transience.PERSISTENT);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100606 } catch (BuildFileNotFoundException e) {
607 throw new PackageFunctionException(e, Transience.PERSISTENT);
608 }
609 if (env.valuesMissing()) {
610 // There are unavailable Skylark dependencies.
611 return null;
612 }
613 return new SkylarkImportResult(importMap, transitiveClosureOfLabels(fileDependencies.build()));
614 }
615
616 private ImmutableList<Label> transitiveClosureOfLabels(
617 ImmutableList<SkylarkFileDependency> immediateDeps) {
618 Set<Label> transitiveClosure = Sets.newHashSet();
619 transitiveClosureOfLabels(immediateDeps, transitiveClosure);
620 return ImmutableList.copyOf(transitiveClosure);
621 }
622
623 private void transitiveClosureOfLabels(
624 ImmutableList<SkylarkFileDependency> immediateDeps, Set<Label> transitiveClosure) {
625 for (SkylarkFileDependency dep : immediateDeps) {
Ulf Adams07dba942015-03-05 14:47:37 +0000626 if (transitiveClosure.add(dep.getLabel())) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100627 transitiveClosureOfLabels(dep.getDependencies(), transitiveClosure);
628 }
629 }
630 }
631
632 @Nullable
633 @Override
634 public String extractTag(SkyKey skyKey) {
635 return null;
636 }
637
638 private static void handleLabelsCrossingSubpackagesAndPropagateInconsistentFilesystemExceptions(
639 Path pkgRoot, PackageIdentifier pkgId, Package.LegacyBuilder pkgBuilder, Environment env)
640 throws InternalInconsistentFilesystemException {
641 Set<SkyKey> containingPkgLookupKeys = Sets.newHashSet();
642 Map<Target, SkyKey> targetToKey = new HashMap<>();
643 for (Target target : pkgBuilder.getTargets()) {
644 PathFragment dir = target.getLabel().toPathFragment().getParentDirectory();
645 PackageIdentifier dirId = new PackageIdentifier(pkgId.getRepository(), dir);
646 if (dir.equals(pkgId.getPackageFragment())) {
647 continue;
648 }
649 SkyKey key = ContainingPackageLookupValue.key(dirId);
650 targetToKey.put(target, key);
651 containingPkgLookupKeys.add(key);
652 }
653 Map<Label, SkyKey> subincludeToKey = new HashMap<>();
654 for (Label subincludeLabel : pkgBuilder.getSubincludeLabels()) {
655 PathFragment dir = subincludeLabel.toPathFragment().getParentDirectory();
656 PackageIdentifier dirId = new PackageIdentifier(pkgId.getRepository(), dir);
657 if (dir.equals(pkgId.getPackageFragment())) {
658 continue;
659 }
660 SkyKey key = ContainingPackageLookupValue.key(dirId);
661 subincludeToKey.put(subincludeLabel, key);
662 containingPkgLookupKeys.add(ContainingPackageLookupValue.key(dirId));
663 }
664 Map<SkyKey, ValueOrException3<BuildFileNotFoundException, InconsistentFilesystemException,
Nathan Harmataad810502015-07-29 01:33:49 +0000665 FileSymlinkException>> containingPkgLookupValues = env.getValuesOrThrow(
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100666 containingPkgLookupKeys, BuildFileNotFoundException.class,
Nathan Harmataad810502015-07-29 01:33:49 +0000667 InconsistentFilesystemException.class, FileSymlinkException.class);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100668 if (env.valuesMissing()) {
669 return;
670 }
671 for (Target target : ImmutableSet.copyOf(pkgBuilder.getTargets())) {
672 SkyKey key = targetToKey.get(target);
673 if (!containingPkgLookupValues.containsKey(key)) {
674 continue;
675 }
676 ContainingPackageLookupValue containingPackageLookupValue =
677 getContainingPkgLookupValueAndPropagateInconsistentFilesystemExceptions(
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000678 pkgId, containingPkgLookupValues.get(key), env);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100679 if (maybeAddEventAboutLabelCrossingSubpackage(pkgBuilder, pkgRoot, target.getLabel(),
680 target.getLocation(), containingPackageLookupValue)) {
681 pkgBuilder.removeTarget(target);
682 pkgBuilder.setContainsErrors();
683 }
684 }
685 for (Label subincludeLabel : pkgBuilder.getSubincludeLabels()) {
686 SkyKey key = subincludeToKey.get(subincludeLabel);
687 if (!containingPkgLookupValues.containsKey(key)) {
688 continue;
689 }
690 ContainingPackageLookupValue containingPackageLookupValue =
691 getContainingPkgLookupValueAndPropagateInconsistentFilesystemExceptions(
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000692 pkgId, containingPkgLookupValues.get(key), env);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100693 if (maybeAddEventAboutLabelCrossingSubpackage(pkgBuilder, pkgRoot, subincludeLabel,
694 /*location=*/null, containingPackageLookupValue)) {
695 pkgBuilder.setContainsErrors();
696 }
697 }
698 }
699
700 @Nullable
701 private static ContainingPackageLookupValue
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000702 getContainingPkgLookupValueAndPropagateInconsistentFilesystemExceptions(
703 PackageIdentifier packageIdentifier,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100704 ValueOrException3<BuildFileNotFoundException, InconsistentFilesystemException,
Nathan Harmataad810502015-07-29 01:33:49 +0000705 FileSymlinkException> containingPkgLookupValueOrException, Environment env)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100706 throws InternalInconsistentFilesystemException {
707 try {
708 return (ContainingPackageLookupValue) containingPkgLookupValueOrException.get();
Nathan Harmataad810502015-07-29 01:33:49 +0000709 } catch (BuildFileNotFoundException | FileSymlinkException e) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100710 env.getListener().handle(Event.error(null, e.getMessage()));
711 return null;
712 } catch (InconsistentFilesystemException e) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000713 throw new InternalInconsistentFilesystemException(packageIdentifier, e);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100714 }
715 }
716
717 private static boolean maybeAddEventAboutLabelCrossingSubpackage(
718 Package.LegacyBuilder pkgBuilder, Path pkgRoot, Label label, @Nullable Location location,
719 @Nullable ContainingPackageLookupValue containingPkgLookupValue) {
720 if (containingPkgLookupValue == null) {
721 return true;
722 }
723 if (!containingPkgLookupValue.hasContainingPackage()) {
724 // The missing package here is a problem, but it's not an error from the perspective of
725 // PackageFunction.
726 return false;
727 }
728 PackageIdentifier containingPkg = containingPkgLookupValue.getContainingPackageName();
729 if (containingPkg.equals(label.getPackageIdentifier())) {
730 // The label does not cross a subpackage boundary.
731 return false;
732 }
Kristina Chodorow1782a8f2015-08-12 11:35:07 +0000733 if (!containingPkg.getPathFragment().startsWith(
734 label.getPackageIdentifier().getPathFragment())) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100735 // This label is referencing an imaginary package, because the containing package should
736 // extend the label's package: if the label is //a/b:c/d, the containing package could be
737 // //a/b/c or //a/b, but should never be //a. Usually such errors will be caught earlier, but
738 // in some exceptional cases (such as a Python-aware BUILD file catching its own io
739 // exceptions), it reaches here, and we tolerate it.
740 return false;
741 }
742 PathFragment labelNameFragment = new PathFragment(label.getName());
743 String message = String.format("Label '%s' crosses boundary of subpackage '%s'",
744 label, containingPkg);
745 Path containingRoot = containingPkgLookupValue.getContainingPackageRoot();
746 if (pkgRoot.equals(containingRoot)) {
747 PathFragment labelNameInContainingPackage = labelNameFragment.subFragment(
748 containingPkg.getPackageFragment().segmentCount()
749 - label.getPackageFragment().segmentCount(),
750 labelNameFragment.segmentCount());
751 message += " (perhaps you meant to put the colon here: "
752 + "'//" + containingPkg + ":" + labelNameInContainingPackage + "'?)";
753 } else {
754 message += " (have you deleted " + containingPkg + "/BUILD? "
755 + "If so, use the --deleted_packages=" + containingPkg + " option)";
756 }
757 pkgBuilder.addEvent(Event.error(location, message));
758 return true;
759 }
760
761 /**
762 * Constructs a {@link Package} object for the given package using legacy package loading.
763 * Note that the returned package may be in error.
Han-Wen Nienhuys06480182015-08-07 15:22:35 +0000764 *
765 * <p>May return null if the computation has to be restarted.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100766 */
Han-Wen Nienhuys06480182015-08-07 15:22:35 +0000767 @Nullable
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000768 private Package.LegacyBuilder loadPackage(
769 Package externalPkg,
770 ParserInputSource inputSource,
771 @Nullable String replacementContents,
772 PackageIdentifier packageId,
773 Path buildFilePath,
Han-Wen Nienhuys06480182015-08-07 15:22:35 +0000774 PathFragment buildFileFragment,
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000775 RuleVisibility defaultVisibility,
776 List<Statement> preludeStatements,
Han-Wen Nienhuys06480182015-08-07 15:22:35 +0000777 Environment env)
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000778 throws InterruptedException, PackageFunctionException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100779 ParserInputSource replacementSource = replacementContents == null ? null
Lukacs Berki48338222015-06-12 11:37:46 +0000780 : ParserInputSource.create(replacementContents, buildFilePath.asFragment());
Eric Fellheimer35e57cf2015-05-27 14:31:11 +0000781 Package.LegacyBuilder pkgBuilder = packageFunctionCache.getIfPresent(packageId);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100782 if (pkgBuilder == null) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100783 profiler.startTask(ProfilerTask.CREATE_PACKAGE, packageId.toString());
784 try {
785 Globber globber = packageFactory.createLegacyGlobber(buildFilePath.getParentDirectory(),
786 packageId, packageLocator);
Ulf Adamsd9b51212015-09-04 15:14:22 +0000787 Preprocessor.Result preprocessingResult = preprocessCache.getIfPresent(packageId);
788 if (preprocessingResult == null) {
Janak Ramakrishnanac023bb2015-09-15 19:37:11 +0000789 try {
790 preprocessingResult =
791 replacementSource == null
792 ? packageFactory.preprocess(packageId, inputSource, globber)
793 : Preprocessor.Result.noPreprocessing(replacementSource);
794 } catch (IOException e) {
795 env
796 .getListener()
797 .handle(
798 Event.error(
799 Location.fromFile(buildFilePath),
800 "preprocessing failed: " + e.getMessage()));
801 throw new PackageFunctionException(
802 new BuildFileContainsErrorsException(packageId, "preprocessing failed", e),
803 Transience.TRANSIENT);
804 }
Ulf Adamsd9b51212015-09-04 15:14:22 +0000805 preprocessCache.put(packageId, preprocessingResult);
806 }
Han-Wen Nienhuys06480182015-08-07 15:22:35 +0000807
808 SkylarkImportResult importResult =
809 discoverSkylarkImports(
810 buildFilePath,
811 buildFileFragment,
812 packageId,
813 env,
814 preprocessingResult.result,
815 preludeStatements);
816 if (importResult == null) {
817 return null;
818 }
Ulf Adamsd9b51212015-09-04 15:14:22 +0000819 preprocessCache.invalidate(packageId);
Han-Wen Nienhuys06480182015-08-07 15:22:35 +0000820
Kristina Chodorow91876f02015-02-27 17:14:12 +0000821 pkgBuilder = packageFactory.createPackageFromPreprocessingResult(externalPkg, packageId,
Ulf Adams14f8c252015-09-09 14:56:35 +0000822 buildFilePath, preprocessingResult, preprocessingResult.events, preludeStatements,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100823 importResult.importMap, importResult.fileDependencies, packageLocator,
824 defaultVisibility, globber);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100825 numPackagesLoaded.incrementAndGet();
826 packageFunctionCache.put(packageId, pkgBuilder);
827 } finally {
828 profiler.completeTask(ProfilerTask.CREATE_PACKAGE);
829 }
830 }
831 return pkgBuilder;
832 }
833
834 private static class InternalInconsistentFilesystemException extends NoSuchPackageException {
835 private boolean isTransient;
836
837 /**
838 * Used to represent a filesystem inconsistency discovered outside the
839 * {@link PackageFunction}.
840 */
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000841 public InternalInconsistentFilesystemException(PackageIdentifier packageIdentifier,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100842 InconsistentFilesystemException e) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000843 super(packageIdentifier, e.getMessage(), e);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100844 // This is not a transient error from the perspective of the PackageFunction.
845 this.isTransient = false;
846 }
847
848 /** Used to represent a filesystem inconsistency discovered by the {@link PackageFunction}. */
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000849 public InternalInconsistentFilesystemException(PackageIdentifier packageIdentifier,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100850 String inconsistencyMessage) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000851 this(packageIdentifier, new InconsistentFilesystemException(inconsistencyMessage));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100852 this.isTransient = true;
853 }
854
855 public boolean isTransient() {
856 return isTransient;
857 }
858 }
859
860 private static class BadWorkspaceFileException extends NoSuchPackageException {
861 private BadWorkspaceFileException(String message) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000862 super(ExternalPackage.PACKAGE_IDENTIFIER,
863 "Error encountered while dealing with the WORKSPACE file: " + message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100864 }
865 }
866
867 private static class BadPreludeFileException extends NoSuchPackageException {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000868 private BadPreludeFileException(PackageIdentifier packageIdentifier, String message) {
869 super(packageIdentifier, "Error encountered while reading the prelude file: " + message);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100870 }
871 }
872
873 /**
874 * Used to declare all the exception types that can be wrapped in the exception thrown by
875 * {@link PackageFunction#compute}.
876 */
877 private static class PackageFunctionException extends SkyFunctionException {
878 public PackageFunctionException(NoSuchPackageException e, Transience transience) {
879 super(e, transience);
880 }
881 }
882
883 /** A simple value class to store the result of the Skylark imports.*/
884 private static final class SkylarkImportResult {
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000885 private final Map<PathFragment, Extension> importMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100886 private final ImmutableList<Label> fileDependencies;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000887 private SkylarkImportResult(
888 Map<PathFragment, Extension> importMap,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100889 ImmutableList<Label> fileDependencies) {
890 this.importMap = importMap;
891 this.fileDependencies = fileDependencies;
892 }
893 }
894}