blob: 9db19e5b4e03038e78d85a9d15b6a0f5d8fe15c6 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
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
Eric Fellheimer35e57cf2015-05-27 14:31:11 +000016import com.google.common.cache.Cache;
John Fielda97e17f2015-11-13 02:19:52 +000017import com.google.common.collect.ImmutableCollection;
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;
Janak Ramakrishnandfd34972015-09-22 02:53:05 +000023import com.google.common.collect.Maps;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010024import com.google.common.collect.Sets;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000025import com.google.devtools.build.lib.cmdline.Label;
John Fielda97e17f2015-11-13 02:19:52 +000026import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
Kristina Chodorow73fa2032015-08-28 17:57:46 +000027import com.google.devtools.build.lib.cmdline.PackageIdentifier;
Nathan Harmata44e1e3a2016-08-23 21:22:17 +000028import com.google.devtools.build.lib.collect.nestedset.NestedSet;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010029import com.google.devtools.build.lib.events.Event;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010030import com.google.devtools.build.lib.events.Location;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031import com.google.devtools.build.lib.events.StoredEventHandler;
32import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException;
33import com.google.devtools.build.lib.packages.BuildFileNotFoundException;
34import com.google.devtools.build.lib.packages.CachingPackageLocator;
Nathan Harmatac5a15d32016-02-04 23:14:29 +000035import com.google.devtools.build.lib.packages.Globber;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010036import com.google.devtools.build.lib.packages.InvalidPackageNameException;
37import com.google.devtools.build.lib.packages.NoSuchPackageException;
38import com.google.devtools.build.lib.packages.Package;
39import com.google.devtools.build.lib.packages.PackageFactory;
Nathan Harmata44e1e3a2016-08-23 21:22:17 +000040import com.google.devtools.build.lib.packages.PackageFactory.LegacyGlobber;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010041import com.google.devtools.build.lib.packages.Preprocessor;
Nathan Harmata9faad192015-10-15 18:41:24 +000042import com.google.devtools.build.lib.packages.Preprocessor.AstAfterPreprocessing;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010043import com.google.devtools.build.lib.packages.RuleVisibility;
44import com.google.devtools.build.lib.packages.Target;
45import com.google.devtools.build.lib.profiler.Profiler;
46import com.google.devtools.build.lib.profiler.ProfilerTask;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010047import com.google.devtools.build.lib.skyframe.GlobValue.InvalidGlobPatternException;
48import com.google.devtools.build.lib.skyframe.SkylarkImportLookupFunction.SkylarkImportFailedException;
49import com.google.devtools.build.lib.syntax.BuildFileAST;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +000050import com.google.devtools.build.lib.syntax.Environment.Extension;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010051import com.google.devtools.build.lib.syntax.EvalException;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010052import com.google.devtools.build.lib.syntax.ParserInputSource;
John Field1ea7fc32015-12-22 19:37:19 +000053import com.google.devtools.build.lib.syntax.SkylarkImport;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010054import com.google.devtools.build.lib.syntax.Statement;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010055import com.google.devtools.build.lib.util.Pair;
Mark Schaller6df81792015-12-10 18:47:47 +000056import com.google.devtools.build.lib.util.Preconditions;
Nathan Harmata4e698242015-10-20 23:18:23 +000057import com.google.devtools.build.lib.vfs.FileSystemUtils;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010058import com.google.devtools.build.lib.vfs.Path;
59import com.google.devtools.build.lib.vfs.PathFragment;
60import com.google.devtools.build.lib.vfs.RootedPath;
61import com.google.devtools.build.skyframe.SkyFunction;
62import com.google.devtools.build.skyframe.SkyFunctionException;
63import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
64import com.google.devtools.build.skyframe.SkyKey;
65import com.google.devtools.build.skyframe.SkyValue;
John Fielda97e17f2015-11-13 02:19:52 +000066import com.google.devtools.build.skyframe.ValueOrException2;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010067import com.google.devtools.build.skyframe.ValueOrException3;
68import com.google.devtools.build.skyframe.ValueOrException4;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010069import java.io.IOException;
Nathan Harmata3a95f352016-02-05 00:11:55 +000070import java.util.ArrayList;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010071import java.util.Collection;
Janak Ramakrishnan063b4882016-07-18 20:33:28 +000072import java.util.Collections;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010073import java.util.HashMap;
Nathan Harmata44e1e3a2016-08-23 21:22:17 +000074import java.util.HashSet;
Janak Ramakrishnanf4dd2832016-10-10 23:56:01 +000075import java.util.LinkedHashMap;
Nathan Harmata3a95f352016-02-05 00:11:55 +000076import java.util.LinkedHashSet;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010077import java.util.List;
78import java.util.Map;
79import java.util.Map.Entry;
80import java.util.Set;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010081import java.util.concurrent.atomic.AtomicBoolean;
82import java.util.concurrent.atomic.AtomicInteger;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010083import javax.annotation.Nullable;
84
85/**
86 * A SkyFunction for {@link PackageValue}s.
87 */
88public class PackageFunction implements SkyFunction {
89
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010090 private final PackageFactory packageFactory;
91 private final CachingPackageLocator packageLocator;
Nathan Harmata6b253fa2016-05-24 15:27:18 +000092 private final Cache<PackageIdentifier, CacheEntryWithGlobDeps<Package.Builder>>
Nathan Harmata3a95f352016-02-05 00:11:55 +000093 packageFunctionCache;
94 private final Cache<PackageIdentifier, CacheEntryWithGlobDeps<AstAfterPreprocessing>> astCache;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010095 private final AtomicBoolean showLoadingProgress;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010096 private final AtomicInteger numPackagesLoaded;
Klaus Aehligc6fd6bb2016-05-27 11:42:32 +000097 private final PackageProgressReceiver packageProgress;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010098 private final Profiler profiler = Profiler.instance();
John Fielda97e17f2015-11-13 02:19:52 +000099 private final Label preludeLabel;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100100
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +0000101 // Not final only for testing.
102 @Nullable private SkylarkImportLookupFunction skylarkImportLookupFunctionForInlining;
103
Lukacs Berki5131ef92015-10-09 14:20:03 +0000104 static final PathFragment DEFAULTS_PACKAGE_NAME = new PathFragment("tools/defaults");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100105
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +0000106 public PackageFunction(
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +0000107 PackageFactory packageFactory,
108 CachingPackageLocator pkgLocator,
109 AtomicBoolean showLoadingProgress,
Nathan Harmata6b253fa2016-05-24 15:27:18 +0000110 Cache<PackageIdentifier, CacheEntryWithGlobDeps<Package.Builder>> packageFunctionCache,
Nathan Harmata3a95f352016-02-05 00:11:55 +0000111 Cache<PackageIdentifier, CacheEntryWithGlobDeps<AstAfterPreprocessing>> astCache,
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +0000112 AtomicInteger numPackagesLoaded,
Klaus Aehligc6fd6bb2016-05-27 11:42:32 +0000113 @Nullable SkylarkImportLookupFunction skylarkImportLookupFunctionForInlining,
114 @Nullable PackageProgressReceiver packageProgress) {
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +0000115 this.skylarkImportLookupFunctionForInlining = skylarkImportLookupFunctionForInlining;
Ulf Adamsfdfdd922015-09-01 08:36:29 +0000116 // Can be null in tests.
John Fielda97e17f2015-11-13 02:19:52 +0000117 this.preludeLabel = packageFactory == null
Ulf Adamsfdfdd922015-09-01 08:36:29 +0000118 ? null
John Fielda97e17f2015-11-13 02:19:52 +0000119 : packageFactory.getRuleClassProvider().getPreludeLabel();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100120 this.packageFactory = packageFactory;
121 this.packageLocator = pkgLocator;
122 this.showLoadingProgress = showLoadingProgress;
123 this.packageFunctionCache = packageFunctionCache;
Nathan Harmata9faad192015-10-15 18:41:24 +0000124 this.astCache = astCache;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100125 this.numPackagesLoaded = numPackagesLoaded;
Klaus Aehligc6fd6bb2016-05-27 11:42:32 +0000126 this.packageProgress = packageProgress;
127 }
128
129 public PackageFunction(
130 PackageFactory packageFactory,
131 CachingPackageLocator pkgLocator,
132 AtomicBoolean showLoadingProgress,
133 Cache<PackageIdentifier, CacheEntryWithGlobDeps<Package.Builder>> packageFunctionCache,
134 Cache<PackageIdentifier, CacheEntryWithGlobDeps<AstAfterPreprocessing>> astCache,
135 AtomicInteger numPackagesLoaded,
136 @Nullable SkylarkImportLookupFunction skylarkImportLookupFunctionForInlining) {
137 this(
138 packageFactory,
139 pkgLocator,
140 showLoadingProgress,
141 packageFunctionCache,
142 astCache,
143 numPackagesLoaded,
144 skylarkImportLookupFunctionForInlining,
145 null);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100146 }
147
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +0000148 public void setSkylarkImportLookupFunctionForInliningForTesting(
149 SkylarkImportLookupFunction skylarkImportLookupFunctionForInlining) {
150 this.skylarkImportLookupFunctionForInlining = skylarkImportLookupFunctionForInlining;
151 }
152
Nathan Harmata3a95f352016-02-05 00:11:55 +0000153 /** An entry in {@link PackageFunction}'s internal caches. */
154 public static class CacheEntryWithGlobDeps<T> {
155 private final T value;
156 private final Set<SkyKey> globDepKeys;
157 @Nullable
Nathan Harmata44e1e3a2016-08-23 21:22:17 +0000158 private final LegacyGlobber legacyGlobber;
Nathan Harmata3a95f352016-02-05 00:11:55 +0000159
160 private CacheEntryWithGlobDeps(T value, Set<SkyKey> globDepKeys,
Nathan Harmata44e1e3a2016-08-23 21:22:17 +0000161 @Nullable LegacyGlobber legacyGlobber) {
Nathan Harmata3a95f352016-02-05 00:11:55 +0000162 this.value = value;
163 this.globDepKeys = globDepKeys;
164 this.legacyGlobber = legacyGlobber;
165 }
166 }
167
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000168 private static void maybeThrowFilesystemInconsistency(PackageIdentifier packageIdentifier,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100169 Exception skyframeException, boolean packageWasInError)
170 throws InternalInconsistentFilesystemException {
171 if (!packageWasInError) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000172 throw new InternalInconsistentFilesystemException(packageIdentifier, "Encountered error '"
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100173 + skyframeException.getMessage() + "' but didn't encounter it when doing the same thing "
174 + "earlier in the build");
175 }
176 }
177
178 /**
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000179 * Marks the given dependencies, and returns those already present. Ignores any exception thrown
180 * while building the dependency, except for filesystem inconsistencies.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100181 *
182 * <p>We need to mark dependencies implicitly used by the legacy package loading code, but we
183 * don't care about any skyframe errors since the package knows whether it's in error or not.
184 */
185 private static Pair<? extends Map<PathFragment, PackageLookupValue>, Boolean>
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000186 getPackageLookupDepsAndPropagateInconsistentFilesystemExceptions(
187 PackageIdentifier packageIdentifier,
188 Iterable<SkyKey> depKeys,
189 Environment env,
190 boolean packageWasInError)
191 throws InternalInconsistentFilesystemException, InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100192 Preconditions.checkState(
193 Iterables.all(depKeys, SkyFunctions.isSkyFunction(SkyFunctions.PACKAGE_LOOKUP)), depKeys);
194 boolean packageShouldBeInError = packageWasInError;
195 ImmutableMap.Builder<PathFragment, PackageLookupValue> builder = ImmutableMap.builder();
196 for (Map.Entry<SkyKey, ValueOrException3<BuildFileNotFoundException,
Nathan Harmataad810502015-07-29 01:33:49 +0000197 InconsistentFilesystemException, FileSymlinkException>> entry :
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100198 env.getValuesOrThrow(depKeys, BuildFileNotFoundException.class,
199 InconsistentFilesystemException.class,
Nathan Harmataad810502015-07-29 01:33:49 +0000200 FileSymlinkException.class).entrySet()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100201 PathFragment pkgName = ((PackageIdentifier) entry.getKey().argument()).getPackageFragment();
202 try {
203 PackageLookupValue value = (PackageLookupValue) entry.getValue().get();
204 if (value != null) {
205 builder.put(pkgName, value);
206 }
207 } catch (BuildFileNotFoundException e) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000208 maybeThrowFilesystemInconsistency(packageIdentifier, e, packageWasInError);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100209 } catch (InconsistentFilesystemException e) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000210 throw new InternalInconsistentFilesystemException(packageIdentifier, e);
Nathan Harmataad810502015-07-29 01:33:49 +0000211 } catch (FileSymlinkException e) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100212 // Legacy doesn't detect symlink cycles.
213 packageShouldBeInError = true;
214 }
215 }
216 return Pair.of(builder.build(), packageShouldBeInError);
217 }
218
Janak Ramakrishnanfb040f62016-06-23 16:05:04 +0000219 private static boolean markFileDepsAndPropagateFilesystemExceptions(
220 PackageIdentifier packageIdentifier,
221 Iterable<SkyKey> depKeys,
222 Environment env,
223 boolean packageWasInError)
224 throws InternalInconsistentFilesystemException, FileOutsidePackageRootsException,
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000225 SymlinkOutsidePackageRootsException, InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100226 Preconditions.checkState(
227 Iterables.all(depKeys, SkyFunctions.isSkyFunction(SkyFunctions.FILE)), depKeys);
228 boolean packageShouldBeInError = packageWasInError;
Nathan Harmataad810502015-07-29 01:33:49 +0000229 for (Map.Entry<SkyKey, ValueOrException3<IOException, FileSymlinkException,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100230 InconsistentFilesystemException>> entry : env.getValuesOrThrow(depKeys, IOException.class,
Nathan Harmataad810502015-07-29 01:33:49 +0000231 FileSymlinkException.class, InconsistentFilesystemException.class).entrySet()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100232 try {
233 entry.getValue().get();
Janak Ramakrishnanfb040f62016-06-23 16:05:04 +0000234 } catch (FileOutsidePackageRootsException | SymlinkOutsidePackageRootsException e) {
235 throw e;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100236 } catch (IOException e) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000237 maybeThrowFilesystemInconsistency(packageIdentifier, e, packageWasInError);
Nathan Harmataad810502015-07-29 01:33:49 +0000238 } catch (FileSymlinkException e) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100239 // Legacy doesn't detect symlink cycles.
240 packageShouldBeInError = true;
241 } catch (InconsistentFilesystemException e) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000242 throw new InternalInconsistentFilesystemException(packageIdentifier, e);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100243 }
244 }
245 return packageShouldBeInError;
246 }
247
Nathan Harmata3a95f352016-02-05 00:11:55 +0000248 /**
249 * These deps have already been marked (see {@link SkyframeHybridGlobber}) but we need to properly
250 * handle some errors that legacy package loading can't handle gracefully.
251 */
Janak Ramakrishnanfb040f62016-06-23 16:05:04 +0000252 private static boolean handleGlobDepsAndPropagateFilesystemExceptions(
253 PackageIdentifier packageIdentifier,
254 Iterable<SkyKey> depKeys,
255 Environment env,
256 boolean packageWasInError)
257 throws InternalInconsistentFilesystemException, FileOutsidePackageRootsException,
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000258 SymlinkOutsidePackageRootsException, InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100259 Preconditions.checkState(
260 Iterables.all(depKeys, SkyFunctions.isSkyFunction(SkyFunctions.GLOB)), depKeys);
261 boolean packageShouldBeInError = packageWasInError;
262 for (Map.Entry<SkyKey, ValueOrException4<IOException, BuildFileNotFoundException,
Nathan Harmataad810502015-07-29 01:33:49 +0000263 FileSymlinkException, InconsistentFilesystemException>> entry :
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100264 env.getValuesOrThrow(depKeys, IOException.class, BuildFileNotFoundException.class,
Nathan Harmataad810502015-07-29 01:33:49 +0000265 FileSymlinkException.class, InconsistentFilesystemException.class).entrySet()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100266 try {
267 entry.getValue().get();
Janak Ramakrishnanfb040f62016-06-23 16:05:04 +0000268 } catch (FileOutsidePackageRootsException | SymlinkOutsidePackageRootsException e) {
269 throw e;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100270 } catch (IOException | BuildFileNotFoundException e) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000271 maybeThrowFilesystemInconsistency(packageIdentifier, e, packageWasInError);
Nathan Harmataad810502015-07-29 01:33:49 +0000272 } catch (FileSymlinkException e) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100273 // Legacy doesn't detect symlink cycles.
274 packageShouldBeInError = true;
275 } catch (InconsistentFilesystemException e) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000276 throw new InternalInconsistentFilesystemException(packageIdentifier, e);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100277 }
278 }
279 return packageShouldBeInError;
280 }
281
282 /**
283 * Marks dependencies implicitly used by legacy package loading code, after the fact. Note that
284 * the given package might already be in error.
285 *
Janak Ramakrishnanfb040f62016-06-23 16:05:04 +0000286 * <p>Most skyframe exceptions encountered here are ignored, as similar errors should have already
287 * been encountered by legacy package loading (if not, then the filesystem is inconsistent). Some
288 * exceptions that Skyframe is stricter about (disallowed access to files outside package roots)
289 * are propagated.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100290 */
Janak Ramakrishnanfb040f62016-06-23 16:05:04 +0000291 private static boolean markDependenciesAndPropagateFilesystemExceptions(
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000292 Environment env,
Nathan Harmata3a95f352016-02-05 00:11:55 +0000293 Set<SkyKey> globDepKeys,
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000294 Map<Label, Path> subincludes,
295 PackageIdentifier packageIdentifier,
296 boolean containsErrors)
Janak Ramakrishnanfb040f62016-06-23 16:05:04 +0000297 throws InternalInconsistentFilesystemException, FileOutsidePackageRootsException,
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000298 SymlinkOutsidePackageRootsException, InterruptedException {
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000299 boolean packageShouldBeInError = containsErrors;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100300
301 // TODO(bazel-team): This means that many packages will have to be preprocessed twice. Ouch!
302 // We need a better continuation mechanism to avoid repeating work. [skyframe-loading]
303
304 // TODO(bazel-team): It would be preferable to perform I/O from the package preprocessor via
305 // Skyframe rather than add (potentially incomplete) dependencies after the fact.
306 // [skyframe-loading]
307
308 Set<SkyKey> subincludePackageLookupDepKeys = Sets.newHashSet();
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000309 for (Label label : subincludes.keySet()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100310 // Declare a dependency on the package lookup for the package giving access to the label.
Lukacs Berkif445ea12015-07-09 07:16:41 +0000311 subincludePackageLookupDepKeys.add(PackageLookupValue.key(label.getPackageIdentifier()));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100312 }
313 Pair<? extends Map<PathFragment, PackageLookupValue>, Boolean> subincludePackageLookupResult =
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000314 getPackageLookupDepsAndPropagateInconsistentFilesystemExceptions(
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000315 packageIdentifier, subincludePackageLookupDepKeys, env, containsErrors);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100316 Map<PathFragment, PackageLookupValue> subincludePackageLookupDeps =
317 subincludePackageLookupResult.getFirst();
Mark Schaller6fc9f882015-06-12 17:53:57 +0000318 packageShouldBeInError |= subincludePackageLookupResult.getSecond();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100319 List<SkyKey> subincludeFileDepKeys = Lists.newArrayList();
320 for (Entry<Label, Path> subincludeEntry : subincludes.entrySet()) {
321 // Ideally, we would have a direct dependency on the target with the given label, but then
322 // subincluding a file from the same package will cause a dependency cycle, since targets
323 // depend on their containing packages.
324 Label label = subincludeEntry.getKey();
325 PackageLookupValue subincludePackageLookupValue =
326 subincludePackageLookupDeps.get(label.getPackageFragment());
327 if (subincludePackageLookupValue != null) {
328 // Declare a dependency on the actual file that was subincluded.
329 Path subincludeFilePath = subincludeEntry.getValue();
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000330 if (subincludeFilePath != null && !subincludePackageLookupValue.packageExists()) {
331 // Legacy blaze puts a non-null path when only when the package does indeed exist.
332 throw new InternalInconsistentFilesystemException(
333 packageIdentifier,
334 String.format(
335 "Unexpected package in %s. Was it modified during the build?",
336 subincludeFilePath));
337 }
338 if (subincludePackageLookupValue.packageExists()) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100339 // Sanity check for consistency of Skyframe and legacy blaze.
340 Path subincludeFilePathSkyframe =
341 subincludePackageLookupValue.getRoot().getRelative(label.toPathFragment());
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000342 if (subincludeFilePath != null
343 && !subincludeFilePathSkyframe.equals(subincludeFilePath)) {
344 throw new InternalInconsistentFilesystemException(
345 packageIdentifier,
346 String.format(
347 "Inconsistent package location for %s: '%s' vs '%s'. "
348 + "Was the source tree modified during the build?",
349 label.getPackageFragment(),
350 subincludeFilePathSkyframe,
351 subincludeFilePath));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100352 }
353 // The actual file may be under a different package root than the package being
354 // constructed.
355 SkyKey subincludeSkyKey =
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000356 FileValue.key(
357 RootedPath.toRootedPath(
358 subincludePackageLookupValue.getRoot(),
359 label.getPackageFragment().getRelative(label.getName())));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100360 subincludeFileDepKeys.add(subincludeSkyKey);
361 }
362 }
363 }
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000364 packageShouldBeInError |=
Janak Ramakrishnanfb040f62016-06-23 16:05:04 +0000365 markFileDepsAndPropagateFilesystemExceptions(
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000366 packageIdentifier, subincludeFileDepKeys, env, containsErrors);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100367
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000368 packageShouldBeInError |=
Janak Ramakrishnanfb040f62016-06-23 16:05:04 +0000369 handleGlobDepsAndPropagateFilesystemExceptions(
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000370 packageIdentifier, globDepKeys, env, containsErrors);
Nathan Harmata3a95f352016-02-05 00:11:55 +0000371
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100372 return packageShouldBeInError;
373 }
374
375 /**
376 * Adds a dependency on the WORKSPACE file, representing it as a special type of package.
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000377 *
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100378 * @throws PackageFunctionException if there is an error computing the workspace file or adding
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000379 * its rules to the //external package.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100380 */
381 private SkyValue getExternalPackage(Environment env, Path packageLookupPath)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000382 throws PackageFunctionException, InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100383 RootedPath workspacePath = RootedPath.toRootedPath(
Nathan Harmatac686fd62016-04-26 17:41:20 +0000384 packageLookupPath, Label.EXTERNAL_PACKAGE_FILE_NAME);
Damien Martin-Guillerezbc8b5e02016-02-05 22:09:09 +0000385 SkyKey workspaceKey = ExternalPackageFunction.key(workspacePath);
Kristina Chodorow91876f02015-02-27 17:14:12 +0000386 PackageValue workspace = null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100387 try {
Janak Ramakrishnan593d6562016-06-22 18:54:56 +0000388 // This may throw a NoSuchPackageException if the WORKSPACE file was malformed or had other
389 // problems. Since this function can't add much context, we silently bubble it up.
390 workspace =
391 (PackageValue)
392 env.getValueOrThrow(
393 workspaceKey,
394 IOException.class,
395 FileSymlinkException.class,
396 InconsistentFilesystemException.class,
397 EvalException.class,
398 SkylarkImportFailedException.class);
Nathan Harmataad810502015-07-29 01:33:49 +0000399 } catch (IOException | FileSymlinkException | InconsistentFilesystemException
John Fieldbcb1bea2016-01-16 19:05:56 +0000400 | EvalException | SkylarkImportFailedException e) {
Googler3aae85f2016-04-11 14:57:06 +0000401 throw new PackageFunctionException(
402 new NoSuchPackageException(
403 Label.EXTERNAL_PACKAGE_IDENTIFIER,
404 "Error encountered while dealing with the WORKSPACE file: " + e.getMessage()),
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100405 Transience.PERSISTENT);
406 }
407 if (workspace == null) {
408 return null;
409 }
410
411 Package pkg = workspace.getPackage();
412 Event.replayEventsOn(env.getListener(), pkg.getEvents());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100413
Nathan Harmatacaa000a2016-06-07 17:46:19 +0000414 packageFactory.afterDoneLoadingPackage(pkg);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100415 return new PackageValue(pkg);
416 }
417
418 @Override
419 public SkyValue compute(SkyKey key, Environment env) throws PackageFunctionException,
420 InterruptedException {
421 PackageIdentifier packageId = (PackageIdentifier) key.argument();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100422
423 SkyKey packageLookupKey = PackageLookupValue.key(packageId);
424 PackageLookupValue packageLookupValue;
425 try {
426 packageLookupValue = (PackageLookupValue)
427 env.getValueOrThrow(packageLookupKey, BuildFileNotFoundException.class,
428 InconsistentFilesystemException.class);
429 } catch (BuildFileNotFoundException e) {
430 throw new PackageFunctionException(e, Transience.PERSISTENT);
431 } catch (InconsistentFilesystemException e) {
432 // This error is not transient from the perspective of the PackageFunction.
433 throw new PackageFunctionException(
Googler3aae85f2016-04-11 14:57:06 +0000434 new NoSuchPackageException(packageId, e.getMessage(), e), Transience.PERSISTENT);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100435 }
436 if (packageLookupValue == null) {
437 return null;
438 }
439
440 if (!packageLookupValue.packageExists()) {
441 switch (packageLookupValue.getErrorReason()) {
442 case NO_BUILD_FILE:
443 case DELETED_PACKAGE:
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000444 throw new PackageFunctionException(new BuildFileNotFoundException(packageId,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100445 packageLookupValue.getErrorMsg()), Transience.PERSISTENT);
446 case INVALID_PACKAGE_NAME:
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000447 throw new PackageFunctionException(new InvalidPackageNameException(packageId,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100448 packageLookupValue.getErrorMsg()), Transience.PERSISTENT);
449 default:
450 // We should never get here.
Ulf Adams07dba942015-03-05 14:47:37 +0000451 throw new IllegalStateException();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100452 }
453 }
454
Lukacs Berkie19ee272015-12-10 11:34:29 +0000455 if (packageId.equals(Label.EXTERNAL_PACKAGE_IDENTIFIER)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100456 return getExternalPackage(env, packageLookupValue.getRoot());
457 }
Lukacs Berkie19ee272015-12-10 11:34:29 +0000458 SkyKey externalPackageKey = PackageValue.key(Label.EXTERNAL_PACKAGE_IDENTIFIER);
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000459 PackageValue externalPackage = (PackageValue) env.getValue(externalPackageKey);
Kristina Chodorow91876f02015-02-27 17:14:12 +0000460 if (externalPackage == null) {
461 return null;
462 }
463 Package externalPkg = externalPackage.getPackage();
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000464 if (externalPkg.containsErrors()) {
465 throw new PackageFunctionException(
Lukacs Berkie19ee272015-12-10 11:34:29 +0000466 new BuildFileContainsErrorsException(Label.EXTERNAL_PACKAGE_IDENTIFIER),
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000467 Transience.PERSISTENT);
468 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100469
John Cater94695912016-08-03 12:09:39 +0000470 RootedPath buildFileRootedPath = packageLookupValue.getRootedPath(packageId);
Lukacs Berki5131ef92015-10-09 14:20:03 +0000471 FileValue buildFileValue = null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100472 Path buildFilePath = buildFileRootedPath.asPath();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100473 String replacementContents = null;
Lukacs Berkib018ee32015-10-19 08:22:23 +0000474
475 if (!isDefaultsPackage(packageId)) {
476 buildFileValue = getBuildFileValue(env, buildFileRootedPath);
477 if (buildFileValue == null) {
478 return null;
479 }
480 } else {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100481 replacementContents = PrecomputedValue.DEFAULTS_PACKAGE_CONTENTS.get(env);
482 if (replacementContents == null) {
483 return null;
484 }
485 }
486
487 RuleVisibility defaultVisibility = PrecomputedValue.DEFAULT_VISIBILITY.get(env);
488 if (defaultVisibility == null) {
489 return null;
490 }
491
John Fielda97e17f2015-11-13 02:19:52 +0000492 SkyKey astLookupKey = ASTFileLookupValue.key(preludeLabel);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100493 ASTFileLookupValue astLookupValue = null;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100494 try {
495 astLookupValue = (ASTFileLookupValue) env.getValueOrThrow(astLookupKey,
496 ErrorReadingSkylarkExtensionException.class, InconsistentFilesystemException.class);
497 } catch (ErrorReadingSkylarkExtensionException | InconsistentFilesystemException e) {
John Fielda97e17f2015-11-13 02:19:52 +0000498 throw new PackageFunctionException(
Googler3aae85f2016-04-11 14:57:06 +0000499 new NoSuchPackageException(
500 packageId, "Error encountered while reading the prelude file: " + e.getMessage()),
501 Transience.PERSISTENT);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100502 }
503 if (astLookupValue == null) {
504 return null;
505 }
John Fielda97e17f2015-11-13 02:19:52 +0000506 // The prelude file doesn't have to exist. If not, we substitute an empty statement list.
507 List<Statement> preludeStatements =
508 astLookupValue.lookupSuccessful()
509 ? astLookupValue.getAST().getStatements() : ImmutableList.<Statement>of();
Nathan Harmata6b253fa2016-05-24 15:27:18 +0000510 CacheEntryWithGlobDeps<Package.Builder> packageBuilderAndGlobDeps =
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000511 loadPackage(
512 externalPkg,
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000513 replacementContents,
514 packageId,
515 buildFilePath,
Nathan Harmata5a9b6992015-10-13 19:52:41 +0000516 buildFileValue,
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000517 defaultVisibility,
518 preludeStatements,
Nathan Harmata3a95f352016-02-05 00:11:55 +0000519 packageLookupValue.getRoot(),
Han-Wen Nienhuys06480182015-08-07 15:22:35 +0000520 env);
Nathan Harmata3a95f352016-02-05 00:11:55 +0000521 if (packageBuilderAndGlobDeps == null) {
Han-Wen Nienhuys06480182015-08-07 15:22:35 +0000522 return null;
523 }
Nathan Harmata6b253fa2016-05-24 15:27:18 +0000524 Package.Builder pkgBuilder = packageBuilderAndGlobDeps.value;
525 pkgBuilder.buildPartial();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100526 try {
Nathan Harmata1cf8e3b2015-10-17 00:19:05 +0000527 // Since the Skyframe dependencies we request below in
Janak Ramakrishnanfb040f62016-06-23 16:05:04 +0000528 // markDependenciesAndPropagateFilesystemExceptions are requested independently of
Nathan Harmata1cf8e3b2015-10-17 00:19:05 +0000529 // the ones requested here in
530 // handleLabelsCrossingSubpackagesAndPropagateInconsistentFilesystemExceptions, we don't
531 // bother checking for missing values and instead piggyback on the env.missingValues() call
532 // for the former. This avoids a Skyframe restart.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100533 handleLabelsCrossingSubpackagesAndPropagateInconsistentFilesystemExceptions(
Nathan Harmata6b253fa2016-05-24 15:27:18 +0000534 packageLookupValue.getRoot(), packageId, pkgBuilder, env);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100535 } catch (InternalInconsistentFilesystemException e) {
Eric Fellheimer35e57cf2015-05-27 14:31:11 +0000536 packageFunctionCache.invalidate(packageId);
Googler3aae85f2016-04-11 14:57:06 +0000537 throw new PackageFunctionException(
538 e.toNoSuchPackageException(),
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100539 e.isTransient() ? Transience.TRANSIENT : Transience.PERSISTENT);
540 }
Nathan Harmata3a95f352016-02-05 00:11:55 +0000541 Set<SkyKey> globKeys = packageBuilderAndGlobDeps.globDepKeys;
Nathan Harmata6b253fa2016-05-24 15:27:18 +0000542 Map<Label, Path> subincludes = pkgBuilder.getSubincludes();
Mark Schaller6fc9f882015-06-12 17:53:57 +0000543 boolean packageShouldBeConsideredInError;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100544 try {
545 packageShouldBeConsideredInError =
Janak Ramakrishnanfb040f62016-06-23 16:05:04 +0000546 markDependenciesAndPropagateFilesystemExceptions(
Nathan Harmata6b253fa2016-05-24 15:27:18 +0000547 env, globKeys, subincludes, packageId, pkgBuilder.containsErrors());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100548 } catch (InternalInconsistentFilesystemException e) {
Eric Fellheimer35e57cf2015-05-27 14:31:11 +0000549 packageFunctionCache.invalidate(packageId);
Googler3aae85f2016-04-11 14:57:06 +0000550 throw new PackageFunctionException(
551 e.toNoSuchPackageException(),
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100552 e.isTransient() ? Transience.TRANSIENT : Transience.PERSISTENT);
Janak Ramakrishnanfb040f62016-06-23 16:05:04 +0000553 } catch (FileOutsidePackageRootsException | SymlinkOutsidePackageRootsException e) {
554 packageFunctionCache.invalidate(packageId);
555 throw new PackageFunctionException(
556 new NoSuchPackageException(packageId, "Encountered file outside package roots", e),
557 Transience.PERSISTENT);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100558 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100559 if (env.valuesMissing()) {
560 return null;
561 }
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000562
Nathan Harmata6b253fa2016-05-24 15:27:18 +0000563 Event.replayEventsOn(env.getListener(), pkgBuilder.getEvents());
Nathan Harmata1cf8e3b2015-10-17 00:19:05 +0000564
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000565 if (packageShouldBeConsideredInError) {
Nathan Harmata6b253fa2016-05-24 15:27:18 +0000566 pkgBuilder.setContainsErrors();
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000567 }
Nathan Harmata6b253fa2016-05-24 15:27:18 +0000568 Package pkg = pkgBuilder.finishBuild();
Janak Ramakrishnan0a4c6e42015-09-17 00:37:58 +0000569
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100570 // We know this SkyFunction will not be called again, so we can remove the cache entry.
Eric Fellheimer35e57cf2015-05-27 14:31:11 +0000571 packageFunctionCache.invalidate(packageId);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100572
Nathan Harmatacaa000a2016-06-07 17:46:19 +0000573 packageFactory.afterDoneLoadingPackage(pkg);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100574 return new PackageValue(pkg);
575 }
576
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000577 private static FileValue getBuildFileValue(Environment env, RootedPath buildFileRootedPath)
578 throws InterruptedException {
Lukacs Berkib018ee32015-10-19 08:22:23 +0000579 FileValue buildFileValue;
580 try {
581 buildFileValue = (FileValue) env.getValueOrThrow(FileValue.key(buildFileRootedPath),
582 IOException.class, FileSymlinkException.class,
583 InconsistentFilesystemException.class);
584 } catch (IOException | FileSymlinkException | InconsistentFilesystemException e) {
585 throw new IllegalStateException("Package lookup succeeded but encountered error when "
586 + "getting FileValue for BUILD file directly.", e);
587 }
588 if (buildFileValue == null) {
589 return null;
590 }
591 Preconditions.checkState(buildFileValue.exists(),
592 "Package lookup succeeded but BUILD file doesn't exist");
593 return buildFileValue;
594 }
595
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000596 @Nullable
597 private SkylarkImportResult discoverSkylarkImports(
598 Path buildFilePath,
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000599 PackageIdentifier packageId,
Nathan Harmata9faad192015-10-15 18:41:24 +0000600 AstAfterPreprocessing astAfterPreprocessing,
601 Environment env)
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +0000602 throws PackageFunctionException, InterruptedException {
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000603 SkylarkImportResult importResult;
Nathan Harmata9faad192015-10-15 18:41:24 +0000604 if (astAfterPreprocessing.containsAstParsingErrors) {
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000605 importResult =
606 new SkylarkImportResult(
John Field1ea7fc32015-12-22 19:37:19 +0000607 ImmutableMap.<String, Extension>of(),
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000608 ImmutableList.<Label>of());
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000609 } else {
Michajlo Matijkiw2a7c8022015-09-22 02:22:12 +0000610 importResult =
Damien Martin-Guillerez8ca065c22015-11-27 16:22:59 +0000611 fetchImportsFromBuildFile(
612 buildFilePath,
613 packageId,
614 astAfterPreprocessing.ast,
615 env,
616 skylarkImportLookupFunctionForInlining);
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000617 }
618
619 return importResult;
620 }
621
Han-Wen Nienhuys06480182015-08-07 15:22:35 +0000622 /**
623 * Fetch the skylark loads for this BUILD file. If any of them haven't been computed yet,
624 * returns null.
625 */
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000626 @Nullable
Damien Martin-Guillerez8ca065c22015-11-27 16:22:59 +0000627 static SkylarkImportResult fetchImportsFromBuildFile(
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000628 Path buildFilePath,
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +0000629 PackageIdentifier packageId,
630 BuildFileAST buildFileAST,
Damien Martin-Guillerez8ca065c22015-11-27 16:22:59 +0000631 Environment env,
632 SkylarkImportLookupFunction skylarkImportLookupFunctionForInlining)
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +0000633 throws PackageFunctionException, InterruptedException {
Brian Silvermand7d6d622016-03-17 09:53:39 +0000634 Preconditions.checkArgument(!packageId.getRepository().isDefault());
635
John Field1ea7fc32015-12-22 19:37:19 +0000636 ImmutableList<SkylarkImport> imports = buildFileAST.getImports();
637 Map<String, Extension> importMap = Maps.newHashMapWithExpectedSize(imports.size());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100638 ImmutableList.Builder<SkylarkFileDependency> fileDependencies = ImmutableList.builder();
John Field1ea7fc32015-12-22 19:37:19 +0000639 ImmutableMap<String, Label> importPathMap;
John Fieldbcb1bea2016-01-16 19:05:56 +0000640
John Fielda97e17f2015-11-13 02:19:52 +0000641 // Find the labels corresponding to the load statements.
642 Label labelForCurrBuildFile;
643 try {
644 labelForCurrBuildFile = Label.create(packageId, "BUILD");
645 } catch (LabelSyntaxException e) {
646 // Shouldn't happen; the Label is well-formed by construction.
647 throw new IllegalStateException(e);
648 }
649 try {
650 importPathMap = SkylarkImportLookupFunction.findLabelsForLoadStatements(
John Field1ea7fc32015-12-22 19:37:19 +0000651 imports, labelForCurrBuildFile, env);
John Fielda97e17f2015-11-13 02:19:52 +0000652 if (importPathMap == null) {
653 return null;
654 }
655 } catch (SkylarkImportFailedException e) {
John Fielda97e17f2015-11-13 02:19:52 +0000656 throw new PackageFunctionException(
657 new BuildFileContainsErrorsException(packageId, e.getMessage()), Transience.PERSISTENT);
658 }
659
660 // Look up and load the imports.
661 ImmutableCollection<Label> importLabels = importPathMap.values();
662 List<SkyKey> importLookupKeys = Lists.newArrayListWithExpectedSize(importLabels.size());
Damien Martin-Guillerez8ca065c22015-11-27 16:22:59 +0000663 boolean inWorkspace = buildFilePath.getBaseName().endsWith("WORKSPACE");
John Fielda97e17f2015-11-13 02:19:52 +0000664 for (Label importLabel : importLabels) {
Damien Martin-Guillerez8ca065c22015-11-27 16:22:59 +0000665 importLookupKeys.add(SkylarkImportLookupValue.key(importLabel, inWorkspace));
John Fielda97e17f2015-11-13 02:19:52 +0000666 }
667 Map<SkyKey, SkyValue> skylarkImportMap = Maps.newHashMapWithExpectedSize(importPathMap.size());
668 boolean valuesMissing = false;
669
670 try {
671 if (skylarkImportLookupFunctionForInlining == null) {
672 // Not inlining
673 Map<SkyKey,
674 ValueOrException2<
675 SkylarkImportFailedException,
676 InconsistentFilesystemException>> skylarkLookupResults = env.getValuesOrThrow(
677 importLookupKeys,
678 SkylarkImportFailedException.class,
679 InconsistentFilesystemException.class);
680 valuesMissing = env.valuesMissing();
681 for (Map.Entry<
682 SkyKey,
683 ValueOrException2<
684 SkylarkImportFailedException,
685 InconsistentFilesystemException>> entry : skylarkLookupResults.entrySet()) {
686 // Fetching the value will raise any deferred exceptions
687 skylarkImportMap.put(entry.getKey(), entry.getValue().get());
688 }
689 } else {
690 // Inlining calls to SkylarkImportLookupFunction
Janak Ramakrishnanf4dd2832016-10-10 23:56:01 +0000691 LinkedHashMap<Label, SkylarkImportLookupValue> alreadyVisitedImports =
692 Maps.newLinkedHashMapWithExpectedSize(importLookupKeys.size());
John Fielda97e17f2015-11-13 02:19:52 +0000693 for (SkyKey importLookupKey : importLookupKeys) {
Janak Ramakrishnanf4dd2832016-10-10 23:56:01 +0000694 SkyValue skyValue =
695 skylarkImportLookupFunctionForInlining.computeWithInlineCalls(
696 importLookupKey, env, alreadyVisitedImports);
John Fielda97e17f2015-11-13 02:19:52 +0000697 if (skyValue == null) {
698 Preconditions.checkState(
699 env.valuesMissing(), "no skylark import value for %s", importLookupKey);
700 // We continue making inline calls even if some requested values are missing, to
701 // maximize the number of dependent (non-inlined) SkyFunctions that are requested, thus
702 // avoiding a quadratic number of restarts.
703 valuesMissing = true;
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +0000704 } else {
John Fielda97e17f2015-11-13 02:19:52 +0000705 skylarkImportMap.put(importLookupKey, skyValue);
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +0000706 }
707 }
Damien Martin-Guillerez8ca065c22015-11-27 16:22:59 +0000708
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +0000709 }
John Fielda97e17f2015-11-13 02:19:52 +0000710 } catch (SkylarkImportFailedException e) {
John Fielda97e17f2015-11-13 02:19:52 +0000711 throw new PackageFunctionException(
712 new BuildFileContainsErrorsException(packageId, e.getMessage()), Transience.PERSISTENT);
713 } catch (InconsistentFilesystemException e) {
714 throw new PackageFunctionException(
Googler3aae85f2016-04-11 14:57:06 +0000715 new NoSuchPackageException(packageId, e.getMessage(), e), Transience.PERSISTENT);
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +0000716 }
John Fieldbcb1bea2016-01-16 19:05:56 +0000717
John Fielda97e17f2015-11-13 02:19:52 +0000718 if (valuesMissing) {
719 // Some imports are unavailable.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100720 return null;
721 }
John Fieldbcb1bea2016-01-16 19:05:56 +0000722
John Fielda97e17f2015-11-13 02:19:52 +0000723 // Process the loaded imports.
John Field1ea7fc32015-12-22 19:37:19 +0000724 for (Entry<String, Label> importEntry : importPathMap.entrySet()) {
725 String importString = importEntry.getKey();
John Fielda97e17f2015-11-13 02:19:52 +0000726 Label importLabel = importEntry.getValue();
Damien Martin-Guillerez8ca065c22015-11-27 16:22:59 +0000727 SkyKey keyForLabel = SkylarkImportLookupValue.key(importLabel, inWorkspace);
John Fielda97e17f2015-11-13 02:19:52 +0000728 SkylarkImportLookupValue importLookupValue =
729 (SkylarkImportLookupValue) skylarkImportMap.get(keyForLabel);
John Field1ea7fc32015-12-22 19:37:19 +0000730 importMap.put(importString, importLookupValue.getEnvironmentExtension());
John Fielda97e17f2015-11-13 02:19:52 +0000731 fileDependencies.add(importLookupValue.getDependency());
732 }
John Fieldbcb1bea2016-01-16 19:05:56 +0000733
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100734 return new SkylarkImportResult(importMap, transitiveClosureOfLabels(fileDependencies.build()));
735 }
736
Damien Martin-Guillerez8ca065c22015-11-27 16:22:59 +0000737 private static ImmutableList<Label> transitiveClosureOfLabels(
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100738 ImmutableList<SkylarkFileDependency> immediateDeps) {
739 Set<Label> transitiveClosure = Sets.newHashSet();
740 transitiveClosureOfLabels(immediateDeps, transitiveClosure);
741 return ImmutableList.copyOf(transitiveClosure);
742 }
743
Damien Martin-Guillerez8ca065c22015-11-27 16:22:59 +0000744 private static void transitiveClosureOfLabels(
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100745 ImmutableList<SkylarkFileDependency> immediateDeps, Set<Label> transitiveClosure) {
746 for (SkylarkFileDependency dep : immediateDeps) {
Ulf Adams07dba942015-03-05 14:47:37 +0000747 if (transitiveClosure.add(dep.getLabel())) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100748 transitiveClosureOfLabels(dep.getDependencies(), transitiveClosure);
749 }
750 }
751 }
752
753 @Nullable
754 @Override
755 public String extractTag(SkyKey skyKey) {
756 return null;
757 }
758
759 private static void handleLabelsCrossingSubpackagesAndPropagateInconsistentFilesystemExceptions(
Nathan Harmata6b253fa2016-05-24 15:27:18 +0000760 Path pkgRoot, PackageIdentifier pkgId, Package.Builder pkgBuilder, Environment env)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000761 throws InternalInconsistentFilesystemException, InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100762 Set<SkyKey> containingPkgLookupKeys = Sets.newHashSet();
763 Map<Target, SkyKey> targetToKey = new HashMap<>();
764 for (Target target : pkgBuilder.getTargets()) {
765 PathFragment dir = target.getLabel().toPathFragment().getParentDirectory();
Miguel Alcon Pinto2627b9f2015-10-01 16:02:53 +0000766 PackageIdentifier dirId = PackageIdentifier.create(pkgId.getRepository(), dir);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100767 if (dir.equals(pkgId.getPackageFragment())) {
768 continue;
769 }
770 SkyKey key = ContainingPackageLookupValue.key(dirId);
771 targetToKey.put(target, key);
772 containingPkgLookupKeys.add(key);
773 }
774 Map<Label, SkyKey> subincludeToKey = new HashMap<>();
775 for (Label subincludeLabel : pkgBuilder.getSubincludeLabels()) {
776 PathFragment dir = subincludeLabel.toPathFragment().getParentDirectory();
Miguel Alcon Pinto2627b9f2015-10-01 16:02:53 +0000777 PackageIdentifier dirId = PackageIdentifier.create(pkgId.getRepository(), dir);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100778 if (dir.equals(pkgId.getPackageFragment())) {
779 continue;
780 }
781 SkyKey key = ContainingPackageLookupValue.key(dirId);
782 subincludeToKey.put(subincludeLabel, key);
783 containingPkgLookupKeys.add(ContainingPackageLookupValue.key(dirId));
784 }
785 Map<SkyKey, ValueOrException3<BuildFileNotFoundException, InconsistentFilesystemException,
Nathan Harmataad810502015-07-29 01:33:49 +0000786 FileSymlinkException>> containingPkgLookupValues = env.getValuesOrThrow(
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100787 containingPkgLookupKeys, BuildFileNotFoundException.class,
Nathan Harmataad810502015-07-29 01:33:49 +0000788 InconsistentFilesystemException.class, FileSymlinkException.class);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100789 if (env.valuesMissing()) {
790 return;
791 }
792 for (Target target : ImmutableSet.copyOf(pkgBuilder.getTargets())) {
793 SkyKey key = targetToKey.get(target);
794 if (!containingPkgLookupValues.containsKey(key)) {
795 continue;
796 }
797 ContainingPackageLookupValue containingPackageLookupValue =
798 getContainingPkgLookupValueAndPropagateInconsistentFilesystemExceptions(
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000799 pkgId, containingPkgLookupValues.get(key), env);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100800 if (maybeAddEventAboutLabelCrossingSubpackage(pkgBuilder, pkgRoot, target.getLabel(),
801 target.getLocation(), containingPackageLookupValue)) {
802 pkgBuilder.removeTarget(target);
803 pkgBuilder.setContainsErrors();
804 }
805 }
806 for (Label subincludeLabel : pkgBuilder.getSubincludeLabels()) {
807 SkyKey key = subincludeToKey.get(subincludeLabel);
808 if (!containingPkgLookupValues.containsKey(key)) {
809 continue;
810 }
811 ContainingPackageLookupValue containingPackageLookupValue =
812 getContainingPkgLookupValueAndPropagateInconsistentFilesystemExceptions(
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000813 pkgId, containingPkgLookupValues.get(key), env);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100814 if (maybeAddEventAboutLabelCrossingSubpackage(pkgBuilder, pkgRoot, subincludeLabel,
815 /*location=*/null, containingPackageLookupValue)) {
816 pkgBuilder.setContainsErrors();
817 }
818 }
819 }
820
821 @Nullable
822 private static ContainingPackageLookupValue
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000823 getContainingPkgLookupValueAndPropagateInconsistentFilesystemExceptions(
824 PackageIdentifier packageIdentifier,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100825 ValueOrException3<BuildFileNotFoundException, InconsistentFilesystemException,
Nathan Harmataad810502015-07-29 01:33:49 +0000826 FileSymlinkException> containingPkgLookupValueOrException, Environment env)
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100827 throws InternalInconsistentFilesystemException {
828 try {
829 return (ContainingPackageLookupValue) containingPkgLookupValueOrException.get();
Nathan Harmataad810502015-07-29 01:33:49 +0000830 } catch (BuildFileNotFoundException | FileSymlinkException e) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100831 env.getListener().handle(Event.error(null, e.getMessage()));
832 return null;
833 } catch (InconsistentFilesystemException e) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +0000834 throw new InternalInconsistentFilesystemException(packageIdentifier, e);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100835 }
836 }
837
838 private static boolean maybeAddEventAboutLabelCrossingSubpackage(
Nathan Harmata6b253fa2016-05-24 15:27:18 +0000839 Package.Builder pkgBuilder, Path pkgRoot, Label label, @Nullable Location location,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100840 @Nullable ContainingPackageLookupValue containingPkgLookupValue) {
841 if (containingPkgLookupValue == null) {
842 return true;
843 }
844 if (!containingPkgLookupValue.hasContainingPackage()) {
845 // The missing package here is a problem, but it's not an error from the perspective of
846 // PackageFunction.
847 return false;
848 }
849 PackageIdentifier containingPkg = containingPkgLookupValue.getContainingPackageName();
850 if (containingPkg.equals(label.getPackageIdentifier())) {
851 // The label does not cross a subpackage boundary.
852 return false;
853 }
Kristina Chodorowa1a31ff2016-07-27 16:34:27 +0000854 if (!containingPkg.getSourceRoot().startsWith(
855 label.getPackageIdentifier().getSourceRoot())) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100856 // This label is referencing an imaginary package, because the containing package should
857 // extend the label's package: if the label is //a/b:c/d, the containing package could be
858 // //a/b/c or //a/b, but should never be //a. Usually such errors will be caught earlier, but
859 // in some exceptional cases (such as a Python-aware BUILD file catching its own io
860 // exceptions), it reaches here, and we tolerate it.
861 return false;
862 }
863 PathFragment labelNameFragment = new PathFragment(label.getName());
864 String message = String.format("Label '%s' crosses boundary of subpackage '%s'",
865 label, containingPkg);
866 Path containingRoot = containingPkgLookupValue.getContainingPackageRoot();
867 if (pkgRoot.equals(containingRoot)) {
868 PathFragment labelNameInContainingPackage = labelNameFragment.subFragment(
869 containingPkg.getPackageFragment().segmentCount()
870 - label.getPackageFragment().segmentCount(),
871 labelNameFragment.segmentCount());
Brian Silverman8f922092016-02-01 16:10:27 +0000872 message += " (perhaps you meant to put the colon here: '";
Brian Silvermand7d6d622016-03-17 09:53:39 +0000873 if (containingPkg.getRepository().isDefault() || containingPkg.getRepository().isMain()) {
Brian Silverman8f922092016-02-01 16:10:27 +0000874 message += "//";
875 }
876 message += containingPkg + ":" + labelNameInContainingPackage + "'?)";
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100877 } else {
878 message += " (have you deleted " + containingPkg + "/BUILD? "
879 + "If so, use the --deleted_packages=" + containingPkg + " option)";
880 }
881 pkgBuilder.addEvent(Event.error(location, message));
882 return true;
883 }
884
885 /**
Nathan Harmata3a95f352016-02-05 00:11:55 +0000886 * A {@link Globber} implemented on top of skyframe that falls back to a
887 * {@link PackageFactory.LegacyGlobber} on a skyframe cache-miss. This way we don't require a
888 * skyframe restart after a call to {@link Globber#runAsync} and before/during a call to
889 * {@link Globber#fetch}.
890 *
891 * <p>There are three advantages to this hybrid approach over the more obvious approach of solely
892 * using a {@link PackageFactory.LegacyGlobber}:
893 * <ul>
894 * <li>We trivially have the proper Skyframe {@link GlobValue} deps, whereas we would need to
895 * request them after-the-fact if we solely used a {@link PackageFactory.LegacyGlobber}.
896 * <li>We don't need to re-evaluate globs whose expression hasn't changed (e.g. in the common case
897 * of a BUILD file edit that doesn't change a glob expression), whereas legacy package loading
898 * with a {@link PackageFactory.LegacyGlobber} would naively re-evaluate globs when re-evaluating
899 * the BUILD file.
900 * <li>We don't need to re-evaluate invalidated globs *twice* (the single re-evaluation via our
901 * GlobValue deps is sufficient and optimal). See above for why the second evaluation would
902 * happen.
903 * </ul>
904 */
905 private static class SkyframeHybridGlobber implements Globber {
906 private final PackageIdentifier packageId;
907 private final Path packageRoot;
908 private final Environment env;
Nathan Harmata44e1e3a2016-08-23 21:22:17 +0000909 private final LegacyGlobber legacyGlobber;
Nathan Harmata3a95f352016-02-05 00:11:55 +0000910 private final Set<SkyKey> globDepsRequested = Sets.newConcurrentHashSet();
911
912 private SkyframeHybridGlobber(PackageIdentifier packageId, Path packageRoot, Environment env,
Nathan Harmata44e1e3a2016-08-23 21:22:17 +0000913 LegacyGlobber legacyGlobber) {
Nathan Harmata3a95f352016-02-05 00:11:55 +0000914 this.packageId = packageId;
915 this.packageRoot = packageRoot;
916 this.env = env;
Nathan Harmata44e1e3a2016-08-23 21:22:17 +0000917 this.legacyGlobber = legacyGlobber;
Nathan Harmata3a95f352016-02-05 00:11:55 +0000918 }
919
920 private Set<SkyKey> getGlobDepsRequested() {
921 return ImmutableSet.copyOf(globDepsRequested);
922 }
923
924 private SkyKey getGlobKey(String pattern, boolean excludeDirs) throws BadGlobException {
925 try {
926 return GlobValue.key(packageId, packageRoot, pattern, excludeDirs,
927 PathFragment.EMPTY_FRAGMENT);
928 } catch (InvalidGlobPatternException e) {
929 throw new BadGlobException(e.getMessage());
930 }
931 }
932
933 @Override
934 public Token runAsync(List<String> includes, List<String> excludes, boolean excludeDirs)
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000935 throws BadGlobException, InterruptedException {
Nathan Harmata3a95f352016-02-05 00:11:55 +0000936 List<SkyKey> globKeys = new ArrayList<>(includes.size() + excludes.size());
937 LinkedHashSet<SkyKey> includesKeys = Sets.newLinkedHashSetWithExpectedSize(includes.size());
938 LinkedHashSet<SkyKey> excludesKeys = Sets.newLinkedHashSetWithExpectedSize(excludes.size());
939 Map<SkyKey, String> globKeyToIncludeStringMap =
940 Maps.newHashMapWithExpectedSize(includes.size());
941 Map<SkyKey, String> globKeyToExcludeStringMap =
942 Maps.newHashMapWithExpectedSize(excludes.size());
943
944 for (String pattern : includes) {
945 SkyKey globKey = getGlobKey(pattern, excludeDirs);
946 globKeys.add(globKey);
947 includesKeys.add(globKey);
948 globKeyToIncludeStringMap.put(globKey, pattern);
949 }
950 for (String pattern : excludes) {
951 SkyKey globKey = getGlobKey(pattern, excludeDirs);
952 globKeys.add(globKey);
953 excludesKeys.add(globKey);
954 globKeyToExcludeStringMap.put(globKey, pattern);
955 }
956 globDepsRequested.addAll(globKeys);
957
958 Map<SkyKey, ValueOrException4<IOException, BuildFileNotFoundException,
959 FileSymlinkCycleException, InconsistentFilesystemException>> globValueMap =
960 env.getValuesOrThrow(globKeys, IOException.class, BuildFileNotFoundException.class,
961 FileSymlinkCycleException.class, InconsistentFilesystemException.class);
962
963 // For each missing glob, evaluate it asychronously via the delegate.
964 //
965 // TODO(bazel-team): Consider not delegating missing globs during glob prefetching - a
966 // single skyframe restart after the prefetch step is probably tolerable.
967 Collection<SkyKey> missingKeys = getMissingKeys(globKeys, globValueMap);
968 List<String> includesToDelegate = new ArrayList<>(missingKeys.size());
969 List<String> excludesToDelegate = new ArrayList<>(missingKeys.size());
970 for (SkyKey missingKey : missingKeys) {
971 String missingIncludePattern = globKeyToIncludeStringMap.get(missingKey);
972 if (missingIncludePattern != null) {
973 includesToDelegate.add(missingIncludePattern);
974 includesKeys.remove(missingKey);
975 }
976 String missingExcludePattern = globKeyToExcludeStringMap.get(missingKey);
977 if (missingExcludePattern != null) {
978 excludesToDelegate.add(missingExcludePattern);
979 excludesKeys.remove(missingKey);
980 }
981 }
Nathan Harmata44e1e3a2016-08-23 21:22:17 +0000982 Token legacyIncludesToken =
983 legacyGlobber.runAsync(includesToDelegate, ImmutableList.<String>of(), excludeDirs);
Nathan Harmata3a95f352016-02-05 00:11:55 +0000984 // See the HybridToken class-comment for why we pass excludesToDelegate as the includes
985 // parameter here.
Nathan Harmata44e1e3a2016-08-23 21:22:17 +0000986 Token legacyExcludesToken =
987 legacyGlobber.runAsync(excludesToDelegate, ImmutableList.<String>of(), excludeDirs);
Nathan Harmata3a95f352016-02-05 00:11:55 +0000988
989 return new HybridToken(globValueMap, includesKeys, excludesKeys,
Nathan Harmata44e1e3a2016-08-23 21:22:17 +0000990 legacyIncludesToken, legacyExcludesToken);
Nathan Harmata3a95f352016-02-05 00:11:55 +0000991 }
992
993 private Collection<SkyKey> getMissingKeys(Collection<SkyKey> globKeys,
994 Map<SkyKey, ValueOrException4<IOException, BuildFileNotFoundException,
995 FileSymlinkCycleException, InconsistentFilesystemException>> globValueMap) {
996 List<SkyKey> missingKeys = new ArrayList<>(globKeys.size());
997 for (SkyKey globKey : globKeys) {
998 ValueOrException4<IOException, BuildFileNotFoundException, FileSymlinkCycleException,
999 InconsistentFilesystemException> valueOrException = globValueMap.get(globKey);
1000 if (valueOrException == null) {
1001 missingKeys.add(globKey);
1002 }
1003 try {
1004 if (valueOrException.get() == null) {
1005 missingKeys.add(globKey);
1006 }
1007 } catch (IOException | BuildFileNotFoundException | FileSymlinkCycleException
1008 | InconsistentFilesystemException doesntMatter) {
1009 continue;
1010 }
1011 }
1012 return missingKeys;
1013 }
1014
1015 @Override
1016 public List<String> fetch(Token token) throws IOException, InterruptedException {
1017 HybridToken hybridToken = (HybridToken) token;
Nathan Harmata44e1e3a2016-08-23 21:22:17 +00001018 return hybridToken.resolve(legacyGlobber);
Nathan Harmata3a95f352016-02-05 00:11:55 +00001019 }
1020
1021 @Override
1022 public void onInterrupt() {
Nathan Harmata44e1e3a2016-08-23 21:22:17 +00001023 legacyGlobber.onInterrupt();
Nathan Harmata3a95f352016-02-05 00:11:55 +00001024 }
1025
1026 @Override
1027 public void onCompletion() {
Nathan Harmata44e1e3a2016-08-23 21:22:17 +00001028 legacyGlobber.onCompletion();
Nathan Harmata3a95f352016-02-05 00:11:55 +00001029 }
1030
1031 /**
1032 * A {@link Globber.Token} that encapsulates the result of a single {@link Globber#runAsync}
1033 * call via the fetching of some globs from skyframe, and some other globs via a
1034 * {@link PackageFactory.LegacyGlobber}. We take care to properly handle 'includes' vs
1035 * 'excludes'.
1036 *
1037 * <p>That is, we evaluate {@code glob(includes, excludes)} by partitioning {@code includes} and
1038 * {@code excludes}.
1039 *
1040 * <pre>
1041 * {@code
1042 * includes = includes_sky U includes_leg
1043 * excludes = excludes_sky U excludes_leg
1044 * }
1045 * </pre>
1046 *
1047 * <p>and then noting
1048 *
1049 * <pre>
1050 * {@code
1051 * glob(includes, excludes) =
1052 * (glob(includes_sky, []) U glob(includes_leg, []))
1053 * - (glob(excludes_sky, []) U glob(excludes_leg, []))
1054 * }
1055 * </pre>
1056 *
1057 * <p>Importantly, we pass excludes=[] in all cases; otherwise we'd be incorrectly not
1058 * subtracting excluded glob matches from the overall list of matches. In other words, we
1059 * implement the subtractive nature of excludes ourselves in {@link #resolve}.
1060 */
1061 private static class HybridToken extends Globber.Token {
1062 // The result of the Skyframe lookup for all the needed glob patterns.
1063 private final Map<SkyKey, ValueOrException4<IOException, BuildFileNotFoundException,
1064 FileSymlinkCycleException, InconsistentFilesystemException>> globValueMap;
1065 // The skyframe keys corresponding to the 'includes' patterns fetched from Skyframe
1066 // (this is includes_sky above).
1067 private final Iterable<SkyKey> includesGlobKeys;
1068 // The skyframe keys corresponding to the 'excludes' patterns fetched from Skyframe
1069 // (this is excludes_sky above).
1070 private final Iterable<SkyKey> excludesGlobKeys;
1071 // A token for computing includes_leg.
Nathan Harmata44e1e3a2016-08-23 21:22:17 +00001072 private final Token legacyIncludesToken;
Nathan Harmata3a95f352016-02-05 00:11:55 +00001073 // A token for computing excludes_leg.
Nathan Harmata44e1e3a2016-08-23 21:22:17 +00001074 private final Token legacyExcludesToken;
Nathan Harmata3a95f352016-02-05 00:11:55 +00001075
1076 private HybridToken(Map<SkyKey, ValueOrException4<IOException, BuildFileNotFoundException,
1077 FileSymlinkCycleException, InconsistentFilesystemException>> globValueMap,
1078 Iterable<SkyKey> includesGlobKeys, Iterable<SkyKey> excludesGlobKeys,
1079 Token delegateIncludesToken, Token delegateExcludesToken) {
1080 this.globValueMap = globValueMap;
1081 this.includesGlobKeys = includesGlobKeys;
1082 this.excludesGlobKeys = excludesGlobKeys;
Nathan Harmata44e1e3a2016-08-23 21:22:17 +00001083 this.legacyIncludesToken = delegateIncludesToken;
1084 this.legacyExcludesToken = delegateExcludesToken;
Nathan Harmata3a95f352016-02-05 00:11:55 +00001085 }
1086
1087 private List<String> resolve(Globber delegate) throws IOException, InterruptedException {
Nathan Harmata44e1e3a2016-08-23 21:22:17 +00001088 HashSet<String> matches = new HashSet<>();
Nathan Harmata3a95f352016-02-05 00:11:55 +00001089 for (SkyKey includeGlobKey : includesGlobKeys) {
1090 // TODO(bazel-team): NestedSet expansion here is suboptimal.
1091 for (PathFragment match : getGlobMatches(includeGlobKey, globValueMap)) {
1092 matches.add(match.getPathString());
1093 }
1094 }
Nathan Harmata44e1e3a2016-08-23 21:22:17 +00001095 matches.addAll(delegate.fetch(legacyIncludesToken));
Nathan Harmata3a95f352016-02-05 00:11:55 +00001096 for (SkyKey excludeGlobKey : excludesGlobKeys) {
1097 for (PathFragment match : getGlobMatches(excludeGlobKey, globValueMap)) {
1098 matches.remove(match.getPathString());
1099 }
1100 }
Nathan Harmata44e1e3a2016-08-23 21:22:17 +00001101 for (String delegateExcludeMatch : delegate.fetch(legacyExcludesToken)) {
Nathan Harmata3ae80a72016-03-16 19:04:26 +00001102 matches.remove(delegateExcludeMatch);
1103 }
Janak Ramakrishnan063b4882016-07-18 20:33:28 +00001104 List<String> result = new ArrayList<>(matches);
Nathan Harmata44e1e3a2016-08-23 21:22:17 +00001105 // Skyframe glob results are unsorted. And we used a LegacyGlobber that doesn't sort.
1106 // Therefore, we want to unconditionally sort here.
1107 Collections.sort(result);
Janak Ramakrishnan063b4882016-07-18 20:33:28 +00001108 return result;
Nathan Harmata3a95f352016-02-05 00:11:55 +00001109 }
1110
Nathan Harmata44e1e3a2016-08-23 21:22:17 +00001111 private static NestedSet<PathFragment> getGlobMatches(
Janak Ramakrishnan063b4882016-07-18 20:33:28 +00001112 SkyKey globKey,
1113 Map<
1114 SkyKey,
1115 ValueOrException4<
1116 IOException, BuildFileNotFoundException, FileSymlinkCycleException,
1117 InconsistentFilesystemException>>
1118 globValueMap)
1119 throws IOException {
Nathan Harmata3a95f352016-02-05 00:11:55 +00001120 ValueOrException4<IOException, BuildFileNotFoundException, FileSymlinkCycleException,
1121 InconsistentFilesystemException> valueOrException =
1122 Preconditions.checkNotNull(globValueMap.get(globKey), "%s should not be missing",
1123 globKey);
1124 try {
Nathan Harmata3a95f352016-02-05 00:11:55 +00001125 return Preconditions.checkNotNull((GlobValue) valueOrException.get(),
1126 "%s should not be missing", globKey).getMatches();
1127 } catch (BuildFileNotFoundException | FileSymlinkCycleException
1128 | InconsistentFilesystemException e) {
1129 // Legacy package loading is only able to handle an IOException, so a rethrow here is the
1130 // best we can do. But after legacy package loading, PackageFunction will go through all
1131 // the skyframe deps and properly handle InconsistentFilesystemExceptions.
1132 throw new IOException(e.getMessage());
1133 }
1134 }
1135 }
1136 }
1137
1138 /**
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001139 * Constructs a {@link Package} object for the given package using legacy package loading.
1140 * Note that the returned package may be in error.
Han-Wen Nienhuys06480182015-08-07 15:22:35 +00001141 *
1142 * <p>May return null if the computation has to be restarted.
Nathan Harmata5a9b6992015-10-13 19:52:41 +00001143 *
Nathan Harmatad8b6ff22015-10-20 21:54:34 +00001144 * <p>Exactly one of {@code replacementContents} and {@code buildFileValue} will be
Nathan Harmata5a9b6992015-10-13 19:52:41 +00001145 * non-{@code null}. The former indicates that we have a faux BUILD file with the given contents
1146 * and the latter indicates that we have a legitimate BUILD file and should actually do
1147 * preprocessing.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001148 */
Han-Wen Nienhuys06480182015-08-07 15:22:35 +00001149 @Nullable
Nathan Harmata6b253fa2016-05-24 15:27:18 +00001150 private CacheEntryWithGlobDeps<Package.Builder> loadPackage(
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +00001151 Package externalPkg,
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +00001152 @Nullable String replacementContents,
1153 PackageIdentifier packageId,
1154 Path buildFilePath,
Nathan Harmata5a9b6992015-10-13 19:52:41 +00001155 @Nullable FileValue buildFileValue,
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +00001156 RuleVisibility defaultVisibility,
1157 List<Statement> preludeStatements,
Nathan Harmata3a95f352016-02-05 00:11:55 +00001158 Path packageRoot,
Han-Wen Nienhuys06480182015-08-07 15:22:35 +00001159 Environment env)
Han-Wen Nienhuysdab43132015-08-06 16:44:44 +00001160 throws InterruptedException, PackageFunctionException {
Nathan Harmata6b253fa2016-05-24 15:27:18 +00001161 CacheEntryWithGlobDeps<Package.Builder> packageFunctionCacheEntry =
Nathan Harmata3a95f352016-02-05 00:11:55 +00001162 packageFunctionCache.getIfPresent(packageId);
1163 if (packageFunctionCacheEntry == null) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001164 profiler.startTask(ProfilerTask.CREATE_PACKAGE, packageId.toString());
Klaus Aehligc6fd6bb2016-05-27 11:42:32 +00001165 if (packageProgress != null) {
1166 packageProgress.startReadPackage(packageId);
1167 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001168 try {
Nathan Harmata3a95f352016-02-05 00:11:55 +00001169 CacheEntryWithGlobDeps<AstAfterPreprocessing> astCacheEntry =
1170 astCache.getIfPresent(packageId);
1171 if (astCacheEntry == null) {
Nathan Harmata5a9b6992015-10-13 19:52:41 +00001172 if (showLoadingProgress.get()) {
1173 env.getListener().handle(Event.progress("Loading package: " + packageId));
1174 }
Nathan Harmata44e1e3a2016-08-23 21:22:17 +00001175 // We use a LegacyGlobber that doesn't sort the matches for each individual glob pattern,
1176 // since we want to sort the final result anyway.
1177 LegacyGlobber legacyGlobber = packageFactory.createLegacyGlobberThatDoesntSort(
Nathan Harmata3a95f352016-02-05 00:11:55 +00001178 buildFilePath.getParentDirectory(), packageId, packageLocator);
1179 SkyframeHybridGlobber skyframeGlobber = new SkyframeHybridGlobber(packageId, packageRoot,
1180 env, legacyGlobber);
Nathan Harmata9faad192015-10-15 18:41:24 +00001181 Preprocessor.Result preprocessingResult;
Nathan Harmata5a9b6992015-10-13 19:52:41 +00001182 if (replacementContents == null) {
Nathan Harmatad8b6ff22015-10-20 21:54:34 +00001183 Preconditions.checkNotNull(buildFileValue, packageId);
Nathan Harmata4e698242015-10-20 23:18:23 +00001184 byte[] buildFileBytes;
Nathan Harmata5a9b6992015-10-13 19:52:41 +00001185 try {
Nathan Harmata4e698242015-10-20 23:18:23 +00001186 buildFileBytes = buildFileValue.isSpecialFile()
1187 ? FileSystemUtils.readContent(buildFilePath)
1188 : FileSystemUtils.readWithKnownFileSize(buildFilePath, buildFileValue.getSize());
Nathan Harmata5a9b6992015-10-13 19:52:41 +00001189 } catch (IOException e) {
Nathan Harmata5a9b6992015-10-13 19:52:41 +00001190 // Note that we did this work, so we should conservatively report this error as
1191 // transient.
1192 throw new PackageFunctionException(new BuildFileContainsErrorsException(
1193 packageId, e.getMessage()), Transience.TRANSIENT);
1194 }
1195 try {
Nathan Harmata4e698242015-10-20 23:18:23 +00001196 preprocessingResult = packageFactory.preprocess(buildFilePath, packageId,
Nathan Harmata3a95f352016-02-05 00:11:55 +00001197 buildFileBytes, skyframeGlobber);
Nathan Harmata5a9b6992015-10-13 19:52:41 +00001198 } catch (IOException e) {
Nathan Harmata5a9b6992015-10-13 19:52:41 +00001199 throw new PackageFunctionException(
John Field1ea7fc32015-12-22 19:37:19 +00001200 new BuildFileContainsErrorsException(
1201 packageId, "preprocessing failed" + e.getMessage(), e), Transience.TRANSIENT);
Nathan Harmata5a9b6992015-10-13 19:52:41 +00001202 }
1203 } else {
1204 ParserInputSource replacementSource =
1205 ParserInputSource.create(replacementContents, buildFilePath.asFragment());
1206 preprocessingResult = Preprocessor.Result.noPreprocessing(replacementSource);
Janak Ramakrishnanac023bb2015-09-15 19:37:11 +00001207 }
Nathan Harmata9faad192015-10-15 18:41:24 +00001208 StoredEventHandler astParsingEventHandler = new StoredEventHandler();
1209 BuildFileAST ast = PackageFactory.parseBuildFile(packageId, preprocessingResult.result,
1210 preludeStatements, astParsingEventHandler);
Nathan Harmata3a95f352016-02-05 00:11:55 +00001211 // If no globs were fetched during preprocessing, then there's no need to reuse the
1212 // legacy globber instance during BUILD file evaluation since the performance argument
1213 // below does not apply.
1214 Set<SkyKey> globDepsRequested = skyframeGlobber.getGlobDepsRequested();
Nathan Harmata44e1e3a2016-08-23 21:22:17 +00001215 LegacyGlobber legacyGlobberToStore = globDepsRequested.isEmpty() ? null : legacyGlobber;
Nathan Harmata3a95f352016-02-05 00:11:55 +00001216 astCacheEntry = new CacheEntryWithGlobDeps<>(
1217 new AstAfterPreprocessing(preprocessingResult, ast, astParsingEventHandler),
1218 globDepsRequested, legacyGlobberToStore);
1219 astCache.put(packageId, astCacheEntry);
Ulf Adamsd9b51212015-09-04 15:14:22 +00001220 }
Nathan Harmata3a95f352016-02-05 00:11:55 +00001221 AstAfterPreprocessing astAfterPreprocessing = astCacheEntry.value;
1222 Set<SkyKey> globDepsRequestedDuringPreprocessing = astCacheEntry.globDepKeys;
Nathan Harmata829d1182015-10-12 22:26:08 +00001223 SkylarkImportResult importResult;
1224 try {
1225 importResult = discoverSkylarkImports(
Nathan Harmata9faad192015-10-15 18:41:24 +00001226 buildFilePath,
Nathan Harmata9faad192015-10-15 18:41:24 +00001227 packageId,
1228 astAfterPreprocessing,
1229 env);
Nathan Harmata829d1182015-10-12 22:26:08 +00001230 } catch (PackageFunctionException | InterruptedException e) {
Nathan Harmata9faad192015-10-15 18:41:24 +00001231 astCache.invalidate(packageId);
Nathan Harmata829d1182015-10-12 22:26:08 +00001232 throw e;
1233 }
Han-Wen Nienhuys06480182015-08-07 15:22:35 +00001234 if (importResult == null) {
1235 return null;
1236 }
Nathan Harmata9faad192015-10-15 18:41:24 +00001237 astCache.invalidate(packageId);
Nathan Harmata3a95f352016-02-05 00:11:55 +00001238 // If a legacy globber was used to evaluate globs during preprocessing, it's important that
1239 // we reuse that globber during BUILD file evaluation for performance, in the case that
1240 // globs were fetched lazily during preprocessing. See Preprocessor.Factory#considersGlobs.
Nathan Harmata44e1e3a2016-08-23 21:22:17 +00001241 LegacyGlobber legacyGlobber = astCacheEntry.legacyGlobber != null
Nathan Harmata3a95f352016-02-05 00:11:55 +00001242 ? astCacheEntry.legacyGlobber
1243 : packageFactory.createLegacyGlobber(
1244 buildFilePath.getParentDirectory(), packageId, packageLocator);
1245 SkyframeHybridGlobber skyframeGlobber = new SkyframeHybridGlobber(packageId, packageRoot,
1246 env, legacyGlobber);
Nathan Harmata6b253fa2016-05-24 15:27:18 +00001247 Package.Builder pkgBuilder = packageFactory.createPackageFromPreprocessingAst(
Nathan Harmata3a95f352016-02-05 00:11:55 +00001248 externalPkg, packageId, buildFilePath, astAfterPreprocessing, importResult.importMap,
1249 importResult.fileDependencies, defaultVisibility, skyframeGlobber);
1250 Set<SkyKey> globDepsRequested = ImmutableSet.<SkyKey>builder()
1251 .addAll(globDepsRequestedDuringPreprocessing)
1252 .addAll(skyframeGlobber.getGlobDepsRequested())
1253 .build();
1254 packageFunctionCacheEntry =
1255 new CacheEntryWithGlobDeps<>(pkgBuilder, globDepsRequested, null);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001256 numPackagesLoaded.incrementAndGet();
Klaus Aehligc6fd6bb2016-05-27 11:42:32 +00001257 if (packageProgress != null) {
1258 packageProgress.doneReadPackage(packageId);
1259 }
Nathan Harmata3a95f352016-02-05 00:11:55 +00001260 packageFunctionCache.put(packageId, packageFunctionCacheEntry);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001261 } finally {
1262 profiler.completeTask(ProfilerTask.CREATE_PACKAGE);
1263 }
1264 }
Nathan Harmata3a95f352016-02-05 00:11:55 +00001265 return packageFunctionCacheEntry;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001266 }
1267
Googler3aae85f2016-04-11 14:57:06 +00001268 private static class InternalInconsistentFilesystemException extends Exception {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001269 private boolean isTransient;
1270
Googler3aae85f2016-04-11 14:57:06 +00001271 private PackageIdentifier packageIdentifier;
1272
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001273 /**
1274 * Used to represent a filesystem inconsistency discovered outside the
1275 * {@link PackageFunction}.
1276 */
Kristina Chodorowe121dd92015-06-17 14:24:35 +00001277 public InternalInconsistentFilesystemException(PackageIdentifier packageIdentifier,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001278 InconsistentFilesystemException e) {
Googler3aae85f2016-04-11 14:57:06 +00001279 super(e.getMessage(), e);
1280 this.packageIdentifier = packageIdentifier;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001281 // This is not a transient error from the perspective of the PackageFunction.
1282 this.isTransient = false;
1283 }
1284
1285 /** Used to represent a filesystem inconsistency discovered by the {@link PackageFunction}. */
Kristina Chodorowe121dd92015-06-17 14:24:35 +00001286 public InternalInconsistentFilesystemException(PackageIdentifier packageIdentifier,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001287 String inconsistencyMessage) {
Kristina Chodorowe121dd92015-06-17 14:24:35 +00001288 this(packageIdentifier, new InconsistentFilesystemException(inconsistencyMessage));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001289 this.isTransient = true;
1290 }
1291
1292 public boolean isTransient() {
1293 return isTransient;
1294 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001295
Googler3aae85f2016-04-11 14:57:06 +00001296 private NoSuchPackageException toNoSuchPackageException() {
1297 return new NoSuchPackageException(
1298 packageIdentifier, this.getMessage(), (Exception) this.getCause());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001299 }
1300 }
1301
1302 /**
1303 * Used to declare all the exception types that can be wrapped in the exception thrown by
1304 * {@link PackageFunction#compute}.
1305 */
Damien Martin-Guillerez8ca065c22015-11-27 16:22:59 +00001306 static class PackageFunctionException extends SkyFunctionException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001307 public PackageFunctionException(NoSuchPackageException e, Transience transience) {
1308 super(e, transience);
1309 }
1310 }
1311
1312 /** A simple value class to store the result of the Skylark imports.*/
Damien Martin-Guillerez8ca065c22015-11-27 16:22:59 +00001313 static final class SkylarkImportResult {
John Field1ea7fc32015-12-22 19:37:19 +00001314 final Map<String, Extension> importMap;
Damien Martin-Guillerez8ca065c22015-11-27 16:22:59 +00001315 final ImmutableList<Label> fileDependencies;
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +00001316 private SkylarkImportResult(
John Field1ea7fc32015-12-22 19:37:19 +00001317 Map<String, Extension> importMap,
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001318 ImmutableList<Label> fileDependencies) {
1319 this.importMap = importMap;
1320 this.fileDependencies = fileDependencies;
1321 }
1322 }
Lukacs Berkib018ee32015-10-19 08:22:23 +00001323
1324 static boolean isDefaultsPackage(PackageIdentifier packageIdentifier) {
Brian Silvermand7d6d622016-03-17 09:53:39 +00001325 return packageIdentifier.getRepository().isMain()
Lukacs Berkib018ee32015-10-19 08:22:23 +00001326 && packageIdentifier.getPackageFragment().equals(DEFAULTS_PACKAGE_NAME);
1327 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001328}