blob: d763563518abab5594c463840fe884841c97cbed [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
tomlua155b532017-11-08 20:12:47 +010016import com.google.common.base.Preconditions;
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +000017import com.google.common.base.Predicates;
shreyax755a0a12018-03-19 20:47:37 -070018import com.google.common.cache.Cache;
19import com.google.common.cache.CacheBuilder;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import com.google.common.collect.ImmutableList;
John Fielda97e17f2015-11-13 02:19:52 +000021import com.google.common.collect.ImmutableMap;
John Fielda97e17f2015-11-13 02:19:52 +000022import com.google.common.collect.Lists;
Janak Ramakrishnandfd34972015-09-22 02:53:05 +000023import com.google.common.collect.Maps;
janakrc3bcb982020-04-14 06:50:08 -070024import com.google.common.flogger.GoogleLogger;
janakr3af783a2020-09-08 14:12:14 -070025import com.google.common.hash.HashFunction;
shahan602cc852018-06-06 20:09:57 -070026import com.google.devtools.build.lib.actions.InconsistentFilesystemException;
brandjond5d86772020-10-26 16:21:22 -070027import com.google.devtools.build.lib.analysis.BlazeDirectories;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000028import com.google.devtools.build.lib.cmdline.Label;
Googlerb2e1fe32019-09-20 14:00:39 -070029import com.google.devtools.build.lib.cmdline.LabelConstants;
30import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
Kristina Chodorow73fa2032015-08-28 17:57:46 +000031import com.google.devtools.build.lib.cmdline.PackageIdentifier;
dannark4e42c322018-11-08 19:32:04 -080032import com.google.devtools.build.lib.cmdline.RepositoryName;
shreyax755a0a12018-03-19 20:47:37 -070033import com.google.devtools.build.lib.concurrent.BlazeInterners;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010034import com.google.devtools.build.lib.events.Event;
Dmitry Lomov950310f2017-03-01 17:45:12 +000035import com.google.devtools.build.lib.events.EventHandler;
brandjona0c6f812020-06-06 14:13:19 -070036import com.google.devtools.build.lib.events.ExtendedEventHandler;
Klaus Aehligda0a7012017-06-14 13:40:23 +020037import com.google.devtools.build.lib.events.ExtendedEventHandler.Postable;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010038import com.google.devtools.build.lib.events.StoredEventHandler;
ajurkowski9f2cab52020-05-12 12:00:24 -070039import com.google.devtools.build.lib.packages.BazelModuleContext;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010040import com.google.devtools.build.lib.packages.BuildFileNotFoundException;
41import com.google.devtools.build.lib.packages.PackageFactory;
gregced281df72020-05-11 12:27:06 -070042import com.google.devtools.build.lib.packages.StarlarkExportable;
janakr15e15c22019-01-30 11:24:49 -080043import com.google.devtools.build.lib.packages.WorkspaceFileValue;
adonovan028e1ad2020-09-07 07:09:59 -070044import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions;
mschaller859c9ac2020-09-25 16:09:19 -070045import com.google.devtools.build.lib.server.FailureDetails.FailureDetail;
46import com.google.devtools.build.lib.server.FailureDetails.StarlarkLoading;
47import com.google.devtools.build.lib.server.FailureDetails.StarlarkLoading.Code;
brandjon844fef52020-06-08 10:30:36 -070048import com.google.devtools.build.lib.skyframe.StarlarkBuiltinsFunction.BuiltinsFailedException;
mschaller859c9ac2020-09-25 16:09:19 -070049import com.google.devtools.build.lib.util.DetailedExitCode;
adonovan1d93d262020-05-07 14:50:42 -070050import com.google.devtools.build.lib.util.Fingerprint;
adonovan699a90a2020-05-20 19:48:53 -070051import com.google.devtools.build.lib.util.Pair;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010052import com.google.devtools.build.lib.vfs.PathFragment;
brandjond5d86772020-10-26 16:21:22 -070053import com.google.devtools.build.lib.vfs.Root;
shreyax755a0a12018-03-19 20:47:37 -070054import com.google.devtools.build.skyframe.RecordingSkyFunctionEnvironment;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010055import com.google.devtools.build.skyframe.SkyFunction;
56import com.google.devtools.build.skyframe.SkyFunctionException;
57import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
58import com.google.devtools.build.skyframe.SkyKey;
59import com.google.devtools.build.skyframe.SkyValue;
laurentlbdd612a82018-10-16 19:42:48 -070060import com.google.devtools.build.skyframe.ValueOrException;
brandjon1d3f3092020-06-08 10:58:25 -070061import java.util.HashMap;
nharmata636fb5e2020-07-24 15:30:08 -070062import java.util.HashSet;
shreyax39ffd972019-02-07 15:41:31 -080063import java.util.LinkedHashSet;
John Fielda97e17f2015-11-13 02:19:52 +000064import java.util.List;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010065import java.util.Map;
brandjon91988192020-05-28 14:57:45 -070066import java.util.function.Consumer;
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +000067import javax.annotation.Nullable;
adonovan450c7ad2020-09-14 13:00:21 -070068import net.starlark.java.eval.EvalException;
69import net.starlark.java.eval.Module;
70import net.starlark.java.eval.Mutability;
71import net.starlark.java.eval.Starlark;
72import net.starlark.java.eval.StarlarkSemantics;
73import net.starlark.java.eval.StarlarkThread;
74import net.starlark.java.syntax.LoadStatement;
75import net.starlark.java.syntax.Program;
76import net.starlark.java.syntax.StarlarkFile;
77import net.starlark.java.syntax.Statement;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010078
79/**
adonovan1d93d262020-05-07 14:50:42 -070080 * A Skyframe function to look up and load a single .bzl module.
John Fielda97e17f2015-11-13 02:19:52 +000081 *
adonovan1d93d262020-05-07 14:50:42 -070082 * <p>Given a {@link Label} referencing a .bzl file, attempts to locate the file and load it. The
83 * Label must be absolute, and must not reference the special {@code external} package. If loading
brandjon771a0292020-05-26 12:04:16 -070084 * is successful, returns a {@link BzlLoadValue} that encapsulates the loaded {@link Module} and its
adonovan39e42d12020-07-09 09:16:58 -070085 * transitive digest information. If loading is unsuccessful, throws a {@link
86 * BzlLoadFunctionException} that encapsulates the cause of the failure.
brandjon91988192020-05-28 14:57:45 -070087 *
brandjonb4ebbfc2020-08-13 15:50:23 -070088 * <p>This Skyframe function supports a special bzl "inlining" mode in which all (indirectly)
89 * recursive calls to {@code BzlLoadFunction} are made in the same thread rather than through
90 * Skyframe. This inlining mode's entry point is {@link #computeInline}; see that method for more
91 * details. Note that it may only be called on an instance of this Skyfunction created by {@link
92 * #createForInlining}. Bzl inlining is not to be confused with the separate inlining of {@code
adonovan0a1f0f52020-09-16 11:11:04 -070093 * BzlCompileFunction}
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010094 */
brandjon771a0292020-05-26 12:04:16 -070095public class BzlLoadFunction implements SkyFunction {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010096
brandjondb4d1ea2020-08-14 11:31:08 -070097 // Used for: 1) obtaining a RuleClassProvider to create the BazelStarlarkContext for Starlark
98 // evaluation; 2) providing predeclared environments to other Skyfunctions
adonovan0a1f0f52020-09-16 11:11:04 -070099 // (StarlarkBuiltinsFunction, BzlCompileFunction) when they are inlined and called via a static
brandjondb4d1ea2020-08-14 11:31:08 -0700100 // computeInline() entry point.
101 private final PackageFactory packageFactory;
brandjon28632522020-06-08 13:14:07 -0700102
brandjond5d86772020-10-26 16:21:22 -0700103 // Used for determining paths to builtins bzls.
104 private final BlazeDirectories directories;
105
adonovan0a1f0f52020-09-16 11:11:04 -0700106 // Handles retrieving BzlCompileValues, either by calling Skyframe or by inlining
107 // BzlCompileFunction; the latter is not to be confused with inlining of BzlLoadFunction. See
brandjon8d669292020-05-28 15:27:38 -0700108 // comment in create() for rationale.
adonovan0a1f0f52020-09-16 11:11:04 -0700109 private final ValueGetter getter;
brandjon8d669292020-05-28 15:27:38 -0700110
111 // Handles inlining of BzlLoadFunction calls.
112 @Nullable private final CachedBzlLoadDataManager cachedBzlLoadDataManager;
shreyax755a0a12018-03-19 20:47:37 -0700113
janakrc3bcb982020-04-14 06:50:08 -0700114 private static final GoogleLogger logger = GoogleLogger.forEnclosingClass();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100115
brandjon771a0292020-05-26 12:04:16 -0700116 private BzlLoadFunction(
nharmatadc1d9dc2020-04-18 16:53:28 -0700117 PackageFactory packageFactory,
brandjond5d86772020-10-26 16:21:22 -0700118 BlazeDirectories directories,
adonovan0a1f0f52020-09-16 11:11:04 -0700119 ValueGetter getter,
brandjon8d669292020-05-28 15:27:38 -0700120 @Nullable CachedBzlLoadDataManager cachedBzlLoadDataManager) {
brandjon28632522020-06-08 13:14:07 -0700121 this.packageFactory = packageFactory;
brandjond5d86772020-10-26 16:21:22 -0700122 this.directories = directories;
adonovan0a1f0f52020-09-16 11:11:04 -0700123 this.getter = getter;
brandjon8d669292020-05-28 15:27:38 -0700124 this.cachedBzlLoadDataManager = cachedBzlLoadDataManager;
shreyax31262052019-08-13 13:40:06 -0700125 }
126
brandjon771a0292020-05-26 12:04:16 -0700127 public static BzlLoadFunction create(
nharmatadc1d9dc2020-04-18 16:53:28 -0700128 PackageFactory packageFactory,
brandjond5d86772020-10-26 16:21:22 -0700129 BlazeDirectories directories,
janakr3af783a2020-09-08 14:12:14 -0700130 HashFunction hashFunction,
adonovan0a1f0f52020-09-16 11:11:04 -0700131 Cache<BzlCompileValue.Key, BzlCompileValue> bzlCompileCache) {
brandjon771a0292020-05-26 12:04:16 -0700132 return new BzlLoadFunction(
nharmatadc1d9dc2020-04-18 16:53:28 -0700133 packageFactory,
brandjond5d86772020-10-26 16:21:22 -0700134 directories,
brandjon771a0292020-05-26 12:04:16 -0700135 // When we are not inlining BzlLoadValue nodes, there is no need to have separate
adonovan0a1f0f52020-09-16 11:11:04 -0700136 // BzlCompileValue nodes for bzl files. Instead we inline BzlCompileFunction for a
brandjon8d669292020-05-28 15:27:38 -0700137 // strict memory win, at a small code complexity cost.
nharmatadc1d9dc2020-04-18 16:53:28 -0700138 //
139 // Detailed explanation:
adonovan0a1f0f52020-09-16 11:11:04 -0700140 // (1) The BzlCompileValue node for a bzl file is used only for the computation of
brandjon771a0292020-05-26 12:04:16 -0700141 // that file's BzlLoadValue node. So there's no concern about duplicate work that would
142 // otherwise get deduped by Skyframe.
adonovan0a1f0f52020-09-16 11:11:04 -0700143 // (2) BzlCompileValue doesn't have an interesting equality relation, so we have no
144 // hope of getting any interesting change-pruning of BzlCompileValue nodes. If we
nharmatadc1d9dc2020-04-18 16:53:28 -0700145 // had an interesting equality relation that was e.g. able to ignore benign
146 // whitespace, then there would be a hypothetical benefit to having separate
adonovan0a1f0f52020-09-16 11:11:04 -0700147 // BzlCompileValue nodes (e.g. on incremental builds we'd be able to not re-execute
148 // top-level code in bzl files if the file were reparsed to an equivalent tree).
149 // TODO(adonovan): this will change once it truly compiles the code (soon).
150 // (3) A BzlCompileValue node lets us avoid redoing work on a BzlLoadFunction Skyframe
brandjon771a0292020-05-26 12:04:16 -0700151 // restart, but we can also achieve that result ourselves with a cache that persists between
152 // Skyframe restarts.
nharmatadc1d9dc2020-04-18 16:53:28 -0700153 //
adonovan0a1f0f52020-09-16 11:11:04 -0700154 // Therefore, BzlCompileValue nodes are wasteful from two perspectives:
155 // (a) BzlCompileValue contains syntax trees, and that business object is really
nharmatadc1d9dc2020-04-18 16:53:28 -0700156 // just a temporary thing for bzl execution. Retaining it forever is pure waste.
157 // (b) The memory overhead of the extra Skyframe node and edge per bzl file is pure
158 // waste.
adonovan0a1f0f52020-09-16 11:11:04 -0700159 new InliningAndCachingGetter(packageFactory, hashFunction, bzlCompileCache),
brandjon8d669292020-05-28 15:27:38 -0700160 /*cachedBzlLoadDataManager=*/ null);
nharmatadc1d9dc2020-04-18 16:53:28 -0700161 }
162
brandjon24912642020-05-27 11:28:45 -0700163 public static BzlLoadFunction createForInlining(
brandjond5d86772020-10-26 16:21:22 -0700164 PackageFactory packageFactory, BlazeDirectories directories, int bzlLoadValueCacheSize) {
brandjon771a0292020-05-26 12:04:16 -0700165 return new BzlLoadFunction(
nharmatadc1d9dc2020-04-18 16:53:28 -0700166 packageFactory,
brandjond5d86772020-10-26 16:21:22 -0700167 directories,
adonovan0a1f0f52020-09-16 11:11:04 -0700168 // When we are inlining BzlLoadValue nodes, then we want to have explicit BzlCompileValue
brandjon771a0292020-05-26 12:04:16 -0700169 // nodes, since now (1) in the comment above doesn't hold. This way we read and parse each
170 // needed bzl file at most once total globally, rather than once per need (in the worst-case
171 // of a BzlLoadValue inlining cache miss). This is important in the situation where a bzl
172 // file is loaded by a lot of other bzl files or BUILD files.
adonovan0a1f0f52020-09-16 11:11:04 -0700173 RegularSkyframeGetter.INSTANCE,
brandjon8d669292020-05-28 15:27:38 -0700174 new CachedBzlLoadDataManager(bzlLoadValueCacheSize));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100175 }
176
177 @Override
shreyax755a0a12018-03-19 20:47:37 -0700178 @Nullable
179 public SkyValue compute(SkyKey skyKey, Environment env)
180 throws SkyFunctionException, InterruptedException {
brandjon771a0292020-05-26 12:04:16 -0700181 BzlLoadValue.Key key = (BzlLoadValue.Key) skyKey.argument();
John Fielda97e17f2015-11-13 02:19:52 +0000182 try {
brandjon7154be02020-05-22 09:47:32 -0700183 return computeInternal(key, env, /*inliningState=*/ null);
brandjon771a0292020-05-26 12:04:16 -0700184 } catch (BzlLoadFailedException e) {
185 throw new BzlLoadFunctionException(e);
John Fielda97e17f2015-11-13 02:19:52 +0000186 }
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +0000187 }
188
brandjon24912642020-05-27 11:28:45 -0700189 /**
190 * Entry point for computing "inline", without any direct or indirect Skyframe calls back into
191 * {@link BzlLoadFunction}. (Other Skyframe calls are permitted.)
192 *
nharmata60d04602020-08-11 12:28:57 -0700193 * <p><b>USAGE NOTES:</b>
194 *
195 * <ul>
196 * <li>This method is intended to be called from {@link PackageFunction} and {@link
197 * StarlarkBuiltinsFunction} and probably shouldn't be used anywhere else. If you think you
198 * need inline Starlark computation, consult with the Core subteam and check out
199 * cl/305127325 for an example of correcting a misuse.
200 * <li>If this method is used with --keep_going and if Skyframe evaluation will never be
brandjonb4ebbfc2020-08-13 15:50:23 -0700201 * interrupted, then this function ensures that the evaluation graph and any error reported
202 * are deterministic.
nharmata60d04602020-08-11 12:28:57 -0700203 * </ul>
brandjon8d669292020-05-28 15:27:38 -0700204 *
205 * <p>Under bzl inlining, there is some calling context that wants to obtain a set of {@link
brandjon91988192020-05-28 14:57:45 -0700206 * BzlLoadValue}s without Skyframe evaluation. For example, a calling context can be a BUILD file
207 * trying to resolve its top-level {@code load()} statements. Although this work proceeds in a
208 * single thread, multiple calling contexts may evaluate .bzls in parallel. To avoid redundant
209 * work, they share a single (global to this Skyfunction instance) cache in lieu of the regular
nharmata636fb5e2020-07-24 15:30:08 -0700210 * Skyframe cache. Unlike the regular Skyframe cache, this cache stores only successes.
brandjon91988192020-05-28 14:57:45 -0700211 *
212 * <p>If two calling contexts race to compute the same .bzl, each one will see a different copy of
213 * it, and only one will end up in the shared cache. This presents a hazard: Suppose A and B both
214 * need foo.bzl, and A needs it twice due to a diamond dependency. If A and B race to compute
215 * foo.bzl, but B's computation populates the cache, then when A comes back to resolve it the
216 * second time it will observe a different {@code BzlLoadValue}. This leads to incorrect Starlark
217 * evaluation since Starlark values may rely on Java object identity (see b/138598337). Even if we
218 * weren't concerned about racing, A may also reevaluate previously computed items due to cache
219 * evictions.
220 *
nharmata636fb5e2020-07-24 15:30:08 -0700221 * <p>To solve this, we keep a second cache, {@link InliningState#successfulLoads}, that is local
222 * to the current calling context, and which never evicts entries. Like the global cache discussed
223 * above, this cache stores only successes. This cache is always checked in preference to the
224 * shared one; it may deviate from the shared one in some of its entries, but the calling context
225 * won't know the difference. (Since bzl inlining is only used for the loading phase, we don't
226 * need to worry about Starlark values from different packages interacting.) The cache is stored
227 * as part of the {@code inliningState} passed in by the caller; the caller can obtain this object
228 * using {@link InliningState#create}.
brandjon91988192020-05-28 14:57:45 -0700229 *
nharmata636fb5e2020-07-24 15:30:08 -0700230 * <p>As an aside, note that we can't avoid having {@link InliningState#successfulLoads} by simply
231 * naively blocking evaluation of .bzls on retrievals from the shared cache. This is because two
232 * contexts could deadlock while trying to evaluate an illegal {@code load()} cycle from opposite
233 * ends. It would be possible to construct a waits-for graph and perform cycle detection, or to
234 * monitor slow threads and do detection lazily, but these do not address the cache eviction
235 * issue. Alternatively, we could make Starlark tolerant of reloading, but that would be
236 * tantamount to implementing full Starlark serialization.
brandjon91988192020-05-28 14:57:45 -0700237 *
nharmata636fb5e2020-07-24 15:30:08 -0700238 * <p>Since our local {@link InliningState#successfulLoads} stores only successes, a separate
239 * concern is that we don't want to unsuccessfully visit the same .bzl more than once in the same
240 * context. (A visitation is unsuccessful if it fails due to an error or if it cannot complete
241 * because of a missing Skyframe dep.) To address this concern we maintain a separate {@link
242 * InliningState#unsuccessfulLoads} set, and use this set to return null instead of duplicating an
243 * unsuccessful visitation.
244 *
245 * @return the requested {@code BzlLoadValue}, or null if there was a missing Skyframe dep, an
246 * unspecified exception in a Skyframe dep request, or if this was a duplicate unsuccessful
247 * visitation
brandjon24912642020-05-27 11:28:45 -0700248 */
nharmata636fb5e2020-07-24 15:30:08 -0700249 // TODO(brandjon): Pick one of the nouns "load" and "bzl" and use that term consistently.
shreyax755a0a12018-03-19 20:47:37 -0700250 @Nullable
brandjon1d3f3092020-06-08 10:58:25 -0700251 BzlLoadValue computeInline(BzlLoadValue.Key key, Environment env, InliningState inliningState)
brandjon4eed64e2020-08-24 10:13:53 -0700252 throws BzlLoadFailedException, InterruptedException {
brandjon91988192020-05-28 14:57:45 -0700253 // Note to refactorors: No Skyframe calls may be made before the RecordingSkyFunctionEnvironment
254 // is set up below in computeInlineForCacheMiss.
brandjon8d669292020-05-28 15:27:38 -0700255 Preconditions.checkNotNull(cachedBzlLoadDataManager);
brandjon1d3f3092020-06-08 10:58:25 -0700256 CachedBzlLoadData cachedData = computeInlineCachedData(key, env, inliningState);
brandjon24912642020-05-27 11:28:45 -0700257 return cachedData != null ? cachedData.getValue() : null;
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +0000258 }
259
brandjon24912642020-05-27 11:28:45 -0700260 /**
nharmata636fb5e2020-07-24 15:30:08 -0700261 * Retrieves or creates the requested {@link CachedBzlLoadData} object for the given bzl, entering
262 * it into the local and shared caches. This is the entry point for recursive calls to the inline
263 * code path.
brandjon24912642020-05-27 11:28:45 -0700264 *
brandjon91988192020-05-28 14:57:45 -0700265 * <p>Skyframe calls made underneath this function will be logged in the resulting {@code
brandjon1d3f3092020-06-08 10:58:25 -0700266 * CachedBzlLoadData) (or its transitive dependencies). The given Skyframe environment must not
267 * be a {@link RecordingSkyFunctionEnvironment}, since that would imply that calls are being
268 * logged in both the returned value and the parent value.
brandjon91988192020-05-28 14:57:45 -0700269 *
nharmata636fb5e2020-07-24 15:30:08 -0700270 * @return null if there was a missing Skyframe dep, an unspecified exception in a Skyframe dep
271 * request, or if this was a duplicate unsuccessful visitation
brandjon24912642020-05-27 11:28:45 -0700272 */
shreyax755a0a12018-03-19 20:47:37 -0700273 @Nullable
brandjon1d3f3092020-06-08 10:58:25 -0700274 private CachedBzlLoadData computeInlineCachedData(
brandjon91988192020-05-28 14:57:45 -0700275 BzlLoadValue.Key key, Environment env, InliningState inliningState)
brandjon4eed64e2020-08-24 10:13:53 -0700276 throws BzlLoadFailedException, InterruptedException {
brandjon91988192020-05-28 14:57:45 -0700277 // Note to refactorors: No Skyframe calls may be made before the RecordingSkyFunctionEnvironment
278 // is set up below in computeInlineForCacheMiss.
279
nharmata636fb5e2020-07-24 15:30:08 -0700280 // Try the caches of successful loads. We must try the thread-local cache before the shared, for
281 // consistency purposes (see the javadoc of #computeInline).
282 CachedBzlLoadData cachedData = inliningState.successfulLoads.get(key);
brandjon24912642020-05-27 11:28:45 -0700283 if (cachedData == null) {
brandjon8d669292020-05-28 15:27:38 -0700284 cachedData = cachedBzlLoadDataManager.cache.getIfPresent(key);
brandjon24912642020-05-27 11:28:45 -0700285 if (cachedData != null) {
286 // Found a cache hit from another thread's computation; register the recorded deps as if our
brandjonc782bbc2020-08-05 13:47:13 -0700287 // thread required them for the current key. Incorporate into successfulLoads any transitive
brandjon24912642020-05-27 11:28:45 -0700288 // cache hits it does not already contain.
nharmata636fb5e2020-07-24 15:30:08 -0700289 cachedData.traverse(env::registerDependencies, inliningState.successfulLoads);
shreyax31262052019-08-13 13:40:06 -0700290 }
291 }
brandjon91988192020-05-28 14:57:45 -0700292
nharmata636fb5e2020-07-24 15:30:08 -0700293 // See if we've already unsuccessfully visited the bzl.
294 if (inliningState.unsuccessfulLoads.contains(key)) {
295 return null;
296 }
297
298 // If we're here, the bzl must have never been visited before in this calling context. Compute
299 // it ourselves, updating the other data structures as appropriate.
brandjon91988192020-05-28 14:57:45 -0700300 if (cachedData == null) {
nharmata636fb5e2020-07-24 15:30:08 -0700301 try {
302 cachedData = computeInlineForCacheMiss(key, env, inliningState);
303 } finally {
304 if (cachedData != null) {
305 inliningState.successfulLoads.put(key, cachedData);
306 cachedBzlLoadDataManager.cache.put(key, cachedData);
307 } else {
308 inliningState.unsuccessfulLoads.add(key);
309 // Either propagate an exception or fall through for null return.
310 }
brandjon91988192020-05-28 14:57:45 -0700311 }
312 }
313
nharmata636fb5e2020-07-24 15:30:08 -0700314 // On success (from cache hit or from scratch), notify the parent CachedBzlLoadData of its new
315 // child.
brandjon24912642020-05-27 11:28:45 -0700316 if (cachedData != null) {
brandjon91988192020-05-28 14:57:45 -0700317 inliningState.childCachedDataHandler.accept(cachedData);
shreyax755a0a12018-03-19 20:47:37 -0700318 }
319
brandjon91988192020-05-28 14:57:45 -0700320 return cachedData;
321 }
shreyaxc163c912019-02-08 12:45:25 -0800322
brandjon91988192020-05-28 14:57:45 -0700323 @Nullable
324 private CachedBzlLoadData computeInlineForCacheMiss(
325 BzlLoadValue.Key key, Environment env, InliningState inliningState)
brandjon4eed64e2020-08-24 10:13:53 -0700326 throws BzlLoadFailedException, InterruptedException {
brandjon91988192020-05-28 14:57:45 -0700327 // We use an instrumented Skyframe env to capture Skyframe deps in the CachedBzlLoadData. This
328 // generally includes transitive Skyframe deps, but specifically excludes deps underneath
329 // recursively loaded .bzls. We unwrap the instrumented env right before recursively calling
brandjon1d3f3092020-06-08 10:58:25 -0700330 // back into computeInlineCachedData.
brandjon8d669292020-05-28 15:27:38 -0700331 CachedBzlLoadData.Builder cachedDataBuilder = cachedBzlLoadDataManager.cachedDataBuilder();
shreyax755a0a12018-03-19 20:47:37 -0700332 Preconditions.checkState(
333 !(env instanceof RecordingSkyFunctionEnvironment),
334 "Found nested RecordingSkyFunctionEnvironment but it should have been stripped: %s",
335 env);
336 RecordingSkyFunctionEnvironment recordingEnv =
337 new RecordingSkyFunctionEnvironment(
338 env,
brandjon24912642020-05-27 11:28:45 -0700339 cachedDataBuilder::addDep,
340 cachedDataBuilder::addDeps,
341 cachedDataBuilder::noteException);
shreyax755a0a12018-03-19 20:47:37 -0700342
brandjon91988192020-05-28 14:57:45 -0700343 inliningState.beginLoad(key); // track for cyclic load() detection
344 BzlLoadValue value;
345 try {
346 value =
347 computeInternal(
348 key,
349 recordingEnv,
350 inliningState.createChildState(
351 /*childCachedDataHandler=*/ cachedDataBuilder::addTransitiveDeps));
352 } finally {
353 inliningState.finishLoad(key);
shreyax755a0a12018-03-19 20:47:37 -0700354 }
brandjon91988192020-05-28 14:57:45 -0700355 if (value == null) {
356 return null;
357 }
358
359 cachedDataBuilder.setValue(value);
360 cachedDataBuilder.setKey(key);
361 return cachedDataBuilder.build();
Michajlo Matijkiw10c363d2015-10-09 22:11:34 +0000362 }
363
brandjon24912642020-05-27 11:28:45 -0700364 public void resetInliningCache() {
brandjon8d669292020-05-28 15:27:38 -0700365 cachedBzlLoadDataManager.reset();
shreyax755a0a12018-03-19 20:47:37 -0700366 }
367
brandjon1af65cf2020-06-06 18:17:53 -0700368 /**
brandjonb4ebbfc2020-08-13 15:50:23 -0700369 * An opaque object that holds state for the bzl inlining computation initiated by {@link
brandjon1d3f3092020-06-08 10:58:25 -0700370 * #computeInline}.
371 *
372 * <p>An original caller of {@code computeInline} (e.g., {@link PackageFunction}) should obtain
373 * one of these objects using {@link InliningState#create}. When the same caller makes several
374 * calls to {@code computeInline} (e.g., for multiple top-level loads in the same BUILD file), the
375 * same object must be passed to each call.
376 *
377 * <p>When a Skyfunction that is called by {@code BzlLoadFunction}'s inlining code path in turn
378 * calls back into {@code computeInline}, it should forward along the same {@code InliningState}
379 * that it received. In particular, {@link StarlarkBuiltinsFunction} forwards the inlining state
brandjon5a33c632020-10-23 22:09:26 -0700380 * to ensure that 1) the .bzls that get loaded from the {@code @_builtins} pseudo-repository are
brandjon1d3f3092020-06-08 10:58:25 -0700381 * properly recorded as dependencies of all .bzl files that use builtins injection, and 2) the
brandjon5a33c632020-10-23 22:09:26 -0700382 * builtins .bzls are not reevaluated.
brandjon24912642020-05-27 11:28:45 -0700383 */
brandjon1d3f3092020-06-08 10:58:25 -0700384 static class InliningState {
brandjon91988192020-05-28 14:57:45 -0700385 /**
nharmata636fb5e2020-07-24 15:30:08 -0700386 * The set of bzls we're currently in the process of loading but haven't fully visited yet. This
387 * is used for cycle detection since we don't have the benefit of Skyframe's internal cycle
388 * detection. The set must use insertion order for correct error reporting.
389 *
390 * <p>This is disjoint with {@link #successfulLoads} and {@link #unsuccessfulLoads}.
391 *
392 * <p>This is local to current calling context. See {@link #computeInline}.
brandjon91988192020-05-28 14:57:45 -0700393 */
394 // Keyed on the SkyKey, not the label, since label could theoretically be ambiguous, even though
brandjon5a33c632020-10-23 22:09:26 -0700395 // in practice keys from BUILD / WORKSPACE / builtins don't call each other. (Not sure if
brandjon91988192020-05-28 14:57:45 -0700396 // WORKSPACE chunking can cause duplicate labels to appear, but we're robust regardless.)
397 private final LinkedHashSet<BzlLoadValue.Key> loadStack;
398
nharmata636fb5e2020-07-24 15:30:08 -0700399 /**
400 * Cache of bzls that have been fully visited and successfully loaded to a value.
401 *
402 * <p>This and {@link #unsuccessfulLoads} partition the set of fully visited bzls.
403 *
404 * <p>This is local to current calling context. See {@link #computeInline}.
405 */
406 private final Map<BzlLoadValue.Key, CachedBzlLoadData> successfulLoads;
407
408 /**
409 * Set of bzls that have been fully visited, but were not successfully loaded to a value.
410 *
411 * <p>This and {@link #successfulLoads} partition the set of fully visited bzls, and is disjoint
412 * with {@link #loadStack}.
413 *
414 * <p>This is local to current calling context. See {@link #computeInline}.
415 */
416 private final HashSet<BzlLoadValue.Key> unsuccessfulLoads;
417
brandjon91988192020-05-28 14:57:45 -0700418 /** Called when a transitive {@code CachedBzlLoadData} is produced. */
419 private final Consumer<CachedBzlLoadData> childCachedDataHandler;
420
nharmatadc1d9dc2020-04-18 16:53:28 -0700421 private InliningState(
brandjon91988192020-05-28 14:57:45 -0700422 LinkedHashSet<BzlLoadValue.Key> loadStack,
nharmata636fb5e2020-07-24 15:30:08 -0700423 Map<BzlLoadValue.Key, CachedBzlLoadData> successfulLoads,
424 HashSet<BzlLoadValue.Key> unsuccessfulLoads,
425 Consumer<CachedBzlLoadData> childCachedDataHandler) {
brandjon24912642020-05-27 11:28:45 -0700426 this.loadStack = loadStack;
nharmata636fb5e2020-07-24 15:30:08 -0700427 this.successfulLoads = successfulLoads;
428 this.unsuccessfulLoads = unsuccessfulLoads;
brandjon91988192020-05-28 14:57:45 -0700429 this.childCachedDataHandler = childCachedDataHandler;
nharmatadc1d9dc2020-04-18 16:53:28 -0700430 }
brandjon91988192020-05-28 14:57:45 -0700431
brandjon1d3f3092020-06-08 10:58:25 -0700432 /**
433 * Creates an initial {@code InliningState} with no information about previously loaded files
434 * (except the shared cache stored in {@link BzlLoadFunction}).
435 */
436 static InliningState create() {
brandjon91988192020-05-28 14:57:45 -0700437 return new InliningState(
brandjon1d3f3092020-06-08 10:58:25 -0700438 /*loadStack=*/ new LinkedHashSet<>(),
nharmata636fb5e2020-07-24 15:30:08 -0700439 /*successfulLoads=*/ new HashMap<>(),
440 /*unsuccessfulLoads=*/ new HashSet<>(),
441 // No parent value to mutate
442 /*childCachedDataHandler=*/ x -> {});
brandjon91988192020-05-28 14:57:45 -0700443 }
444
brandjon1d3f3092020-06-08 10:58:25 -0700445 private InliningState createChildState(Consumer<CachedBzlLoadData> childCachedDataHandler) {
nharmata636fb5e2020-07-24 15:30:08 -0700446 return new InliningState(
447 loadStack, successfulLoads, unsuccessfulLoads, childCachedDataHandler);
brandjon91988192020-05-28 14:57:45 -0700448 }
449
450 /** Records entry to a {@code load()}, throwing an exception if a cycle is detected. */
brandjon1d3f3092020-06-08 10:58:25 -0700451 private void beginLoad(BzlLoadValue.Key key) throws BzlLoadFailedException {
brandjon91988192020-05-28 14:57:45 -0700452 if (!loadStack.add(key)) {
453 ImmutableList<BzlLoadValue.Key> cycle =
454 CycleUtils.splitIntoPathAndChain(Predicates.equalTo(key), loadStack).second;
mschaller859c9ac2020-09-25 16:09:19 -0700455 throw new BzlLoadFailedException("Starlark load cycle: " + cycle, Code.CYCLE);
brandjon91988192020-05-28 14:57:45 -0700456 }
457 }
458
459 /** Records exit from a {@code load()}. */
brandjon1d3f3092020-06-08 10:58:25 -0700460 private void finishLoad(BzlLoadValue.Key key) throws BzlLoadFailedException {
brandjon91988192020-05-28 14:57:45 -0700461 Preconditions.checkState(loadStack.remove(key), key);
462 }
nharmatadc1d9dc2020-04-18 16:53:28 -0700463 }
464
brandjonb4ebbfc2020-08-13 15:50:23 -0700465 /**
466 * Entry point for compute logic that's common to both (bzl) inlining and non-inlining code paths.
467 */
shreyax755a0a12018-03-19 20:47:37 -0700468 // It is vital that we don't return any value if any call to env#getValue(s)OrThrow throws an
469 // exception. We are allowed to wrap the thrown exception and rethrow it for any calling functions
470 // to handle though.
471 @Nullable
brandjon771a0292020-05-26 12:04:16 -0700472 private BzlLoadValue computeInternal(
473 BzlLoadValue.Key key, Environment env, @Nullable InliningState inliningState)
brandjon4eed64e2020-08-24 10:13:53 -0700474 throws BzlLoadFailedException, InterruptedException {
brandjonbc884e12020-05-22 10:24:37 -0700475 Label label = key.getLabel();
brandjon7154be02020-05-22 09:47:32 -0700476 PathFragment filePath = label.toPathFragment();
John Field110b0652015-11-13 21:56:42 +0000477
brandjond5d86772020-10-26 16:21:22 -0700478 StarlarkSemantics starlarkSemantics = PrecomputedValue.STARLARK_SEMANTICS.get(env);
479 if (starlarkSemantics == null) {
480 return null;
481 }
482
483 BzlCompileValue.Key compileKey = validatePackageAndGetCompileKey(key, env, starlarkSemantics);
adonovan0a1f0f52020-09-16 11:11:04 -0700484 if (compileKey == null) {
brandjon4d15f2b2020-08-12 09:15:52 -0700485 return null;
486 }
adonovan0a1f0f52020-09-16 11:11:04 -0700487 BzlCompileValue compileValue;
brandjon4d15f2b2020-08-12 09:15:52 -0700488 try {
adonovan0a1f0f52020-09-16 11:11:04 -0700489 compileValue = getter.getBzlCompileValue(compileKey, env);
490 } catch (BzlCompileFunction.FailedIOException e) {
brandjonb9c81cd2020-08-24 11:00:58 -0700491 throw BzlLoadFailedException.errorReadingBzl(filePath, e);
brandjon4d15f2b2020-08-12 09:15:52 -0700492 }
adonovan0a1f0f52020-09-16 11:11:04 -0700493 if (compileValue == null) {
brandjon4d15f2b2020-08-12 09:15:52 -0700494 return null;
495 }
496
497 BzlLoadValue result = null;
adonovan0a1f0f52020-09-16 11:11:04 -0700498 // Release the compiled bzl iff the value gets completely evaluated (to either error or non-null
499 // result).
brandjon4d15f2b2020-08-12 09:15:52 -0700500 boolean completed = true;
501 try {
brandjond5d86772020-10-26 16:21:22 -0700502 result =
503 computeInternalWithCompiledBzl(key, compileValue, starlarkSemantics, env, inliningState);
brandjon4d15f2b2020-08-12 09:15:52 -0700504 completed = result != null;
505 } finally {
506 if (completed) { // only false on unexceptional null result
adonovan0a1f0f52020-09-16 11:11:04 -0700507 getter.doneWithBzlCompileValue(compileKey);
brandjon4d15f2b2020-08-12 09:15:52 -0700508 }
509 }
510 return result;
511 }
512
513 /**
brandjond5d86772020-10-26 16:21:22 -0700514 * Given a bzl key, validates that the corresponding package exists (if required) and returns the
515 * associated compile key based on the package's root. Returns null for a missing Skyframe dep or
516 * unspecified exception.
brandjon4d15f2b2020-08-12 09:15:52 -0700517 *
brandjond5d86772020-10-26 16:21:22 -0700518 * <p>.bzl files are not necessarily targets, because they can be loaded by BUILD and other .bzl
519 * files without ever being declared in a BUILD file. However, .bzl files are still identified by
520 * a label in the same way that file targets are. In particular, it is illegal to refer to a .bzl
521 * file using a label whose package part is not the .bzl file's innermost containing package. For
522 * example, if pkg and pkg/subpkg have BUILD files but not pkg/subdir, then {@code
523 * pkg/subdir:foo.bzl} and {@code pkg:subpkg/foo.bzl} are disallowed.
524 *
525 * <p>In the case of builtins .bzl files, all labels are written as if the pseudo-repo constitutes
526 * one big package, e.g {@code @builtins//:some/path/foo.bzl}, but no BUILD file need exist. The
527 * compile key's root is determined by {@code --experimental_builtins_bzl_path} instead of by
528 * package lookup.
brandjon4d15f2b2020-08-12 09:15:52 -0700529 */
530 @Nullable
brandjond5d86772020-10-26 16:21:22 -0700531 private BzlCompileValue.Key validatePackageAndGetCompileKey(
532 BzlLoadValue.Key key, Environment env, StarlarkSemantics starlarkSemantics)
533 throws BzlLoadFailedException, InterruptedException {
brandjon3f0917a2020-10-26 18:01:50 -0700534 Label label = key.getLabel();
535
brandjond5d86772020-10-26 16:21:22 -0700536 // Bypass package lookup entirely if builtins.
537 if (key.isBuiltins()) {
brandjon3f0917a2020-10-26 18:01:50 -0700538 if (!label.getPackageName().isEmpty()) {
539 throw BzlLoadFailedException.noBuildFile(label, "@_builtins cannot have subpackages");
540 }
brandjond5d86772020-10-26 16:21:22 -0700541 return key.getCompileKey(getBuiltinsRoot(starlarkSemantics));
542 }
brandjon4d15f2b2020-08-12 09:15:52 -0700543
544 // Do package lookup.
545 PathFragment dir = Label.getContainingDirectory(label);
Googler0617f2c2020-10-22 08:43:54 -0700546 PackageIdentifier dirId = PackageIdentifier.create(label.getRepository(), dir);
brandjon4d15f2b2020-08-12 09:15:52 -0700547 ContainingPackageLookupValue packageLookup;
548 try {
549 packageLookup =
550 (ContainingPackageLookupValue)
551 env.getValueOrThrow(
552 ContainingPackageLookupValue.key(dirId),
553 BuildFileNotFoundException.class,
554 InconsistentFilesystemException.class);
brandjonb9c81cd2020-08-24 11:00:58 -0700555 } catch (BuildFileNotFoundException | InconsistentFilesystemException e) {
mschaller859c9ac2020-09-25 16:09:19 -0700556 throw BzlLoadFailedException.errorFindingContainingPackage(label.toPathFragment(), e);
brandjon4d15f2b2020-08-12 09:15:52 -0700557 }
558 if (packageLookup == null) {
559 return null;
560 }
561
adonovan0a1f0f52020-09-16 11:11:04 -0700562 // Resolve to compile key or error.
563 BzlCompileValue.Key compileKey;
brandjon4d15f2b2020-08-12 09:15:52 -0700564 boolean packageOk =
565 packageLookup.hasContainingPackage()
566 && packageLookup.getContainingPackageName().equals(label.getPackageIdentifier());
567 if (key.isBuildPrelude() && !packageOk) {
568 // Ignore the prelude, its package doesn't exist.
adonovan0a1f0f52020-09-16 11:11:04 -0700569 compileKey = BzlCompileValue.EMPTY_PRELUDE_KEY;
brandjon4d15f2b2020-08-12 09:15:52 -0700570 } else {
571 if (packageOk) {
adonovan0a1f0f52020-09-16 11:11:04 -0700572 compileKey = key.getCompileKey(packageLookup.getContainingPackageRoot());
brandjon4d15f2b2020-08-12 09:15:52 -0700573 } else {
574 if (!packageLookup.hasContainingPackage()) {
575 throw BzlLoadFailedException.noBuildFile(
576 label, packageLookup.getReasonForNoContainingPackage());
577 } else {
578 throw BzlLoadFailedException.labelCrossesPackageBoundary(label, packageLookup);
579 }
580 }
581 }
adonovan0a1f0f52020-09-16 11:11:04 -0700582 return compileKey;
brandjon4d15f2b2020-08-12 09:15:52 -0700583 }
584
brandjond5d86772020-10-26 16:21:22 -0700585 private Root getBuiltinsRoot(StarlarkSemantics starlarkSemantics) throws BzlLoadFailedException {
586 String path = starlarkSemantics.get(BuildLanguageOptions.EXPERIMENTAL_BUILTINS_BZL_PATH);
587 if (path.isEmpty()) {
588 throw new IllegalStateException("Requested builtins root, but injection is disabled");
589 } else if (path.equals("%install_base%")) {
590 // TODO(#11437): Support reading builtins from the install base (the expected case in
591 // production)
592 // return Root.fromPath(directories.getInstallBase().getRelative("bzl_builtins"));
593 //
594 // This feature is experimental so the exact exception doesn't matter much, but let's not
595 // crash Bazel.
596 throw new BzlLoadFailedException(
597 "Loading builtins from install base is not implemented", Code.BUILTINS_ERROR);
598 } else if (path.equals("%workspace%")) {
599 // TODO(#11437): Support reading builtins from a well-known path in the workspace, as
600 // determined by the RuleClassProvider.
601 // return Root.fromPath(directories.getWorkspace());
602 throw new BzlLoadFailedException(
603 "Loading builtins from workspace is not implemented", Code.BUILTINS_ERROR);
604 } else {
605 // TODO(#11437): Should we consider interning these roots?
606 return Root.fromPath(directories.getWorkspace().getRelative(path));
607 }
608 }
609
brandjon4d15f2b2020-08-12 09:15:52 -0700610 /**
adonovan0a1f0f52020-09-16 11:11:04 -0700611 * Compute logic for once the compiled .bzl has been fetched and confirmed to exist (though it may
612 * have Starlark errors).
brandjon4d15f2b2020-08-12 09:15:52 -0700613 */
614 @Nullable
adonovan0a1f0f52020-09-16 11:11:04 -0700615 private BzlLoadValue computeInternalWithCompiledBzl(
brandjon4d15f2b2020-08-12 09:15:52 -0700616 BzlLoadValue.Key key,
adonovan0a1f0f52020-09-16 11:11:04 -0700617 BzlCompileValue compileValue,
brandjond5d86772020-10-26 16:21:22 -0700618 StarlarkSemantics starlarkSemantics,
brandjon4d15f2b2020-08-12 09:15:52 -0700619 Environment env,
620 @Nullable InliningState inliningState)
brandjon4eed64e2020-08-24 10:13:53 -0700621 throws BzlLoadFailedException, InterruptedException {
brandjon4d15f2b2020-08-12 09:15:52 -0700622 Label label = key.getLabel();
brandjon4d15f2b2020-08-12 09:15:52 -0700623
brandjon3f017db2020-08-20 07:51:39 -0700624 // Ensure the .bzl exists and passes static checks (parsing, resolving).
adonovan0a1f0f52020-09-16 11:11:04 -0700625 // (A missing prelude file still returns a valid but empty BzlCompileValue.)
626 if (!compileValue.lookupSuccessful()) {
mschaller859c9ac2020-09-25 16:09:19 -0700627 throw new BzlLoadFailedException(compileValue.getError(), Code.COMPILE_ERROR);
brandjon4d15f2b2020-08-12 09:15:52 -0700628 }
adonovan0a1f0f52020-09-16 11:11:04 -0700629 StarlarkFile file = compileValue.getAST();
brandjon4d15f2b2020-08-12 09:15:52 -0700630 if (!file.ok()) {
brandjond5d86772020-10-26 16:21:22 -0700631 throw BzlLoadFailedException.starlarkErrors(label);
brandjon4d15f2b2020-08-12 09:15:52 -0700632 }
633
brandjon3f017db2020-08-20 07:51:39 -0700634 // Determine dependency BzlLoadValue keys for the load statements in this bzl. Labels are
635 // resolved relative to the current repo mapping.
brandjon7154be02020-05-22 09:47:32 -0700636 ImmutableMap<RepositoryName, RepositoryName> repoMapping = getRepositoryMapping(key, env);
Googlerb2e1fe32019-09-20 14:00:39 -0700637 if (repoMapping == null) {
dannark4e42c322018-11-08 19:32:04 -0800638 return null;
639 }
adonovanc0e86902020-11-19 15:50:29 -0800640 ImmutableList<Pair<String, Label>> loadLabels =
brandjon7154be02020-05-22 09:47:32 -0700641 getLoadLabels(env.getListener(), file, label.getPackageIdentifier(), repoMapping);
brandjon3f017db2020-08-20 07:51:39 -0700642 if (loadLabels == null) {
Googlerb2e1fe32019-09-20 14:00:39 -0700643 // malformed load statements
brandjond5d86772020-10-26 16:21:22 -0700644 throw BzlLoadFailedException.starlarkErrors(label);
Googlerb2e1fe32019-09-20 14:00:39 -0700645 }
brandjon3f017db2020-08-20 07:51:39 -0700646 List<BzlLoadValue.Key> loadKeys = Lists.newArrayListWithExpectedSize(loadLabels.size());
647 for (Pair<String, Label> entry : loadLabels) {
648 loadKeys.add(key.getKeyForLoad(entry.second));
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100649 }
adonovan699a90a2020-05-20 19:48:53 -0700650
brandjon3f017db2020-08-20 07:51:39 -0700651 // Evaluate the dependency bzls. When not using bzl inlining, this is done in parallel for all
652 // loads.
brandjon844fef52020-06-08 10:30:36 -0700653 // TODO(bazel-team): In case of a failed load(), we should report the location of the load()
654 // statement in the requesting file, e.g. using
brandjon4eed64e2020-08-24 10:13:53 -0700655 // file.getLoadStatements().get(...).getStartLocation().
brandjon3f017db2020-08-20 07:51:39 -0700656 List<BzlLoadValue> loadValues =
adonovan699a90a2020-05-20 19:48:53 -0700657 inliningState == null
brandjon844fef52020-06-08 10:30:36 -0700658 ? computeBzlLoadsWithSkyframe(env, loadKeys, file)
659 : computeBzlLoadsWithInlining(env, loadKeys, file, inliningState);
brandjon3f017db2020-08-20 07:51:39 -0700660 if (loadValues == null) {
adonovan699a90a2020-05-20 19:48:53 -0700661 return null; // Skyframe deps unavailable
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100662 }
Janak Ramakrishnandf0531f2015-09-23 17:30:04 +0000663
brandjon3f017db2020-08-20 07:51:39 -0700664 // Accumulate a transitive digest of the bzl file, the digests of its direct loads, and the
brandjon5a33c632020-10-23 22:09:26 -0700665 // digest of the @_builtins pseudo-repository (if applicable).
adonovan1d93d262020-05-07 14:50:42 -0700666 Fingerprint fp = new Fingerprint();
adonovan0a1f0f52020-09-16 11:11:04 -0700667 fp.addBytes(compileValue.getDigest());
brandjon3f017db2020-08-20 07:51:39 -0700668
669 // Populate the load map and add transitive digests to the fingerprint.
670 Map<String, Module> loadMap = Maps.newLinkedHashMapWithExpectedSize(loadLabels.size());
671 for (int i = 0; i < loadLabels.size(); i++) {
672 String loadString = loadLabels.get(i).first;
673 BzlLoadValue v = loadValues.get(i);
674 loadMap.put(loadString, v.getModule()); // dups ok
adonovan1d93d262020-05-07 14:50:42 -0700675 fp.addBytes(v.getTransitiveDigest());
Janak Ramakrishnandfd34972015-09-22 02:53:05 +0000676 }
brandjon3f017db2020-08-20 07:51:39 -0700677
brandjond5d86772020-10-26 16:21:22 -0700678 // Retrieve predeclared symbols and complete the digest computation.
brandjon3f017db2020-08-20 07:51:39 -0700679 ImmutableMap<String, Object> predeclared =
680 getAndDigestPredeclaredEnvironment(key, env, starlarkSemantics, fp, inliningState);
681 if (predeclared == null) {
682 return null;
683 }
adonovan1d93d262020-05-07 14:50:42 -0700684 byte[] transitiveDigest = fp.digestAndReset();
John Field1ea7fc32015-12-22 19:37:19 +0000685
brandjon3f017db2020-08-20 07:51:39 -0700686 // Construct the initial Starlark module, then execute the code and return the result.
687 // The additional information in BazelModuleContext reifies the load DAG.
brandjonbc9bd062020-08-12 09:41:12 -0700688 Module module = Module.withPredeclared(starlarkSemantics, predeclared);
adonovan87b46082020-07-08 15:58:04 -0700689 module.setClientData(
690 BazelModuleContext.create(
brandjon3f017db2020-08-20 07:51:39 -0700691 label, file.getStartLocation().file(), ImmutableMap.copyOf(loadMap), transitiveDigest));
adonovand1b1c4c2020-08-26 18:39:53 -0700692
693 // compile
694 //
adonovan0a1f0f52020-09-16 11:11:04 -0700695 // Note: file is shared: it belongs to BzlCompileFunction, which already resolved it;
696 // see end of BzlCompileFunction.compute.
697 // TODO(adonovan): compile it there too. We must ensure the Module env there has the
698 // same set of keys as here.
adonovand1b1c4c2020-08-26 18:39:53 -0700699 //
700 // For now, Program temporarily gives us a way to compile an already-resolved file:
701 Program prog = Program.compileResolvedFile(file);
702
brandjona0c6f812020-06-06 14:13:19 -0700703 // executeBzlFile may post events to the Environment's handler, but events do not matter when
brandjon3f017db2020-08-20 07:51:39 -0700704 // caching BzlLoadValues. Note that executing the code mutates the module.
brandjona0c6f812020-06-06 14:13:19 -0700705 executeBzlFile(
adonovand1b1c4c2020-08-26 18:39:53 -0700706 prog, key.getLabel(), module, loadMap, starlarkSemantics, env.getListener(), repoMapping);
adonovan39e42d12020-07-09 09:16:58 -0700707 return new BzlLoadValue(module, transitiveDigest);
John Fielda97e17f2015-11-13 02:19:52 +0000708 }
709
laurentlb4efb6c22019-07-03 15:33:46 -0700710 private static ImmutableMap<RepositoryName, RepositoryName> getRepositoryMapping(
brandjon771a0292020-05-26 12:04:16 -0700711 BzlLoadValue.Key key, Environment env) throws InterruptedException {
brandjon7154be02020-05-22 09:47:32 -0700712 Label enclosingFileLabel = key.getLabel();
pcloudy260c3aa2019-01-04 02:33:50 -0800713
dannarkd102a392019-01-09 13:12:01 -0800714 ImmutableMap<RepositoryName, RepositoryName> repositoryMapping;
brandjonb28a76b2020-05-26 12:53:22 -0700715 if (key instanceof BzlLoadValue.KeyForWorkspace) {
brandjon7154be02020-05-22 09:47:32 -0700716 // Still during workspace file evaluation
brandjonb28a76b2020-05-26 12:53:22 -0700717 BzlLoadValue.KeyForWorkspace keyForWorkspace = (BzlLoadValue.KeyForWorkspace) key;
718 if (keyForWorkspace.getWorkspaceChunk() == 0) {
brandjon7154be02020-05-22 09:47:32 -0700719 // There is no previous workspace chunk
720 repositoryMapping = ImmutableMap.of();
721 } else {
722 SkyKey workspaceFileKey =
723 WorkspaceFileValue.key(
brandjonb28a76b2020-05-26 12:53:22 -0700724 keyForWorkspace.getWorkspacePath(), keyForWorkspace.getWorkspaceChunk() - 1);
brandjon7154be02020-05-22 09:47:32 -0700725 WorkspaceFileValue workspaceFileValue = (WorkspaceFileValue) env.getValue(workspaceFileKey);
726 // Note: we know for sure that the requested WorkspaceFileValue is fully computed so we do
727 // not need to check if it is null
728 repositoryMapping =
729 workspaceFileValue
730 .getRepositoryMapping()
Googler0617f2c2020-10-22 08:43:54 -0700731 .getOrDefault(enclosingFileLabel.getRepository(), ImmutableMap.of());
brandjon7154be02020-05-22 09:47:32 -0700732 }
733 } else {
734 // We are fully done with workspace evaluation so we should get the mappings from the
735 // final RepositoryMappingValue
dannark4e42c322018-11-08 19:32:04 -0800736 PackageIdentifier packageIdentifier = enclosingFileLabel.getPackageIdentifier();
737 RepositoryMappingValue repositoryMappingValue =
738 (RepositoryMappingValue)
739 env.getValue(RepositoryMappingValue.key(packageIdentifier.getRepository()));
740 if (repositoryMappingValue == null) {
741 return null;
742 }
743 repositoryMapping = repositoryMappingValue.getRepositoryMapping();
dannark4e42c322018-11-08 19:32:04 -0800744 }
dannarkd102a392019-01-09 13:12:01 -0800745 return repositoryMapping;
746 }
747
748 /**
adonovan699a90a2020-05-20 19:48:53 -0700749 * Returns a list of pairs mapping each load string in the BUILD or .bzl file to the Label it
750 * resolves to. Labels are resolved relative to {@code base}, the file's package. If any load
751 * statement is malformed, the function reports one or more errors to the handler and returns
752 * null. Order matches the source.
John Fielda97e17f2015-11-13 02:19:52 +0000753 */
754 @Nullable
adonovanc0e86902020-11-19 15:50:29 -0800755 static ImmutableList<Pair<String, Label>> getLoadLabels(
Googlerb2e1fe32019-09-20 14:00:39 -0700756 EventHandler handler,
Googler66d099e2019-09-26 08:07:06 -0700757 StarlarkFile file,
Googlerb2e1fe32019-09-20 14:00:39 -0700758 PackageIdentifier base,
759 ImmutableMap<RepositoryName, RepositoryName> repoMapping) {
760 Preconditions.checkArgument(!base.getRepository().isDefault());
761
762 // It's redundant that getRelativeWithRemapping needs a Label;
763 // a PackageIdentifier should suffice. Make one here.
764 Label buildLabel = getBUILDLabel(base);
765
766 boolean ok = true;
adonovanc0e86902020-11-19 15:50:29 -0800767 ImmutableList.Builder<Pair<String, Label>> loads = ImmutableList.builder();
Googlerb2e1fe32019-09-20 14:00:39 -0700768 for (Statement stmt : file.getStatements()) {
769 if (stmt instanceof LoadStatement) {
770 LoadStatement load = (LoadStatement) stmt;
771 String module = load.getImport().getValue();
772
773 // Parse the load statement's module string as a label.
774 // It must end in .bzl and not be in package "//external".
Googlerb2e1fe32019-09-20 14:00:39 -0700775 try {
776 Label label = buildLabel.getRelativeWithRemapping(module, repoMapping);
777 if (!label.getName().endsWith(".bzl")) {
778 throw new LabelSyntaxException("The label must reference a file with extension '.bzl'");
779 }
780 if (label.getPackageIdentifier().equals(LabelConstants.EXTERNAL_PACKAGE_IDENTIFIER)) {
781 throw new LabelSyntaxException(
782 "Starlark files may not be loaded from the //external package");
783 }
brandjon3f0917a2020-10-26 18:01:50 -0700784 if (StarlarkBuiltinsValue.isBuiltinsRepo(base.getRepository())
785 && !StarlarkBuiltinsValue.isBuiltinsRepo(label.getRepository())) {
786 throw new LabelSyntaxException(
787 ".bzl files in @_builtins cannot load from outside of @_builtins");
788 }
adonovan699a90a2020-05-20 19:48:53 -0700789 loads.add(Pair.of(module, label));
Googlerb2e1fe32019-09-20 14:00:39 -0700790 } catch (LabelSyntaxException ex) {
Googler83439e62019-09-24 12:11:30 -0700791 handler.handle(
adonovan22096af2020-01-08 07:09:52 -0800792 Event.error(
793 load.getImport().getStartLocation(), "in load statement: " + ex.getMessage()));
Googlerb2e1fe32019-09-20 14:00:39 -0700794 ok = false;
795 }
796 }
797 }
adonovanc0e86902020-11-19 15:50:29 -0800798 return ok ? loads.build() : null;
Googlerb2e1fe32019-09-20 14:00:39 -0700799 }
800
801 private static Label getBUILDLabel(PackageIdentifier pkgid) {
802 try {
803 return Label.create(pkgid, "BUILD");
804 } catch (LabelSyntaxException e) {
805 // Shouldn't happen; the Label is well-formed by construction.
806 throw new IllegalStateException(e);
807 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100808 }
809
adgarebc0f2c2019-08-15 15:36:56 -0700810 /**
brandjon24912642020-05-27 11:28:45 -0700811 * Computes the BzlLoadValue for all given keys using vanilla Skyframe evaluation, returning
812 * {@code null} if Skyframe deps were missing and have been requested.
adgarebc0f2c2019-08-15 15:36:56 -0700813 */
814 @Nullable
brandjon8d669292020-05-28 15:27:38 -0700815 private static List<BzlLoadValue> computeBzlLoadsWithSkyframe(
brandjon844fef52020-06-08 10:30:36 -0700816 Environment env, List<BzlLoadValue.Key> keys, StarlarkFile requestingFile)
brandjon771a0292020-05-26 12:04:16 -0700817 throws BzlLoadFailedException, InterruptedException {
818 List<BzlLoadValue> bzlLoads = Lists.newArrayListWithExpectedSize(keys.size());
819 Map<SkyKey, ValueOrException<BzlLoadFailedException>> values =
820 env.getValuesOrThrow(keys, BzlLoadFailedException.class);
brandjonf4f91932020-05-11 10:20:48 -0700821 // Uses same order as load()s in the file. Order matters since we report the first error.
brandjon771a0292020-05-26 12:04:16 -0700822 for (BzlLoadValue.Key key : keys) {
adgarebc0f2c2019-08-15 15:36:56 -0700823 try {
brandjon771a0292020-05-26 12:04:16 -0700824 bzlLoads.add((BzlLoadValue) values.get(key).get());
825 } catch (BzlLoadFailedException exn) {
brandjon844fef52020-06-08 10:30:36 -0700826 throw BzlLoadFailedException.whileLoadingDep(requestingFile.getStartLocation().file(), exn);
adgarebc0f2c2019-08-15 15:36:56 -0700827 }
828 }
brandjon771a0292020-05-26 12:04:16 -0700829 return env.valuesMissing() ? null : bzlLoads;
adgarebc0f2c2019-08-15 15:36:56 -0700830 }
831
832 /**
brandjon24912642020-05-27 11:28:45 -0700833 * Computes the BzlLoadValue for all given keys by reusing this instance of the BzlLoadFunction,
nharmata636fb5e2020-07-24 15:30:08 -0700834 * bypassing traditional Skyframe evaluation.
835 *
836 * @return null if there was a missing Skyframe dep, an unspecified exception in a Skyframe dep
837 * request, or if this was a duplicate unsuccessful visitation
adgarebc0f2c2019-08-15 15:36:56 -0700838 */
839 @Nullable
brandjon24912642020-05-27 11:28:45 -0700840 private List<BzlLoadValue> computeBzlLoadsWithInlining(
brandjon844fef52020-06-08 10:30:36 -0700841 Environment env,
842 List<BzlLoadValue.Key> keys,
843 StarlarkFile requestingFile,
844 InliningState inliningState)
brandjon4eed64e2020-08-24 10:13:53 -0700845 throws BzlLoadFailedException, InterruptedException {
brandjon844fef52020-06-08 10:30:36 -0700846 String filePathForErrors = requestingFile.getStartLocation().file();
adgarebc0f2c2019-08-15 15:36:56 -0700847 Preconditions.checkState(
848 env instanceof RecordingSkyFunctionEnvironment,
brandjon771a0292020-05-26 12:04:16 -0700849 "Expected to be recording dep requests when inlining BzlLoadFunction: %s",
brandjon844fef52020-06-08 10:30:36 -0700850 filePathForErrors);
adgarebc0f2c2019-08-15 15:36:56 -0700851 Environment strippedEnv = ((RecordingSkyFunctionEnvironment) env).getDelegate();
brandjon91988192020-05-28 14:57:45 -0700852
brandjon771a0292020-05-26 12:04:16 -0700853 List<BzlLoadValue> bzlLoads = Lists.newArrayListWithExpectedSize(keys.size());
nharmata60d04602020-08-11 12:28:57 -0700854 // For the sake of ensuring the graph structure is deterministic, we need to request all of our
brandjonb4ebbfc2020-08-13 15:50:23 -0700855 // deps, even if some of them yield errors. The first exception that is seen gets deferred, to
856 // be raised after the loop. All other exceptions are swallowed.
nharmata60d04602020-08-11 12:28:57 -0700857 //
brandjonb4ebbfc2020-08-13 15:50:23 -0700858 // To see how immediately returning the first error leads to non-determinism, consider the case
859 // of two dependencies A and B, where A is in error and appears in a load statement above B.
860 // If A has completed at the time we request it, and if we were to immediately propagate that
861 // error, we never request B. On the other hand, if A is missing (null return), we do request B
862 // in the meantime for the sake of parallelism.
863 //
864 // This approach assumes --keep_going; determinism is not guaranteed otherwise. It also assumes
865 // InterruptedException does not occur, since we don't catch and defer it.
brandjon4eed64e2020-08-24 10:13:53 -0700866 BzlLoadFailedException deferredException = null;
adgarebc0f2c2019-08-15 15:36:56 -0700867 boolean valuesMissing = false;
brandjon771a0292020-05-26 12:04:16 -0700868 // NOTE: Iterating over loads in the order listed in the file.
869 for (BzlLoadValue.Key key : keys) {
brandjon24912642020-05-27 11:28:45 -0700870 CachedBzlLoadData cachedData;
adgarebc0f2c2019-08-15 15:36:56 -0700871 try {
brandjon1d3f3092020-06-08 10:58:25 -0700872 cachedData = computeInlineCachedData(key, strippedEnv, inliningState);
brandjon844fef52020-06-08 10:30:36 -0700873 } catch (BzlLoadFailedException e) {
brandjon4eed64e2020-08-24 10:13:53 -0700874 if (deferredException == null) {
875 deferredException = BzlLoadFailedException.whileLoadingDep(filePathForErrors, e);
876 }
adgarebc0f2c2019-08-15 15:36:56 -0700877 continue;
878 }
brandjon24912642020-05-27 11:28:45 -0700879 if (cachedData == null) {
nharmata636fb5e2020-07-24 15:30:08 -0700880 // A null value for `cachedData` can occur when it (or its transitive loads) has a Skyframe
881 // dep that is missing or in error. It can also occur if there's a transitive load on a bzl
882 // that was already seen by inliningState and which returned null (note: in this case, it's
883 // not necessarily true that there are missing Skyframe deps because this bzl could have
884 // already been visited unsuccessfully). In both these cases, we want to continue making our
885 // inline calls, so as to maximize the number of dependent (non-inlined) SkyFunctions that
886 // are requested and avoid a quadratic number of restarts.
adgarebc0f2c2019-08-15 15:36:56 -0700887 valuesMissing = true;
888 } else {
brandjon24912642020-05-27 11:28:45 -0700889 bzlLoads.add(cachedData.getValue());
adgarebc0f2c2019-08-15 15:36:56 -0700890 }
891 }
892 if (deferredException != null) {
brandjon4eed64e2020-08-24 10:13:53 -0700893 throw deferredException;
adgarebc0f2c2019-08-15 15:36:56 -0700894 }
brandjon771a0292020-05-26 12:04:16 -0700895 return valuesMissing ? null : bzlLoads;
adgarebc0f2c2019-08-15 15:36:56 -0700896 }
897
brandjona0c6f812020-06-06 14:13:19 -0700898 /**
brandjonbc9bd062020-08-12 09:41:12 -0700899 * Obtains the predeclared environment for a .bzl file, based on the type of .bzl and (if
brandjona0c6f812020-06-06 14:13:19 -0700900 * applicable) the injected builtins.
brandjonbc9bd062020-08-12 09:41:12 -0700901 *
902 * <p>Returns null if there was a missing Skyframe dep or unspecified exception.
brandjon3f017db2020-08-20 07:51:39 -0700903 *
904 * <p>In the case that injected builtins are used, updates the given fingerprint with the digest
brandjon5a33c632020-10-23 22:09:26 -0700905 * of the {@code @_builtins} pseudo-repository.
brandjona0c6f812020-06-06 14:13:19 -0700906 */
brandjonbc9bd062020-08-12 09:41:12 -0700907 @Nullable
brandjon3f017db2020-08-20 07:51:39 -0700908 private ImmutableMap<String, Object> getAndDigestPredeclaredEnvironment(
brandjonbc9bd062020-08-12 09:41:12 -0700909 BzlLoadValue.Key key,
910 Environment env,
911 StarlarkSemantics starlarkSemantics,
brandjon3f017db2020-08-20 07:51:39 -0700912 Fingerprint fp,
brandjonbc9bd062020-08-12 09:41:12 -0700913 InliningState inliningState)
914 throws BzlLoadFailedException, InterruptedException {
brandjona0c6f812020-06-06 14:13:19 -0700915 if (key instanceof BzlLoadValue.KeyForBuild) {
brandjon3f017db2020-08-20 07:51:39 -0700916 // TODO(#11437): Remove ability to disable injection by setting flag to empty string.
adonovan028e1ad2020-09-07 07:09:59 -0700917 if (starlarkSemantics.get(BuildLanguageOptions.EXPERIMENTAL_BUILTINS_BZL_PATH).isEmpty()) {
brandjonaae35ab2020-08-20 11:04:26 -0700918 return packageFactory.getUninjectedBuildBzlEnv();
brandjon3f017db2020-08-20 07:51:39 -0700919 }
brandjonbc9bd062020-08-12 09:41:12 -0700920 StarlarkBuiltinsValue starlarkBuiltinsValue;
921 try {
brandjon3f017db2020-08-20 07:51:39 -0700922 if (inliningState == null) {
923 starlarkBuiltinsValue =
924 (StarlarkBuiltinsValue)
925 env.getValueOrThrow(StarlarkBuiltinsValue.key(), BuiltinsFailedException.class);
brandjonbc9bd062020-08-12 09:41:12 -0700926 } else {
brandjon3f017db2020-08-20 07:51:39 -0700927 starlarkBuiltinsValue =
928 StarlarkBuiltinsFunction.computeInline(
929 StarlarkBuiltinsValue.key(),
930 env,
931 inliningState,
932 packageFactory,
933 /*bzlLoadFunction=*/ this);
brandjonbc9bd062020-08-12 09:41:12 -0700934 }
935 } catch (BuiltinsFailedException e) {
brandjon3f017db2020-08-20 07:51:39 -0700936 throw BzlLoadFailedException.builtinsFailed(key.getLabel(), e);
brandjonbc9bd062020-08-12 09:41:12 -0700937 }
938 if (starlarkBuiltinsValue == null) {
939 return null;
940 }
brandjon3f017db2020-08-20 07:51:39 -0700941 fp.addBytes(starlarkBuiltinsValue.transitiveDigest);
brandjon1af65cf2020-06-06 18:17:53 -0700942 return starlarkBuiltinsValue.predeclaredForBuildBzl;
brandjona0c6f812020-06-06 14:13:19 -0700943 } else if (key instanceof BzlLoadValue.KeyForWorkspace) {
brandjonaae35ab2020-08-20 11:04:26 -0700944 return packageFactory.getWorkspaceBzlEnv();
brandjona0c6f812020-06-06 14:13:19 -0700945 } else if (key instanceof BzlLoadValue.KeyForBuiltins) {
brandjonaae35ab2020-08-20 11:04:26 -0700946 return packageFactory.getBuiltinsBzlEnv();
brandjona0c6f812020-06-06 14:13:19 -0700947 } else {
948 throw new AssertionError("Unknown key type: " + key.getClass());
949 }
950 }
951
adonovand1b1c4c2020-08-26 18:39:53 -0700952 /** Executes the compiled .bzl file defining the module to be loaded. */
brandjona0c6f812020-06-06 14:13:19 -0700953 private void executeBzlFile(
adonovand1b1c4c2020-08-26 18:39:53 -0700954 Program prog,
brandjon7154be02020-05-22 09:47:32 -0700955 Label label,
brandjona0c6f812020-06-06 14:13:19 -0700956 Module module,
adonovan1d93d262020-05-07 14:50:42 -0700957 Map<String, Module> loadedModules,
laurentlb6659b4c2019-02-18 07:23:36 -0800958 StarlarkSemantics starlarkSemantics,
brandjona0c6f812020-06-06 14:13:19 -0700959 ExtendedEventHandler skyframeEventHandler,
dannarkd102a392019-01-09 13:12:01 -0800960 ImmutableMap<RepositoryName, RepositoryName> repositoryMapping)
brandjon771a0292020-05-26 12:04:16 -0700961 throws BzlLoadFailedException, InterruptedException {
brandjon771a0292020-05-26 12:04:16 -0700962 try (Mutability mu = Mutability.create("loading", label)) {
adonovan09d13702020-05-19 08:26:55 -0700963 StarlarkThread thread = new StarlarkThread(mu, starlarkSemantics);
adonovandfcda9e2020-05-08 13:10:45 -0700964 thread.setLoader(loadedModules::get);
brandjona0c6f812020-06-06 14:13:19 -0700965 StoredEventHandler starlarkEventHandler = new StoredEventHandler();
966 thread.setPrintHandler(Event.makeDebugPrintHandler(starlarkEventHandler));
brandjondb4d1ea2020-08-14 11:31:08 -0700967 packageFactory
968 .getRuleClassProvider()
969 .setStarlarkThreadContext(thread, label, repositoryMapping);
adonovand1b1c4c2020-08-26 18:39:53 -0700970 execAndExport(prog, label, starlarkEventHandler, module, thread);
Googler2234d382015-03-24 17:18:02 +0000971
brandjona0c6f812020-06-06 14:13:19 -0700972 Event.replayEventsOn(skyframeEventHandler, starlarkEventHandler.getEvents());
973 for (Postable post : starlarkEventHandler.getPosts()) {
974 skyframeEventHandler.post(post);
Klaus Aehligda0a7012017-06-14 13:40:23 +0200975 }
brandjona0c6f812020-06-06 14:13:19 -0700976 if (starlarkEventHandler.hasErrors()) {
brandjond5d86772020-10-26 16:21:22 -0700977 throw BzlLoadFailedException.errors(label);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000978 }
Googler2234d382015-03-24 17:18:02 +0000979 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100980 }
981
adonovan1d93d262020-05-07 14:50:42 -0700982 // Precondition: thread has a valid transitiveDigest.
brandjona0c6f812020-06-06 14:13:19 -0700983 // TODO(adonovan): executeBzlFile would make a better public API than this function.
Googlera3421e22019-09-26 06:48:32 -0700984 public static void execAndExport(
adonovand1b1c4c2020-08-26 18:39:53 -0700985 Program prog, Label label, EventHandler handler, Module module, StarlarkThread thread)
Googlera3421e22019-09-26 06:48:32 -0700986 throws InterruptedException {
Dmitry Lomov950310f2017-03-01 17:45:12 +0000987
Googlerba868542019-10-09 07:26:27 -0700988 // Intercept execution after every assignment at top level
989 // and "export" any newly assigned exportable globals.
990 // TODO(adonovan): change the semantics; see b/65374671.
991 thread.setPostAssignHook(
992 (name, value) -> {
gregced281df72020-05-11 12:27:06 -0700993 if (value instanceof StarlarkExportable) {
994 StarlarkExportable exp = (StarlarkExportable) value;
Googlerba868542019-10-09 07:26:27 -0700995 if (!exp.isExported()) {
996 try {
brandjon7154be02020-05-22 09:47:32 -0700997 exp.export(label, name);
Googlerba868542019-10-09 07:26:27 -0700998 } catch (EvalException ex) {
adonovan3391e172020-08-05 14:21:14 -0700999 handler.handle(Event.error(null, ex.getMessageWithStack()));
Googlerba868542019-10-09 07:26:27 -07001000 }
1001 }
Dmitry Lomov950310f2017-03-01 17:45:12 +00001002 }
Googlerba868542019-10-09 07:26:27 -07001003 });
1004
1005 try {
adonovand1b1c4c2020-08-26 18:39:53 -07001006 Starlark.execFileProgram(prog, module, thread);
Googlerba868542019-10-09 07:26:27 -07001007 } catch (EvalException ex) {
adonovan3391e172020-08-05 14:21:14 -07001008 handler.handle(Event.error(null, ex.getMessageWithStack()));
Dmitry Lomov950310f2017-03-01 17:45:12 +00001009 }
1010 }
1011
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001012 @Override
1013 public String extractTag(SkyKey skyKey) {
1014 return null;
1015 }
1016
brandjon8d669292020-05-28 15:27:38 -07001017 /**
adonovan0a1f0f52020-09-16 11:11:04 -07001018 * A manager abstracting over the method for obtaining {@code BzlCompileValue}s. See comment in
brandjon8d669292020-05-28 15:27:38 -07001019 * {@link #create}.
1020 */
adonovan0a1f0f52020-09-16 11:11:04 -07001021 private interface ValueGetter {
nharmatadc1d9dc2020-04-18 16:53:28 -07001022 @Nullable
adonovan0a1f0f52020-09-16 11:11:04 -07001023 BzlCompileValue getBzlCompileValue(BzlCompileValue.Key key, Environment env)
1024 throws BzlCompileFunction.FailedIOException, InterruptedException;
nharmatadc1d9dc2020-04-18 16:53:28 -07001025
adonovan0a1f0f52020-09-16 11:11:04 -07001026 void doneWithBzlCompileValue(BzlCompileValue.Key key);
nharmatadc1d9dc2020-04-18 16:53:28 -07001027 }
1028
adonovan0a1f0f52020-09-16 11:11:04 -07001029 /** A manager that obtains compiled .bzl files from Skyframe calls. */
1030 private static class RegularSkyframeGetter implements ValueGetter {
1031 private static final RegularSkyframeGetter INSTANCE = new RegularSkyframeGetter();
nharmatadc1d9dc2020-04-18 16:53:28 -07001032
1033 @Nullable
1034 @Override
adonovan0a1f0f52020-09-16 11:11:04 -07001035 public BzlCompileValue getBzlCompileValue(BzlCompileValue.Key key, Environment env)
1036 throws BzlCompileFunction.FailedIOException, InterruptedException {
1037 return (BzlCompileValue) env.getValueOrThrow(key, BzlCompileFunction.FailedIOException.class);
nharmatadc1d9dc2020-04-18 16:53:28 -07001038 }
1039
1040 @Override
adonovan0a1f0f52020-09-16 11:11:04 -07001041 public void doneWithBzlCompileValue(BzlCompileValue.Key key) {}
nharmatadc1d9dc2020-04-18 16:53:28 -07001042 }
1043
brandjon8d669292020-05-28 15:27:38 -07001044 /**
adonovan0a1f0f52020-09-16 11:11:04 -07001045 * A manager that obtains compiled .bzls by inlining {@link BzlCompileFunction} (not to be
1046 * confused with inlining of {@code BzlLoadFunction}). Values are cached within the manager and
1047 * released explicitly by calling {@link #doneWithBzlCompileValue}.
brandjon8d669292020-05-28 15:27:38 -07001048 */
adonovan0a1f0f52020-09-16 11:11:04 -07001049 private static class InliningAndCachingGetter implements ValueGetter {
brandjondb4d1ea2020-08-14 11:31:08 -07001050 private final PackageFactory packageFactory;
janakr3af783a2020-09-08 14:12:14 -07001051 private final HashFunction hashFunction;
adonovan0a1f0f52020-09-16 11:11:04 -07001052 // We keep a cache of BzlCompileValues that have been computed but whose corresponding
1053 // BzlLoadValue has not yet completed. This avoids repeating the BzlCompileValue work in case
brandjon771a0292020-05-26 12:04:16 -07001054 // of Skyframe restarts. (If we weren't inlining, Skyframe would cache this for us.)
adonovan0a1f0f52020-09-16 11:11:04 -07001055 private final Cache<BzlCompileValue.Key, BzlCompileValue> bzlCompileCache;
nharmatadc1d9dc2020-04-18 16:53:28 -07001056
adonovan0a1f0f52020-09-16 11:11:04 -07001057 private InliningAndCachingGetter(
brandjondb4d1ea2020-08-14 11:31:08 -07001058 PackageFactory packageFactory,
janakr3af783a2020-09-08 14:12:14 -07001059 HashFunction hashFunction,
adonovan0a1f0f52020-09-16 11:11:04 -07001060 Cache<BzlCompileValue.Key, BzlCompileValue> bzlCompileCache) {
brandjondb4d1ea2020-08-14 11:31:08 -07001061 this.packageFactory = packageFactory;
janakr3af783a2020-09-08 14:12:14 -07001062 this.hashFunction = hashFunction;
adonovan0a1f0f52020-09-16 11:11:04 -07001063 this.bzlCompileCache = bzlCompileCache;
nharmatadc1d9dc2020-04-18 16:53:28 -07001064 }
1065
1066 @Nullable
1067 @Override
adonovan0a1f0f52020-09-16 11:11:04 -07001068 public BzlCompileValue getBzlCompileValue(BzlCompileValue.Key key, Environment env)
1069 throws BzlCompileFunction.FailedIOException, InterruptedException {
1070 BzlCompileValue value = bzlCompileCache.getIfPresent(key);
nharmatadc1d9dc2020-04-18 16:53:28 -07001071 if (value == null) {
adonovan0a1f0f52020-09-16 11:11:04 -07001072 value = BzlCompileFunction.computeInline(key, env, packageFactory, hashFunction);
nharmatadc1d9dc2020-04-18 16:53:28 -07001073 if (value != null) {
adonovan0a1f0f52020-09-16 11:11:04 -07001074 bzlCompileCache.put(key, value);
nharmatadc1d9dc2020-04-18 16:53:28 -07001075 }
1076 }
1077 return value;
1078 }
1079
1080 @Override
adonovan0a1f0f52020-09-16 11:11:04 -07001081 public void doneWithBzlCompileValue(BzlCompileValue.Key key) {
1082 bzlCompileCache.invalidate(key);
nharmatadc1d9dc2020-04-18 16:53:28 -07001083 }
1084 }
1085
brandjon8d669292020-05-28 15:27:38 -07001086 /**
1087 * Per-instance manager for {@link CachedBzlLoadData}, used when {@code BzlLoadFunction} calls are
1088 * inlined.
1089 */
1090 private static class CachedBzlLoadDataManager {
1091 private final int cacheSize;
1092 private Cache<BzlLoadValue.Key, CachedBzlLoadData> cache;
1093 private CachedBzlLoadDataBuilderFactory cachedDataBuilderFactory =
brandjon24912642020-05-27 11:28:45 -07001094 new CachedBzlLoadDataBuilderFactory();
nharmatadc1d9dc2020-04-18 16:53:28 -07001095
brandjon8d669292020-05-28 15:27:38 -07001096 private CachedBzlLoadDataManager(int cacheSize) {
1097 this.cacheSize = cacheSize;
nharmatadc1d9dc2020-04-18 16:53:28 -07001098 }
1099
brandjon24912642020-05-27 11:28:45 -07001100 private CachedBzlLoadData.Builder cachedDataBuilder() {
brandjon8d669292020-05-28 15:27:38 -07001101 return cachedDataBuilderFactory.newCachedBzlLoadDataBuilder();
brandjon24912642020-05-27 11:28:45 -07001102 }
1103
nharmatadc1d9dc2020-04-18 16:53:28 -07001104 private void reset() {
brandjon8d669292020-05-28 15:27:38 -07001105 if (cache != null) {
1106 logger.atInfo().log("Starlark inlining cache stats from earlier build: " + cache.stats());
nharmatadc1d9dc2020-04-18 16:53:28 -07001107 }
brandjon8d669292020-05-28 15:27:38 -07001108 cachedDataBuilderFactory = new CachedBzlLoadDataBuilderFactory();
nharmatadc1d9dc2020-04-18 16:53:28 -07001109 Preconditions.checkState(
brandjon8d669292020-05-28 15:27:38 -07001110 cacheSize >= 0, "Expected positive Starlark cache size if caching. %s", cacheSize);
1111 cache =
nharmatadc1d9dc2020-04-18 16:53:28 -07001112 CacheBuilder.newBuilder()
1113 .concurrencyLevel(BlazeInterners.concurrencyLevel())
brandjon8d669292020-05-28 15:27:38 -07001114 .maximumSize(cacheSize)
nharmatadc1d9dc2020-04-18 16:53:28 -07001115 .recordStats()
1116 .build();
1117 }
1118 }
1119
brandjonb9c81cd2020-08-24 11:00:58 -07001120 static final class BzlLoadFailedException extends Exception implements SaneAnalysisException {
1121 private final Transience transience;
mschaller859c9ac2020-09-25 16:09:19 -07001122 private final DetailedExitCode detailedExitCode;
brandjonb9c81cd2020-08-24 11:00:58 -07001123
mschaller859c9ac2020-09-25 16:09:19 -07001124 private BzlLoadFailedException(
1125 String errorMessage, DetailedExitCode detailedExitCode, Transience transience) {
brandjonb9c81cd2020-08-24 11:00:58 -07001126 super(errorMessage);
mschaller859c9ac2020-09-25 16:09:19 -07001127 this.transience = transience;
1128 this.detailedExitCode = detailedExitCode;
brandjonb9c81cd2020-08-24 11:00:58 -07001129 }
1130
mschaller859c9ac2020-09-25 16:09:19 -07001131 private BzlLoadFailedException(String errorMessage, DetailedExitCode detailedExitCode) {
1132 this(errorMessage, detailedExitCode, Transience.PERSISTENT);
1133 }
1134
1135 private BzlLoadFailedException(
1136 String errorMessage,
1137 DetailedExitCode detailedExitCode,
1138 Exception cause,
1139 Transience transience) {
brandjonb9c81cd2020-08-24 11:00:58 -07001140 super(errorMessage, cause);
1141 this.transience = transience;
mschaller859c9ac2020-09-25 16:09:19 -07001142 this.detailedExitCode = detailedExitCode;
1143 }
1144
1145 private BzlLoadFailedException(String errorMessage, Code code) {
1146 this(errorMessage, createDetailedExitCode(errorMessage, code), Transience.PERSISTENT);
1147 }
1148
1149 private BzlLoadFailedException(
1150 String errorMessage, Code code, Exception cause, Transience transience) {
1151 this(errorMessage, createDetailedExitCode(errorMessage, code), cause, transience);
brandjonb9c81cd2020-08-24 11:00:58 -07001152 }
1153
1154 Transience getTransience() {
1155 return transience;
1156 }
1157
mschaller859c9ac2020-09-25 16:09:19 -07001158 @Override
1159 public DetailedExitCode getDetailedExitCode() {
1160 return detailedExitCode;
1161 }
1162
1163 private static DetailedExitCode createDetailedExitCode(String message, Code code) {
1164 return DetailedExitCode.of(
1165 FailureDetail.newBuilder()
1166 .setMessage(message)
1167 .setStarlarkLoading(StarlarkLoading.newBuilder().setCode(code))
1168 .build());
1169 }
1170
brandjonb9c81cd2020-08-24 11:00:58 -07001171 // TODO(bazel-team): This exception should hold a Location of the requesting file's load
1172 // statement, and code that catches it should use the location in the Event they create.
1173 static BzlLoadFailedException whileLoadingDep(
1174 String requestingFile, BzlLoadFailedException cause) {
1175 // Don't chain exception cause, just incorporate the message with a prefix.
brandjond5d86772020-10-26 16:21:22 -07001176 // TODO(brandjon): Normalize the requestingFile part to use the same convention for printing
1177 // the bzl file as in errors(), starlarkErrors().
mschaller859c9ac2020-09-25 16:09:19 -07001178 return new BzlLoadFailedException(
1179 "in " + requestingFile + ": " + cause.getMessage(), cause.getDetailedExitCode());
brandjonb9c81cd2020-08-24 11:00:58 -07001180 }
1181
brandjond5d86772020-10-26 16:21:22 -07001182 static BzlLoadFailedException errors(Label label) {
1183 // TODO(brandjon): Merge with starlarkErrors(). Drop "Extension" terminology, but still
1184 // distinguish between static and dynamic (eval) errors in the message.
mschaller859c9ac2020-09-25 16:09:19 -07001185 return new BzlLoadFailedException(
brandjond5d86772020-10-26 16:21:22 -07001186 String.format(
1187 "Extension file '%s'%s has errors",
1188 label.toPathFragment(),
1189 StarlarkBuiltinsValue.isBuiltinsRepo(label.getRepository()) ? " (internal)" : ""),
1190 Code.EVAL_ERROR);
brandjonb9c81cd2020-08-24 11:00:58 -07001191 }
1192
mschaller859c9ac2020-09-25 16:09:19 -07001193 static BzlLoadFailedException errorFindingContainingPackage(
1194 PathFragment file, Exception cause) {
1195 String errorMessage =
brandjonb9c81cd2020-08-24 11:00:58 -07001196 String.format(
mschaller859c9ac2020-09-25 16:09:19 -07001197 "Encountered error while reading extension file '%s': %s", file, cause.getMessage());
1198 DetailedExitCode detailedExitCode =
1199 cause instanceof DetailedException
1200 ? ((DetailedException) cause).getDetailedExitCode()
1201 : createDetailedExitCode(errorMessage, Code.CONTAINING_PACKAGE_NOT_FOUND);
1202 return new BzlLoadFailedException(
1203 errorMessage, detailedExitCode, cause, Transience.PERSISTENT);
brandjonb9c81cd2020-08-24 11:00:58 -07001204 }
1205
1206 static BzlLoadFailedException errorReadingBzl(
adonovan0a1f0f52020-09-16 11:11:04 -07001207 PathFragment file, BzlCompileFunction.FailedIOException cause) {
mschaller859c9ac2020-09-25 16:09:19 -07001208 String errorMessage =
brandjonb9c81cd2020-08-24 11:00:58 -07001209 String.format(
mschaller859c9ac2020-09-25 16:09:19 -07001210 "Encountered error while reading extension file '%s': %s", file, cause.getMessage());
1211 return new BzlLoadFailedException(errorMessage, Code.IO_ERROR, cause, cause.getTransience());
brandjonb9c81cd2020-08-24 11:00:58 -07001212 }
1213
1214 static BzlLoadFailedException noBuildFile(Label file, @Nullable String reason) {
1215 if (reason != null) {
1216 return new BzlLoadFailedException(
mschaller859c9ac2020-09-25 16:09:19 -07001217 String.format("Unable to find package for %s: %s.", file, reason),
1218 Code.PACKAGE_NOT_FOUND);
brandjonb9c81cd2020-08-24 11:00:58 -07001219 }
1220 return new BzlLoadFailedException(
1221 String.format(
1222 "Every .bzl file must have a corresponding package, but '%s' does not have one."
1223 + " Please create a BUILD file in the same or any parent directory. Note that"
1224 + " this BUILD file does not need to do anything except exist.",
mschaller859c9ac2020-09-25 16:09:19 -07001225 file),
1226 Code.PACKAGE_NOT_FOUND);
brandjonb9c81cd2020-08-24 11:00:58 -07001227 }
1228
1229 static BzlLoadFailedException labelCrossesPackageBoundary(
1230 Label label, ContainingPackageLookupValue containingPackageLookupValue) {
1231 return new BzlLoadFailedException(
1232 ContainingPackageLookupValue.getErrorMessageForLabelCrossingPackageBoundary(
1233 // We don't actually know the proper Root to pass in here (since we don't e.g. know
1234 // the root of the bzl/BUILD file that is trying to load 'label'). Therefore we just
1235 // pass in the Root of the containing package in order to still get a useful error
1236 // message for the user.
1237 containingPackageLookupValue.getContainingPackageRoot(),
1238 label,
mschaller859c9ac2020-09-25 16:09:19 -07001239 containingPackageLookupValue),
1240 Code.LABEL_CROSSES_PACKAGE_BOUNDARY);
brandjonb9c81cd2020-08-24 11:00:58 -07001241 }
1242
brandjond5d86772020-10-26 16:21:22 -07001243 static BzlLoadFailedException starlarkErrors(Label label) {
mschaller859c9ac2020-09-25 16:09:19 -07001244 return new BzlLoadFailedException(
brandjond5d86772020-10-26 16:21:22 -07001245 String.format(
1246 "Extension '%s'%s has errors",
1247 label.toPathFragment(),
1248 StarlarkBuiltinsValue.isBuiltinsRepo(label.getRepository()) ? " (internal)" : ""),
1249 Code.PARSE_ERROR);
brandjonb9c81cd2020-08-24 11:00:58 -07001250 }
1251
1252 static BzlLoadFailedException builtinsFailed(Label file, BuiltinsFailedException cause) {
1253 return new BzlLoadFailedException(
1254 String.format(
1255 "Internal error while loading Starlark builtins for %s: %s",
1256 file, cause.getMessage()),
mschaller859c9ac2020-09-25 16:09:19 -07001257 Code.BUILTINS_ERROR,
brandjonb9c81cd2020-08-24 11:00:58 -07001258 cause,
1259 cause.getTransience());
1260 }
1261 }
1262
brandjon771a0292020-05-26 12:04:16 -07001263 private static final class BzlLoadFunctionException extends SkyFunctionException {
1264 private BzlLoadFunctionException(BzlLoadFailedException cause) {
nharmatadc1d9dc2020-04-18 16:53:28 -07001265 super(cause, cause.transience);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001266 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01001267 }
1268}