Damien Martin-Guillerez | f88f4d8 | 2015-09-25 13:56:55 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. All rights reserved. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | package com.google.devtools.build.lib.skyframe; |
| 15 | |
tomlu | a155b53 | 2017-11-08 20:12:47 +0100 | [diff] [blame] | 16 | import com.google.common.base.Preconditions; |
Janak Ramakrishnan | df0531f | 2015-09-23 17:30:04 +0000 | [diff] [blame] | 17 | import com.google.common.base.Predicates; |
shreyax | 755a0a1 | 2018-03-19 20:47:37 -0700 | [diff] [blame] | 18 | import com.google.common.cache.Cache; |
| 19 | import com.google.common.cache.CacheBuilder; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 20 | import com.google.common.collect.ImmutableList; |
John Field | a97e17f | 2015-11-13 02:19:52 +0000 | [diff] [blame] | 21 | import com.google.common.collect.ImmutableMap; |
John Field | a97e17f | 2015-11-13 02:19:52 +0000 | [diff] [blame] | 22 | import com.google.common.collect.Lists; |
Janak Ramakrishnan | dfd3497 | 2015-09-22 02:53:05 +0000 | [diff] [blame] | 23 | import com.google.common.collect.Maps; |
janakr | c3bcb98 | 2020-04-14 06:50:08 -0700 | [diff] [blame] | 24 | import com.google.common.flogger.GoogleLogger; |
janakr | 3af783a | 2020-09-08 14:12:14 -0700 | [diff] [blame] | 25 | import com.google.common.hash.HashFunction; |
shahan | 602cc85 | 2018-06-06 20:09:57 -0700 | [diff] [blame] | 26 | import com.google.devtools.build.lib.actions.InconsistentFilesystemException; |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 27 | import com.google.devtools.build.lib.analysis.BlazeDirectories; |
Lukacs Berki | 6e91eb9 | 2015-09-21 09:12:37 +0000 | [diff] [blame] | 28 | import com.google.devtools.build.lib.cmdline.Label; |
Googler | b2e1fe3 | 2019-09-20 14:00:39 -0700 | [diff] [blame] | 29 | import com.google.devtools.build.lib.cmdline.LabelConstants; |
| 30 | import com.google.devtools.build.lib.cmdline.LabelSyntaxException; |
Kristina Chodorow | 73fa203 | 2015-08-28 17:57:46 +0000 | [diff] [blame] | 31 | import com.google.devtools.build.lib.cmdline.PackageIdentifier; |
dannark | 4e42c32 | 2018-11-08 19:32:04 -0800 | [diff] [blame] | 32 | import com.google.devtools.build.lib.cmdline.RepositoryName; |
shreyax | 755a0a1 | 2018-03-19 20:47:37 -0700 | [diff] [blame] | 33 | import com.google.devtools.build.lib.concurrent.BlazeInterners; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 34 | import com.google.devtools.build.lib.events.Event; |
Dmitry Lomov | 950310f | 2017-03-01 17:45:12 +0000 | [diff] [blame] | 35 | import com.google.devtools.build.lib.events.EventHandler; |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 36 | import com.google.devtools.build.lib.events.ExtendedEventHandler; |
Klaus Aehlig | da0a701 | 2017-06-14 13:40:23 +0200 | [diff] [blame] | 37 | import com.google.devtools.build.lib.events.ExtendedEventHandler.Postable; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 38 | import com.google.devtools.build.lib.events.StoredEventHandler; |
ajurkowski | 9f2cab5 | 2020-05-12 12:00:24 -0700 | [diff] [blame] | 39 | import com.google.devtools.build.lib.packages.BazelModuleContext; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 40 | import com.google.devtools.build.lib.packages.BuildFileNotFoundException; |
| 41 | import com.google.devtools.build.lib.packages.PackageFactory; |
gregce | d281df7 | 2020-05-11 12:27:06 -0700 | [diff] [blame] | 42 | import com.google.devtools.build.lib.packages.StarlarkExportable; |
janakr | 15e15c2 | 2019-01-30 11:24:49 -0800 | [diff] [blame] | 43 | import com.google.devtools.build.lib.packages.WorkspaceFileValue; |
adonovan | 028e1ad | 2020-09-07 07:09:59 -0700 | [diff] [blame] | 44 | import com.google.devtools.build.lib.packages.semantics.BuildLanguageOptions; |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 45 | import com.google.devtools.build.lib.server.FailureDetails.FailureDetail; |
| 46 | import com.google.devtools.build.lib.server.FailureDetails.StarlarkLoading; |
| 47 | import com.google.devtools.build.lib.server.FailureDetails.StarlarkLoading.Code; |
brandjon | 844fef5 | 2020-06-08 10:30:36 -0700 | [diff] [blame] | 48 | import com.google.devtools.build.lib.skyframe.StarlarkBuiltinsFunction.BuiltinsFailedException; |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 49 | import com.google.devtools.build.lib.util.DetailedExitCode; |
adonovan | 1d93d26 | 2020-05-07 14:50:42 -0700 | [diff] [blame] | 50 | import com.google.devtools.build.lib.util.Fingerprint; |
adonovan | 699a90a | 2020-05-20 19:48:53 -0700 | [diff] [blame] | 51 | import com.google.devtools.build.lib.util.Pair; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 52 | import com.google.devtools.build.lib.vfs.PathFragment; |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 53 | import com.google.devtools.build.lib.vfs.Root; |
shreyax | 755a0a1 | 2018-03-19 20:47:37 -0700 | [diff] [blame] | 54 | import com.google.devtools.build.skyframe.RecordingSkyFunctionEnvironment; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 55 | import com.google.devtools.build.skyframe.SkyFunction; |
| 56 | import com.google.devtools.build.skyframe.SkyFunctionException; |
| 57 | import com.google.devtools.build.skyframe.SkyFunctionException.Transience; |
| 58 | import com.google.devtools.build.skyframe.SkyKey; |
| 59 | import com.google.devtools.build.skyframe.SkyValue; |
laurentlb | dd612a8 | 2018-10-16 19:42:48 -0700 | [diff] [blame] | 60 | import com.google.devtools.build.skyframe.ValueOrException; |
brandjon | 1d3f309 | 2020-06-08 10:58:25 -0700 | [diff] [blame] | 61 | import java.util.HashMap; |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 62 | import java.util.HashSet; |
shreyax | 39ffd97 | 2019-02-07 15:41:31 -0800 | [diff] [blame] | 63 | import java.util.LinkedHashSet; |
John Field | a97e17f | 2015-11-13 02:19:52 +0000 | [diff] [blame] | 64 | import java.util.List; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 65 | import java.util.Map; |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 66 | import java.util.function.Consumer; |
Janak Ramakrishnan | df0531f | 2015-09-23 17:30:04 +0000 | [diff] [blame] | 67 | import javax.annotation.Nullable; |
adonovan | 450c7ad | 2020-09-14 13:00:21 -0700 | [diff] [blame] | 68 | import net.starlark.java.eval.EvalException; |
| 69 | import net.starlark.java.eval.Module; |
| 70 | import net.starlark.java.eval.Mutability; |
| 71 | import net.starlark.java.eval.Starlark; |
| 72 | import net.starlark.java.eval.StarlarkSemantics; |
| 73 | import net.starlark.java.eval.StarlarkThread; |
| 74 | import net.starlark.java.syntax.LoadStatement; |
| 75 | import net.starlark.java.syntax.Program; |
| 76 | import net.starlark.java.syntax.StarlarkFile; |
| 77 | import net.starlark.java.syntax.Statement; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 78 | |
| 79 | /** |
adonovan | 1d93d26 | 2020-05-07 14:50:42 -0700 | [diff] [blame] | 80 | * A Skyframe function to look up and load a single .bzl module. |
John Field | a97e17f | 2015-11-13 02:19:52 +0000 | [diff] [blame] | 81 | * |
adonovan | 1d93d26 | 2020-05-07 14:50:42 -0700 | [diff] [blame] | 82 | * <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 |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 84 | * is successful, returns a {@link BzlLoadValue} that encapsulates the loaded {@link Module} and its |
adonovan | 39e42d1 | 2020-07-09 09:16:58 -0700 | [diff] [blame] | 85 | * transitive digest information. If loading is unsuccessful, throws a {@link |
| 86 | * BzlLoadFunctionException} that encapsulates the cause of the failure. |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 87 | * |
brandjon | b4ebbfc | 2020-08-13 15:50:23 -0700 | [diff] [blame] | 88 | * <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 |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 93 | * BzlCompileFunction} |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 94 | */ |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 95 | public class BzlLoadFunction implements SkyFunction { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 96 | |
brandjon | db4d1ea | 2020-08-14 11:31:08 -0700 | [diff] [blame] | 97 | // Used for: 1) obtaining a RuleClassProvider to create the BazelStarlarkContext for Starlark |
| 98 | // evaluation; 2) providing predeclared environments to other Skyfunctions |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 99 | // (StarlarkBuiltinsFunction, BzlCompileFunction) when they are inlined and called via a static |
brandjon | db4d1ea | 2020-08-14 11:31:08 -0700 | [diff] [blame] | 100 | // computeInline() entry point. |
| 101 | private final PackageFactory packageFactory; |
brandjon | 2863252 | 2020-06-08 13:14:07 -0700 | [diff] [blame] | 102 | |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 103 | // Used for determining paths to builtins bzls. |
| 104 | private final BlazeDirectories directories; |
| 105 | |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 106 | // Handles retrieving BzlCompileValues, either by calling Skyframe or by inlining |
| 107 | // BzlCompileFunction; the latter is not to be confused with inlining of BzlLoadFunction. See |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 108 | // comment in create() for rationale. |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 109 | private final ValueGetter getter; |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 110 | |
| 111 | // Handles inlining of BzlLoadFunction calls. |
| 112 | @Nullable private final CachedBzlLoadDataManager cachedBzlLoadDataManager; |
shreyax | 755a0a1 | 2018-03-19 20:47:37 -0700 | [diff] [blame] | 113 | |
janakr | c3bcb98 | 2020-04-14 06:50:08 -0700 | [diff] [blame] | 114 | private static final GoogleLogger logger = GoogleLogger.forEnclosingClass(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 115 | |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 116 | private BzlLoadFunction( |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 117 | PackageFactory packageFactory, |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 118 | BlazeDirectories directories, |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 119 | ValueGetter getter, |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 120 | @Nullable CachedBzlLoadDataManager cachedBzlLoadDataManager) { |
brandjon | 2863252 | 2020-06-08 13:14:07 -0700 | [diff] [blame] | 121 | this.packageFactory = packageFactory; |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 122 | this.directories = directories; |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 123 | this.getter = getter; |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 124 | this.cachedBzlLoadDataManager = cachedBzlLoadDataManager; |
shreyax | 3126205 | 2019-08-13 13:40:06 -0700 | [diff] [blame] | 125 | } |
| 126 | |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 127 | public static BzlLoadFunction create( |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 128 | PackageFactory packageFactory, |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 129 | BlazeDirectories directories, |
janakr | 3af783a | 2020-09-08 14:12:14 -0700 | [diff] [blame] | 130 | HashFunction hashFunction, |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 131 | Cache<BzlCompileValue.Key, BzlCompileValue> bzlCompileCache) { |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 132 | return new BzlLoadFunction( |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 133 | packageFactory, |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 134 | directories, |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 135 | // When we are not inlining BzlLoadValue nodes, there is no need to have separate |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 136 | // BzlCompileValue nodes for bzl files. Instead we inline BzlCompileFunction for a |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 137 | // strict memory win, at a small code complexity cost. |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 138 | // |
| 139 | // Detailed explanation: |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 140 | // (1) The BzlCompileValue node for a bzl file is used only for the computation of |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 141 | // that file's BzlLoadValue node. So there's no concern about duplicate work that would |
| 142 | // otherwise get deduped by Skyframe. |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 143 | // (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 |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 145 | // 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 |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 147 | // 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 |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 151 | // restart, but we can also achieve that result ourselves with a cache that persists between |
| 152 | // Skyframe restarts. |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 153 | // |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 154 | // Therefore, BzlCompileValue nodes are wasteful from two perspectives: |
| 155 | // (a) BzlCompileValue contains syntax trees, and that business object is really |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 156 | // 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. |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 159 | new InliningAndCachingGetter(packageFactory, hashFunction, bzlCompileCache), |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 160 | /*cachedBzlLoadDataManager=*/ null); |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 161 | } |
| 162 | |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 163 | public static BzlLoadFunction createForInlining( |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 164 | PackageFactory packageFactory, BlazeDirectories directories, int bzlLoadValueCacheSize) { |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 165 | return new BzlLoadFunction( |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 166 | packageFactory, |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 167 | directories, |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 168 | // When we are inlining BzlLoadValue nodes, then we want to have explicit BzlCompileValue |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 169 | // 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. |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 173 | RegularSkyframeGetter.INSTANCE, |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 174 | new CachedBzlLoadDataManager(bzlLoadValueCacheSize)); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 175 | } |
| 176 | |
| 177 | @Override |
shreyax | 755a0a1 | 2018-03-19 20:47:37 -0700 | [diff] [blame] | 178 | @Nullable |
| 179 | public SkyValue compute(SkyKey skyKey, Environment env) |
| 180 | throws SkyFunctionException, InterruptedException { |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 181 | BzlLoadValue.Key key = (BzlLoadValue.Key) skyKey.argument(); |
John Field | a97e17f | 2015-11-13 02:19:52 +0000 | [diff] [blame] | 182 | try { |
brandjon | 7154be0 | 2020-05-22 09:47:32 -0700 | [diff] [blame] | 183 | return computeInternal(key, env, /*inliningState=*/ null); |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 184 | } catch (BzlLoadFailedException e) { |
| 185 | throw new BzlLoadFunctionException(e); |
John Field | a97e17f | 2015-11-13 02:19:52 +0000 | [diff] [blame] | 186 | } |
Janak Ramakrishnan | df0531f | 2015-09-23 17:30:04 +0000 | [diff] [blame] | 187 | } |
| 188 | |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 189 | /** |
| 190 | * Entry point for computing "inline", without any direct or indirect Skyframe calls back into |
| 191 | * {@link BzlLoadFunction}. (Other Skyframe calls are permitted.) |
| 192 | * |
nharmata | 60d0460 | 2020-08-11 12:28:57 -0700 | [diff] [blame] | 193 | * <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 |
brandjon | b4ebbfc | 2020-08-13 15:50:23 -0700 | [diff] [blame] | 201 | * interrupted, then this function ensures that the evaluation graph and any error reported |
| 202 | * are deterministic. |
nharmata | 60d0460 | 2020-08-11 12:28:57 -0700 | [diff] [blame] | 203 | * </ul> |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 204 | * |
| 205 | * <p>Under bzl inlining, there is some calling context that wants to obtain a set of {@link |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 206 | * 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 |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 210 | * Skyframe cache. Unlike the regular Skyframe cache, this cache stores only successes. |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 211 | * |
| 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 | * |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 221 | * <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}. |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 229 | * |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 230 | * <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. |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 237 | * |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 238 | * <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 |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 248 | */ |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 249 | // TODO(brandjon): Pick one of the nouns "load" and "bzl" and use that term consistently. |
shreyax | 755a0a1 | 2018-03-19 20:47:37 -0700 | [diff] [blame] | 250 | @Nullable |
brandjon | 1d3f309 | 2020-06-08 10:58:25 -0700 | [diff] [blame] | 251 | BzlLoadValue computeInline(BzlLoadValue.Key key, Environment env, InliningState inliningState) |
brandjon | 4eed64e | 2020-08-24 10:13:53 -0700 | [diff] [blame] | 252 | throws BzlLoadFailedException, InterruptedException { |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 253 | // Note to refactorors: No Skyframe calls may be made before the RecordingSkyFunctionEnvironment |
| 254 | // is set up below in computeInlineForCacheMiss. |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 255 | Preconditions.checkNotNull(cachedBzlLoadDataManager); |
brandjon | 1d3f309 | 2020-06-08 10:58:25 -0700 | [diff] [blame] | 256 | CachedBzlLoadData cachedData = computeInlineCachedData(key, env, inliningState); |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 257 | return cachedData != null ? cachedData.getValue() : null; |
Janak Ramakrishnan | df0531f | 2015-09-23 17:30:04 +0000 | [diff] [blame] | 258 | } |
| 259 | |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 260 | /** |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 261 | * 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. |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 264 | * |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 265 | * <p>Skyframe calls made underneath this function will be logged in the resulting {@code |
brandjon | 1d3f309 | 2020-06-08 10:58:25 -0700 | [diff] [blame] | 266 | * 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. |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 269 | * |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 270 | * @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 |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 272 | */ |
shreyax | 755a0a1 | 2018-03-19 20:47:37 -0700 | [diff] [blame] | 273 | @Nullable |
brandjon | 1d3f309 | 2020-06-08 10:58:25 -0700 | [diff] [blame] | 274 | private CachedBzlLoadData computeInlineCachedData( |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 275 | BzlLoadValue.Key key, Environment env, InliningState inliningState) |
brandjon | 4eed64e | 2020-08-24 10:13:53 -0700 | [diff] [blame] | 276 | throws BzlLoadFailedException, InterruptedException { |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 277 | // Note to refactorors: No Skyframe calls may be made before the RecordingSkyFunctionEnvironment |
| 278 | // is set up below in computeInlineForCacheMiss. |
| 279 | |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 280 | // 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); |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 283 | if (cachedData == null) { |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 284 | cachedData = cachedBzlLoadDataManager.cache.getIfPresent(key); |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 285 | if (cachedData != null) { |
| 286 | // Found a cache hit from another thread's computation; register the recorded deps as if our |
brandjon | c782bbc | 2020-08-05 13:47:13 -0700 | [diff] [blame] | 287 | // thread required them for the current key. Incorporate into successfulLoads any transitive |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 288 | // cache hits it does not already contain. |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 289 | cachedData.traverse(env::registerDependencies, inliningState.successfulLoads); |
shreyax | 3126205 | 2019-08-13 13:40:06 -0700 | [diff] [blame] | 290 | } |
| 291 | } |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 292 | |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 293 | // 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. |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 300 | if (cachedData == null) { |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 301 | 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 | } |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 311 | } |
| 312 | } |
| 313 | |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 314 | // On success (from cache hit or from scratch), notify the parent CachedBzlLoadData of its new |
| 315 | // child. |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 316 | if (cachedData != null) { |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 317 | inliningState.childCachedDataHandler.accept(cachedData); |
shreyax | 755a0a1 | 2018-03-19 20:47:37 -0700 | [diff] [blame] | 318 | } |
| 319 | |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 320 | return cachedData; |
| 321 | } |
shreyax | c163c91 | 2019-02-08 12:45:25 -0800 | [diff] [blame] | 322 | |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 323 | @Nullable |
| 324 | private CachedBzlLoadData computeInlineForCacheMiss( |
| 325 | BzlLoadValue.Key key, Environment env, InliningState inliningState) |
brandjon | 4eed64e | 2020-08-24 10:13:53 -0700 | [diff] [blame] | 326 | throws BzlLoadFailedException, InterruptedException { |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 327 | // 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 |
brandjon | 1d3f309 | 2020-06-08 10:58:25 -0700 | [diff] [blame] | 330 | // back into computeInlineCachedData. |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 331 | CachedBzlLoadData.Builder cachedDataBuilder = cachedBzlLoadDataManager.cachedDataBuilder(); |
shreyax | 755a0a1 | 2018-03-19 20:47:37 -0700 | [diff] [blame] | 332 | 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, |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 339 | cachedDataBuilder::addDep, |
| 340 | cachedDataBuilder::addDeps, |
| 341 | cachedDataBuilder::noteException); |
shreyax | 755a0a1 | 2018-03-19 20:47:37 -0700 | [diff] [blame] | 342 | |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 343 | 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); |
shreyax | 755a0a1 | 2018-03-19 20:47:37 -0700 | [diff] [blame] | 354 | } |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 355 | if (value == null) { |
| 356 | return null; |
| 357 | } |
| 358 | |
| 359 | cachedDataBuilder.setValue(value); |
| 360 | cachedDataBuilder.setKey(key); |
| 361 | return cachedDataBuilder.build(); |
Michajlo Matijkiw | 10c363d | 2015-10-09 22:11:34 +0000 | [diff] [blame] | 362 | } |
| 363 | |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 364 | public void resetInliningCache() { |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 365 | cachedBzlLoadDataManager.reset(); |
shreyax | 755a0a1 | 2018-03-19 20:47:37 -0700 | [diff] [blame] | 366 | } |
| 367 | |
brandjon | 1af65cf | 2020-06-06 18:17:53 -0700 | [diff] [blame] | 368 | /** |
brandjon | b4ebbfc | 2020-08-13 15:50:23 -0700 | [diff] [blame] | 369 | * An opaque object that holds state for the bzl inlining computation initiated by {@link |
brandjon | 1d3f309 | 2020-06-08 10:58:25 -0700 | [diff] [blame] | 370 | * #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 |
brandjon | 5a33c63 | 2020-10-23 22:09:26 -0700 | [diff] [blame] | 380 | * to ensure that 1) the .bzls that get loaded from the {@code @_builtins} pseudo-repository are |
brandjon | 1d3f309 | 2020-06-08 10:58:25 -0700 | [diff] [blame] | 381 | * properly recorded as dependencies of all .bzl files that use builtins injection, and 2) the |
brandjon | 5a33c63 | 2020-10-23 22:09:26 -0700 | [diff] [blame] | 382 | * builtins .bzls are not reevaluated. |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 383 | */ |
brandjon | 1d3f309 | 2020-06-08 10:58:25 -0700 | [diff] [blame] | 384 | static class InliningState { |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 385 | /** |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 386 | * 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}. |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 393 | */ |
| 394 | // Keyed on the SkyKey, not the label, since label could theoretically be ambiguous, even though |
brandjon | 5a33c63 | 2020-10-23 22:09:26 -0700 | [diff] [blame] | 395 | // in practice keys from BUILD / WORKSPACE / builtins don't call each other. (Not sure if |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 396 | // WORKSPACE chunking can cause duplicate labels to appear, but we're robust regardless.) |
| 397 | private final LinkedHashSet<BzlLoadValue.Key> loadStack; |
| 398 | |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 399 | /** |
| 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 | |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 418 | /** Called when a transitive {@code CachedBzlLoadData} is produced. */ |
| 419 | private final Consumer<CachedBzlLoadData> childCachedDataHandler; |
| 420 | |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 421 | private InliningState( |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 422 | LinkedHashSet<BzlLoadValue.Key> loadStack, |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 423 | Map<BzlLoadValue.Key, CachedBzlLoadData> successfulLoads, |
| 424 | HashSet<BzlLoadValue.Key> unsuccessfulLoads, |
| 425 | Consumer<CachedBzlLoadData> childCachedDataHandler) { |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 426 | this.loadStack = loadStack; |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 427 | this.successfulLoads = successfulLoads; |
| 428 | this.unsuccessfulLoads = unsuccessfulLoads; |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 429 | this.childCachedDataHandler = childCachedDataHandler; |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 430 | } |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 431 | |
brandjon | 1d3f309 | 2020-06-08 10:58:25 -0700 | [diff] [blame] | 432 | /** |
| 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() { |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 437 | return new InliningState( |
brandjon | 1d3f309 | 2020-06-08 10:58:25 -0700 | [diff] [blame] | 438 | /*loadStack=*/ new LinkedHashSet<>(), |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 439 | /*successfulLoads=*/ new HashMap<>(), |
| 440 | /*unsuccessfulLoads=*/ new HashSet<>(), |
| 441 | // No parent value to mutate |
| 442 | /*childCachedDataHandler=*/ x -> {}); |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 443 | } |
| 444 | |
brandjon | 1d3f309 | 2020-06-08 10:58:25 -0700 | [diff] [blame] | 445 | private InliningState createChildState(Consumer<CachedBzlLoadData> childCachedDataHandler) { |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 446 | return new InliningState( |
| 447 | loadStack, successfulLoads, unsuccessfulLoads, childCachedDataHandler); |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 448 | } |
| 449 | |
| 450 | /** Records entry to a {@code load()}, throwing an exception if a cycle is detected. */ |
brandjon | 1d3f309 | 2020-06-08 10:58:25 -0700 | [diff] [blame] | 451 | private void beginLoad(BzlLoadValue.Key key) throws BzlLoadFailedException { |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 452 | if (!loadStack.add(key)) { |
| 453 | ImmutableList<BzlLoadValue.Key> cycle = |
| 454 | CycleUtils.splitIntoPathAndChain(Predicates.equalTo(key), loadStack).second; |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 455 | throw new BzlLoadFailedException("Starlark load cycle: " + cycle, Code.CYCLE); |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 456 | } |
| 457 | } |
| 458 | |
| 459 | /** Records exit from a {@code load()}. */ |
brandjon | 1d3f309 | 2020-06-08 10:58:25 -0700 | [diff] [blame] | 460 | private void finishLoad(BzlLoadValue.Key key) throws BzlLoadFailedException { |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 461 | Preconditions.checkState(loadStack.remove(key), key); |
| 462 | } |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 463 | } |
| 464 | |
brandjon | b4ebbfc | 2020-08-13 15:50:23 -0700 | [diff] [blame] | 465 | /** |
| 466 | * Entry point for compute logic that's common to both (bzl) inlining and non-inlining code paths. |
| 467 | */ |
shreyax | 755a0a1 | 2018-03-19 20:47:37 -0700 | [diff] [blame] | 468 | // 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 |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 472 | private BzlLoadValue computeInternal( |
| 473 | BzlLoadValue.Key key, Environment env, @Nullable InliningState inliningState) |
brandjon | 4eed64e | 2020-08-24 10:13:53 -0700 | [diff] [blame] | 474 | throws BzlLoadFailedException, InterruptedException { |
brandjon | bc884e1 | 2020-05-22 10:24:37 -0700 | [diff] [blame] | 475 | Label label = key.getLabel(); |
brandjon | 7154be0 | 2020-05-22 09:47:32 -0700 | [diff] [blame] | 476 | PathFragment filePath = label.toPathFragment(); |
John Field | 110b065 | 2015-11-13 21:56:42 +0000 | [diff] [blame] | 477 | |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 478 | StarlarkSemantics starlarkSemantics = PrecomputedValue.STARLARK_SEMANTICS.get(env); |
| 479 | if (starlarkSemantics == null) { |
| 480 | return null; |
| 481 | } |
| 482 | |
| 483 | BzlCompileValue.Key compileKey = validatePackageAndGetCompileKey(key, env, starlarkSemantics); |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 484 | if (compileKey == null) { |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 485 | return null; |
| 486 | } |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 487 | BzlCompileValue compileValue; |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 488 | try { |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 489 | compileValue = getter.getBzlCompileValue(compileKey, env); |
| 490 | } catch (BzlCompileFunction.FailedIOException e) { |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 491 | throw BzlLoadFailedException.errorReadingBzl(filePath, e); |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 492 | } |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 493 | if (compileValue == null) { |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 494 | return null; |
| 495 | } |
| 496 | |
| 497 | BzlLoadValue result = null; |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 498 | // Release the compiled bzl iff the value gets completely evaluated (to either error or non-null |
| 499 | // result). |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 500 | boolean completed = true; |
| 501 | try { |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 502 | result = |
| 503 | computeInternalWithCompiledBzl(key, compileValue, starlarkSemantics, env, inliningState); |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 504 | completed = result != null; |
| 505 | } finally { |
| 506 | if (completed) { // only false on unexceptional null result |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 507 | getter.doneWithBzlCompileValue(compileKey); |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 508 | } |
| 509 | } |
| 510 | return result; |
| 511 | } |
| 512 | |
| 513 | /** |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 514 | * 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. |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 517 | * |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 518 | * <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. |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 529 | */ |
| 530 | @Nullable |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 531 | private BzlCompileValue.Key validatePackageAndGetCompileKey( |
| 532 | BzlLoadValue.Key key, Environment env, StarlarkSemantics starlarkSemantics) |
| 533 | throws BzlLoadFailedException, InterruptedException { |
brandjon | 3f0917a | 2020-10-26 18:01:50 -0700 | [diff] [blame] | 534 | Label label = key.getLabel(); |
| 535 | |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 536 | // Bypass package lookup entirely if builtins. |
| 537 | if (key.isBuiltins()) { |
brandjon | 3f0917a | 2020-10-26 18:01:50 -0700 | [diff] [blame] | 538 | if (!label.getPackageName().isEmpty()) { |
| 539 | throw BzlLoadFailedException.noBuildFile(label, "@_builtins cannot have subpackages"); |
| 540 | } |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 541 | return key.getCompileKey(getBuiltinsRoot(starlarkSemantics)); |
| 542 | } |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 543 | |
| 544 | // Do package lookup. |
| 545 | PathFragment dir = Label.getContainingDirectory(label); |
Googler | 0617f2c | 2020-10-22 08:43:54 -0700 | [diff] [blame] | 546 | PackageIdentifier dirId = PackageIdentifier.create(label.getRepository(), dir); |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 547 | ContainingPackageLookupValue packageLookup; |
| 548 | try { |
| 549 | packageLookup = |
| 550 | (ContainingPackageLookupValue) |
| 551 | env.getValueOrThrow( |
| 552 | ContainingPackageLookupValue.key(dirId), |
| 553 | BuildFileNotFoundException.class, |
| 554 | InconsistentFilesystemException.class); |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 555 | } catch (BuildFileNotFoundException | InconsistentFilesystemException e) { |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 556 | throw BzlLoadFailedException.errorFindingContainingPackage(label.toPathFragment(), e); |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 557 | } |
| 558 | if (packageLookup == null) { |
| 559 | return null; |
| 560 | } |
| 561 | |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 562 | // Resolve to compile key or error. |
| 563 | BzlCompileValue.Key compileKey; |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 564 | 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. |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 569 | compileKey = BzlCompileValue.EMPTY_PRELUDE_KEY; |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 570 | } else { |
| 571 | if (packageOk) { |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 572 | compileKey = key.getCompileKey(packageLookup.getContainingPackageRoot()); |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 573 | } else { |
| 574 | if (!packageLookup.hasContainingPackage()) { |
| 575 | throw BzlLoadFailedException.noBuildFile( |
| 576 | label, packageLookup.getReasonForNoContainingPackage()); |
| 577 | } else { |
| 578 | throw BzlLoadFailedException.labelCrossesPackageBoundary(label, packageLookup); |
| 579 | } |
| 580 | } |
| 581 | } |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 582 | return compileKey; |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 583 | } |
| 584 | |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 585 | 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 | |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 610 | /** |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 611 | * Compute logic for once the compiled .bzl has been fetched and confirmed to exist (though it may |
| 612 | * have Starlark errors). |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 613 | */ |
| 614 | @Nullable |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 615 | private BzlLoadValue computeInternalWithCompiledBzl( |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 616 | BzlLoadValue.Key key, |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 617 | BzlCompileValue compileValue, |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 618 | StarlarkSemantics starlarkSemantics, |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 619 | Environment env, |
| 620 | @Nullable InliningState inliningState) |
brandjon | 4eed64e | 2020-08-24 10:13:53 -0700 | [diff] [blame] | 621 | throws BzlLoadFailedException, InterruptedException { |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 622 | Label label = key.getLabel(); |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 623 | |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 624 | // Ensure the .bzl exists and passes static checks (parsing, resolving). |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 625 | // (A missing prelude file still returns a valid but empty BzlCompileValue.) |
| 626 | if (!compileValue.lookupSuccessful()) { |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 627 | throw new BzlLoadFailedException(compileValue.getError(), Code.COMPILE_ERROR); |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 628 | } |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 629 | StarlarkFile file = compileValue.getAST(); |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 630 | if (!file.ok()) { |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 631 | throw BzlLoadFailedException.starlarkErrors(label); |
brandjon | 4d15f2b | 2020-08-12 09:15:52 -0700 | [diff] [blame] | 632 | } |
| 633 | |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 634 | // Determine dependency BzlLoadValue keys for the load statements in this bzl. Labels are |
| 635 | // resolved relative to the current repo mapping. |
brandjon | 7154be0 | 2020-05-22 09:47:32 -0700 | [diff] [blame] | 636 | ImmutableMap<RepositoryName, RepositoryName> repoMapping = getRepositoryMapping(key, env); |
Googler | b2e1fe3 | 2019-09-20 14:00:39 -0700 | [diff] [blame] | 637 | if (repoMapping == null) { |
dannark | 4e42c32 | 2018-11-08 19:32:04 -0800 | [diff] [blame] | 638 | return null; |
| 639 | } |
adonovan | c0e8690 | 2020-11-19 15:50:29 -0800 | [diff] [blame^] | 640 | ImmutableList<Pair<String, Label>> loadLabels = |
brandjon | 7154be0 | 2020-05-22 09:47:32 -0700 | [diff] [blame] | 641 | getLoadLabels(env.getListener(), file, label.getPackageIdentifier(), repoMapping); |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 642 | if (loadLabels == null) { |
Googler | b2e1fe3 | 2019-09-20 14:00:39 -0700 | [diff] [blame] | 643 | // malformed load statements |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 644 | throw BzlLoadFailedException.starlarkErrors(label); |
Googler | b2e1fe3 | 2019-09-20 14:00:39 -0700 | [diff] [blame] | 645 | } |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 646 | List<BzlLoadValue.Key> loadKeys = Lists.newArrayListWithExpectedSize(loadLabels.size()); |
| 647 | for (Pair<String, Label> entry : loadLabels) { |
| 648 | loadKeys.add(key.getKeyForLoad(entry.second)); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 649 | } |
adonovan | 699a90a | 2020-05-20 19:48:53 -0700 | [diff] [blame] | 650 | |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 651 | // Evaluate the dependency bzls. When not using bzl inlining, this is done in parallel for all |
| 652 | // loads. |
brandjon | 844fef5 | 2020-06-08 10:30:36 -0700 | [diff] [blame] | 653 | // 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 |
brandjon | 4eed64e | 2020-08-24 10:13:53 -0700 | [diff] [blame] | 655 | // file.getLoadStatements().get(...).getStartLocation(). |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 656 | List<BzlLoadValue> loadValues = |
adonovan | 699a90a | 2020-05-20 19:48:53 -0700 | [diff] [blame] | 657 | inliningState == null |
brandjon | 844fef5 | 2020-06-08 10:30:36 -0700 | [diff] [blame] | 658 | ? computeBzlLoadsWithSkyframe(env, loadKeys, file) |
| 659 | : computeBzlLoadsWithInlining(env, loadKeys, file, inliningState); |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 660 | if (loadValues == null) { |
adonovan | 699a90a | 2020-05-20 19:48:53 -0700 | [diff] [blame] | 661 | return null; // Skyframe deps unavailable |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 662 | } |
Janak Ramakrishnan | df0531f | 2015-09-23 17:30:04 +0000 | [diff] [blame] | 663 | |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 664 | // Accumulate a transitive digest of the bzl file, the digests of its direct loads, and the |
brandjon | 5a33c63 | 2020-10-23 22:09:26 -0700 | [diff] [blame] | 665 | // digest of the @_builtins pseudo-repository (if applicable). |
adonovan | 1d93d26 | 2020-05-07 14:50:42 -0700 | [diff] [blame] | 666 | Fingerprint fp = new Fingerprint(); |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 667 | fp.addBytes(compileValue.getDigest()); |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 668 | |
| 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 |
adonovan | 1d93d26 | 2020-05-07 14:50:42 -0700 | [diff] [blame] | 675 | fp.addBytes(v.getTransitiveDigest()); |
Janak Ramakrishnan | dfd3497 | 2015-09-22 02:53:05 +0000 | [diff] [blame] | 676 | } |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 677 | |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 678 | // Retrieve predeclared symbols and complete the digest computation. |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 679 | ImmutableMap<String, Object> predeclared = |
| 680 | getAndDigestPredeclaredEnvironment(key, env, starlarkSemantics, fp, inliningState); |
| 681 | if (predeclared == null) { |
| 682 | return null; |
| 683 | } |
adonovan | 1d93d26 | 2020-05-07 14:50:42 -0700 | [diff] [blame] | 684 | byte[] transitiveDigest = fp.digestAndReset(); |
John Field | 1ea7fc3 | 2015-12-22 19:37:19 +0000 | [diff] [blame] | 685 | |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 686 | // Construct the initial Starlark module, then execute the code and return the result. |
| 687 | // The additional information in BazelModuleContext reifies the load DAG. |
brandjon | bc9bd06 | 2020-08-12 09:41:12 -0700 | [diff] [blame] | 688 | Module module = Module.withPredeclared(starlarkSemantics, predeclared); |
adonovan | 87b4608 | 2020-07-08 15:58:04 -0700 | [diff] [blame] | 689 | module.setClientData( |
| 690 | BazelModuleContext.create( |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 691 | label, file.getStartLocation().file(), ImmutableMap.copyOf(loadMap), transitiveDigest)); |
adonovan | d1b1c4c | 2020-08-26 18:39:53 -0700 | [diff] [blame] | 692 | |
| 693 | // compile |
| 694 | // |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 695 | // 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. |
adonovan | d1b1c4c | 2020-08-26 18:39:53 -0700 | [diff] [blame] | 699 | // |
| 700 | // For now, Program temporarily gives us a way to compile an already-resolved file: |
| 701 | Program prog = Program.compileResolvedFile(file); |
| 702 | |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 703 | // executeBzlFile may post events to the Environment's handler, but events do not matter when |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 704 | // caching BzlLoadValues. Note that executing the code mutates the module. |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 705 | executeBzlFile( |
adonovan | d1b1c4c | 2020-08-26 18:39:53 -0700 | [diff] [blame] | 706 | prog, key.getLabel(), module, loadMap, starlarkSemantics, env.getListener(), repoMapping); |
adonovan | 39e42d1 | 2020-07-09 09:16:58 -0700 | [diff] [blame] | 707 | return new BzlLoadValue(module, transitiveDigest); |
John Field | a97e17f | 2015-11-13 02:19:52 +0000 | [diff] [blame] | 708 | } |
| 709 | |
laurentlb | 4efb6c2 | 2019-07-03 15:33:46 -0700 | [diff] [blame] | 710 | private static ImmutableMap<RepositoryName, RepositoryName> getRepositoryMapping( |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 711 | BzlLoadValue.Key key, Environment env) throws InterruptedException { |
brandjon | 7154be0 | 2020-05-22 09:47:32 -0700 | [diff] [blame] | 712 | Label enclosingFileLabel = key.getLabel(); |
pcloudy | 260c3aa | 2019-01-04 02:33:50 -0800 | [diff] [blame] | 713 | |
dannark | d102a39 | 2019-01-09 13:12:01 -0800 | [diff] [blame] | 714 | ImmutableMap<RepositoryName, RepositoryName> repositoryMapping; |
brandjon | b28a76b | 2020-05-26 12:53:22 -0700 | [diff] [blame] | 715 | if (key instanceof BzlLoadValue.KeyForWorkspace) { |
brandjon | 7154be0 | 2020-05-22 09:47:32 -0700 | [diff] [blame] | 716 | // Still during workspace file evaluation |
brandjon | b28a76b | 2020-05-26 12:53:22 -0700 | [diff] [blame] | 717 | BzlLoadValue.KeyForWorkspace keyForWorkspace = (BzlLoadValue.KeyForWorkspace) key; |
| 718 | if (keyForWorkspace.getWorkspaceChunk() == 0) { |
brandjon | 7154be0 | 2020-05-22 09:47:32 -0700 | [diff] [blame] | 719 | // There is no previous workspace chunk |
| 720 | repositoryMapping = ImmutableMap.of(); |
| 721 | } else { |
| 722 | SkyKey workspaceFileKey = |
| 723 | WorkspaceFileValue.key( |
brandjon | b28a76b | 2020-05-26 12:53:22 -0700 | [diff] [blame] | 724 | keyForWorkspace.getWorkspacePath(), keyForWorkspace.getWorkspaceChunk() - 1); |
brandjon | 7154be0 | 2020-05-22 09:47:32 -0700 | [diff] [blame] | 725 | 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() |
Googler | 0617f2c | 2020-10-22 08:43:54 -0700 | [diff] [blame] | 731 | .getOrDefault(enclosingFileLabel.getRepository(), ImmutableMap.of()); |
brandjon | 7154be0 | 2020-05-22 09:47:32 -0700 | [diff] [blame] | 732 | } |
| 733 | } else { |
| 734 | // We are fully done with workspace evaluation so we should get the mappings from the |
| 735 | // final RepositoryMappingValue |
dannark | 4e42c32 | 2018-11-08 19:32:04 -0800 | [diff] [blame] | 736 | 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(); |
dannark | 4e42c32 | 2018-11-08 19:32:04 -0800 | [diff] [blame] | 744 | } |
dannark | d102a39 | 2019-01-09 13:12:01 -0800 | [diff] [blame] | 745 | return repositoryMapping; |
| 746 | } |
| 747 | |
| 748 | /** |
adonovan | 699a90a | 2020-05-20 19:48:53 -0700 | [diff] [blame] | 749 | * 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 Field | a97e17f | 2015-11-13 02:19:52 +0000 | [diff] [blame] | 753 | */ |
| 754 | @Nullable |
adonovan | c0e8690 | 2020-11-19 15:50:29 -0800 | [diff] [blame^] | 755 | static ImmutableList<Pair<String, Label>> getLoadLabels( |
Googler | b2e1fe3 | 2019-09-20 14:00:39 -0700 | [diff] [blame] | 756 | EventHandler handler, |
Googler | 66d099e | 2019-09-26 08:07:06 -0700 | [diff] [blame] | 757 | StarlarkFile file, |
Googler | b2e1fe3 | 2019-09-20 14:00:39 -0700 | [diff] [blame] | 758 | 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; |
adonovan | c0e8690 | 2020-11-19 15:50:29 -0800 | [diff] [blame^] | 767 | ImmutableList.Builder<Pair<String, Label>> loads = ImmutableList.builder(); |
Googler | b2e1fe3 | 2019-09-20 14:00:39 -0700 | [diff] [blame] | 768 | 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". |
Googler | b2e1fe3 | 2019-09-20 14:00:39 -0700 | [diff] [blame] | 775 | 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 | } |
brandjon | 3f0917a | 2020-10-26 18:01:50 -0700 | [diff] [blame] | 784 | 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 | } |
adonovan | 699a90a | 2020-05-20 19:48:53 -0700 | [diff] [blame] | 789 | loads.add(Pair.of(module, label)); |
Googler | b2e1fe3 | 2019-09-20 14:00:39 -0700 | [diff] [blame] | 790 | } catch (LabelSyntaxException ex) { |
Googler | 83439e6 | 2019-09-24 12:11:30 -0700 | [diff] [blame] | 791 | handler.handle( |
adonovan | 22096af | 2020-01-08 07:09:52 -0800 | [diff] [blame] | 792 | Event.error( |
| 793 | load.getImport().getStartLocation(), "in load statement: " + ex.getMessage())); |
Googler | b2e1fe3 | 2019-09-20 14:00:39 -0700 | [diff] [blame] | 794 | ok = false; |
| 795 | } |
| 796 | } |
| 797 | } |
adonovan | c0e8690 | 2020-11-19 15:50:29 -0800 | [diff] [blame^] | 798 | return ok ? loads.build() : null; |
Googler | b2e1fe3 | 2019-09-20 14:00:39 -0700 | [diff] [blame] | 799 | } |
| 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 Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 808 | } |
| 809 | |
adgar | ebc0f2c | 2019-08-15 15:36:56 -0700 | [diff] [blame] | 810 | /** |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 811 | * Computes the BzlLoadValue for all given keys using vanilla Skyframe evaluation, returning |
| 812 | * {@code null} if Skyframe deps were missing and have been requested. |
adgar | ebc0f2c | 2019-08-15 15:36:56 -0700 | [diff] [blame] | 813 | */ |
| 814 | @Nullable |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 815 | private static List<BzlLoadValue> computeBzlLoadsWithSkyframe( |
brandjon | 844fef5 | 2020-06-08 10:30:36 -0700 | [diff] [blame] | 816 | Environment env, List<BzlLoadValue.Key> keys, StarlarkFile requestingFile) |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 817 | throws BzlLoadFailedException, InterruptedException { |
| 818 | List<BzlLoadValue> bzlLoads = Lists.newArrayListWithExpectedSize(keys.size()); |
| 819 | Map<SkyKey, ValueOrException<BzlLoadFailedException>> values = |
| 820 | env.getValuesOrThrow(keys, BzlLoadFailedException.class); |
brandjon | f4f9193 | 2020-05-11 10:20:48 -0700 | [diff] [blame] | 821 | // Uses same order as load()s in the file. Order matters since we report the first error. |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 822 | for (BzlLoadValue.Key key : keys) { |
adgar | ebc0f2c | 2019-08-15 15:36:56 -0700 | [diff] [blame] | 823 | try { |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 824 | bzlLoads.add((BzlLoadValue) values.get(key).get()); |
| 825 | } catch (BzlLoadFailedException exn) { |
brandjon | 844fef5 | 2020-06-08 10:30:36 -0700 | [diff] [blame] | 826 | throw BzlLoadFailedException.whileLoadingDep(requestingFile.getStartLocation().file(), exn); |
adgar | ebc0f2c | 2019-08-15 15:36:56 -0700 | [diff] [blame] | 827 | } |
| 828 | } |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 829 | return env.valuesMissing() ? null : bzlLoads; |
adgar | ebc0f2c | 2019-08-15 15:36:56 -0700 | [diff] [blame] | 830 | } |
| 831 | |
| 832 | /** |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 833 | * Computes the BzlLoadValue for all given keys by reusing this instance of the BzlLoadFunction, |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 834 | * 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 |
adgar | ebc0f2c | 2019-08-15 15:36:56 -0700 | [diff] [blame] | 838 | */ |
| 839 | @Nullable |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 840 | private List<BzlLoadValue> computeBzlLoadsWithInlining( |
brandjon | 844fef5 | 2020-06-08 10:30:36 -0700 | [diff] [blame] | 841 | Environment env, |
| 842 | List<BzlLoadValue.Key> keys, |
| 843 | StarlarkFile requestingFile, |
| 844 | InliningState inliningState) |
brandjon | 4eed64e | 2020-08-24 10:13:53 -0700 | [diff] [blame] | 845 | throws BzlLoadFailedException, InterruptedException { |
brandjon | 844fef5 | 2020-06-08 10:30:36 -0700 | [diff] [blame] | 846 | String filePathForErrors = requestingFile.getStartLocation().file(); |
adgar | ebc0f2c | 2019-08-15 15:36:56 -0700 | [diff] [blame] | 847 | Preconditions.checkState( |
| 848 | env instanceof RecordingSkyFunctionEnvironment, |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 849 | "Expected to be recording dep requests when inlining BzlLoadFunction: %s", |
brandjon | 844fef5 | 2020-06-08 10:30:36 -0700 | [diff] [blame] | 850 | filePathForErrors); |
adgar | ebc0f2c | 2019-08-15 15:36:56 -0700 | [diff] [blame] | 851 | Environment strippedEnv = ((RecordingSkyFunctionEnvironment) env).getDelegate(); |
brandjon | 9198819 | 2020-05-28 14:57:45 -0700 | [diff] [blame] | 852 | |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 853 | List<BzlLoadValue> bzlLoads = Lists.newArrayListWithExpectedSize(keys.size()); |
nharmata | 60d0460 | 2020-08-11 12:28:57 -0700 | [diff] [blame] | 854 | // For the sake of ensuring the graph structure is deterministic, we need to request all of our |
brandjon | b4ebbfc | 2020-08-13 15:50:23 -0700 | [diff] [blame] | 855 | // 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. |
nharmata | 60d0460 | 2020-08-11 12:28:57 -0700 | [diff] [blame] | 857 | // |
brandjon | b4ebbfc | 2020-08-13 15:50:23 -0700 | [diff] [blame] | 858 | // 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. |
brandjon | 4eed64e | 2020-08-24 10:13:53 -0700 | [diff] [blame] | 866 | BzlLoadFailedException deferredException = null; |
adgar | ebc0f2c | 2019-08-15 15:36:56 -0700 | [diff] [blame] | 867 | boolean valuesMissing = false; |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 868 | // NOTE: Iterating over loads in the order listed in the file. |
| 869 | for (BzlLoadValue.Key key : keys) { |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 870 | CachedBzlLoadData cachedData; |
adgar | ebc0f2c | 2019-08-15 15:36:56 -0700 | [diff] [blame] | 871 | try { |
brandjon | 1d3f309 | 2020-06-08 10:58:25 -0700 | [diff] [blame] | 872 | cachedData = computeInlineCachedData(key, strippedEnv, inliningState); |
brandjon | 844fef5 | 2020-06-08 10:30:36 -0700 | [diff] [blame] | 873 | } catch (BzlLoadFailedException e) { |
brandjon | 4eed64e | 2020-08-24 10:13:53 -0700 | [diff] [blame] | 874 | if (deferredException == null) { |
| 875 | deferredException = BzlLoadFailedException.whileLoadingDep(filePathForErrors, e); |
| 876 | } |
adgar | ebc0f2c | 2019-08-15 15:36:56 -0700 | [diff] [blame] | 877 | continue; |
| 878 | } |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 879 | if (cachedData == null) { |
nharmata | 636fb5e | 2020-07-24 15:30:08 -0700 | [diff] [blame] | 880 | // 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. |
adgar | ebc0f2c | 2019-08-15 15:36:56 -0700 | [diff] [blame] | 887 | valuesMissing = true; |
| 888 | } else { |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 889 | bzlLoads.add(cachedData.getValue()); |
adgar | ebc0f2c | 2019-08-15 15:36:56 -0700 | [diff] [blame] | 890 | } |
| 891 | } |
| 892 | if (deferredException != null) { |
brandjon | 4eed64e | 2020-08-24 10:13:53 -0700 | [diff] [blame] | 893 | throw deferredException; |
adgar | ebc0f2c | 2019-08-15 15:36:56 -0700 | [diff] [blame] | 894 | } |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 895 | return valuesMissing ? null : bzlLoads; |
adgar | ebc0f2c | 2019-08-15 15:36:56 -0700 | [diff] [blame] | 896 | } |
| 897 | |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 898 | /** |
brandjon | bc9bd06 | 2020-08-12 09:41:12 -0700 | [diff] [blame] | 899 | * Obtains the predeclared environment for a .bzl file, based on the type of .bzl and (if |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 900 | * applicable) the injected builtins. |
brandjon | bc9bd06 | 2020-08-12 09:41:12 -0700 | [diff] [blame] | 901 | * |
| 902 | * <p>Returns null if there was a missing Skyframe dep or unspecified exception. |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 903 | * |
| 904 | * <p>In the case that injected builtins are used, updates the given fingerprint with the digest |
brandjon | 5a33c63 | 2020-10-23 22:09:26 -0700 | [diff] [blame] | 905 | * of the {@code @_builtins} pseudo-repository. |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 906 | */ |
brandjon | bc9bd06 | 2020-08-12 09:41:12 -0700 | [diff] [blame] | 907 | @Nullable |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 908 | private ImmutableMap<String, Object> getAndDigestPredeclaredEnvironment( |
brandjon | bc9bd06 | 2020-08-12 09:41:12 -0700 | [diff] [blame] | 909 | BzlLoadValue.Key key, |
| 910 | Environment env, |
| 911 | StarlarkSemantics starlarkSemantics, |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 912 | Fingerprint fp, |
brandjon | bc9bd06 | 2020-08-12 09:41:12 -0700 | [diff] [blame] | 913 | InliningState inliningState) |
| 914 | throws BzlLoadFailedException, InterruptedException { |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 915 | if (key instanceof BzlLoadValue.KeyForBuild) { |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 916 | // TODO(#11437): Remove ability to disable injection by setting flag to empty string. |
adonovan | 028e1ad | 2020-09-07 07:09:59 -0700 | [diff] [blame] | 917 | if (starlarkSemantics.get(BuildLanguageOptions.EXPERIMENTAL_BUILTINS_BZL_PATH).isEmpty()) { |
brandjon | aae35ab | 2020-08-20 11:04:26 -0700 | [diff] [blame] | 918 | return packageFactory.getUninjectedBuildBzlEnv(); |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 919 | } |
brandjon | bc9bd06 | 2020-08-12 09:41:12 -0700 | [diff] [blame] | 920 | StarlarkBuiltinsValue starlarkBuiltinsValue; |
| 921 | try { |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 922 | if (inliningState == null) { |
| 923 | starlarkBuiltinsValue = |
| 924 | (StarlarkBuiltinsValue) |
| 925 | env.getValueOrThrow(StarlarkBuiltinsValue.key(), BuiltinsFailedException.class); |
brandjon | bc9bd06 | 2020-08-12 09:41:12 -0700 | [diff] [blame] | 926 | } else { |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 927 | starlarkBuiltinsValue = |
| 928 | StarlarkBuiltinsFunction.computeInline( |
| 929 | StarlarkBuiltinsValue.key(), |
| 930 | env, |
| 931 | inliningState, |
| 932 | packageFactory, |
| 933 | /*bzlLoadFunction=*/ this); |
brandjon | bc9bd06 | 2020-08-12 09:41:12 -0700 | [diff] [blame] | 934 | } |
| 935 | } catch (BuiltinsFailedException e) { |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 936 | throw BzlLoadFailedException.builtinsFailed(key.getLabel(), e); |
brandjon | bc9bd06 | 2020-08-12 09:41:12 -0700 | [diff] [blame] | 937 | } |
| 938 | if (starlarkBuiltinsValue == null) { |
| 939 | return null; |
| 940 | } |
brandjon | 3f017db | 2020-08-20 07:51:39 -0700 | [diff] [blame] | 941 | fp.addBytes(starlarkBuiltinsValue.transitiveDigest); |
brandjon | 1af65cf | 2020-06-06 18:17:53 -0700 | [diff] [blame] | 942 | return starlarkBuiltinsValue.predeclaredForBuildBzl; |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 943 | } else if (key instanceof BzlLoadValue.KeyForWorkspace) { |
brandjon | aae35ab | 2020-08-20 11:04:26 -0700 | [diff] [blame] | 944 | return packageFactory.getWorkspaceBzlEnv(); |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 945 | } else if (key instanceof BzlLoadValue.KeyForBuiltins) { |
brandjon | aae35ab | 2020-08-20 11:04:26 -0700 | [diff] [blame] | 946 | return packageFactory.getBuiltinsBzlEnv(); |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 947 | } else { |
| 948 | throw new AssertionError("Unknown key type: " + key.getClass()); |
| 949 | } |
| 950 | } |
| 951 | |
adonovan | d1b1c4c | 2020-08-26 18:39:53 -0700 | [diff] [blame] | 952 | /** Executes the compiled .bzl file defining the module to be loaded. */ |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 953 | private void executeBzlFile( |
adonovan | d1b1c4c | 2020-08-26 18:39:53 -0700 | [diff] [blame] | 954 | Program prog, |
brandjon | 7154be0 | 2020-05-22 09:47:32 -0700 | [diff] [blame] | 955 | Label label, |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 956 | Module module, |
adonovan | 1d93d26 | 2020-05-07 14:50:42 -0700 | [diff] [blame] | 957 | Map<String, Module> loadedModules, |
laurentlb | 6659b4c | 2019-02-18 07:23:36 -0800 | [diff] [blame] | 958 | StarlarkSemantics starlarkSemantics, |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 959 | ExtendedEventHandler skyframeEventHandler, |
dannark | d102a39 | 2019-01-09 13:12:01 -0800 | [diff] [blame] | 960 | ImmutableMap<RepositoryName, RepositoryName> repositoryMapping) |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 961 | throws BzlLoadFailedException, InterruptedException { |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 962 | try (Mutability mu = Mutability.create("loading", label)) { |
adonovan | 09d1370 | 2020-05-19 08:26:55 -0700 | [diff] [blame] | 963 | StarlarkThread thread = new StarlarkThread(mu, starlarkSemantics); |
adonovan | dfcda9e | 2020-05-08 13:10:45 -0700 | [diff] [blame] | 964 | thread.setLoader(loadedModules::get); |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 965 | StoredEventHandler starlarkEventHandler = new StoredEventHandler(); |
| 966 | thread.setPrintHandler(Event.makeDebugPrintHandler(starlarkEventHandler)); |
brandjon | db4d1ea | 2020-08-14 11:31:08 -0700 | [diff] [blame] | 967 | packageFactory |
| 968 | .getRuleClassProvider() |
| 969 | .setStarlarkThreadContext(thread, label, repositoryMapping); |
adonovan | d1b1c4c | 2020-08-26 18:39:53 -0700 | [diff] [blame] | 970 | execAndExport(prog, label, starlarkEventHandler, module, thread); |
Googler | 2234d38 | 2015-03-24 17:18:02 +0000 | [diff] [blame] | 971 | |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 972 | Event.replayEventsOn(skyframeEventHandler, starlarkEventHandler.getEvents()); |
| 973 | for (Postable post : starlarkEventHandler.getPosts()) { |
| 974 | skyframeEventHandler.post(post); |
Klaus Aehlig | da0a701 | 2017-06-14 13:40:23 +0200 | [diff] [blame] | 975 | } |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 976 | if (starlarkEventHandler.hasErrors()) { |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 977 | throw BzlLoadFailedException.errors(label); |
Francois-Rene Rideau | 89312fb | 2015-09-10 18:53:03 +0000 | [diff] [blame] | 978 | } |
Googler | 2234d38 | 2015-03-24 17:18:02 +0000 | [diff] [blame] | 979 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 980 | } |
| 981 | |
adonovan | 1d93d26 | 2020-05-07 14:50:42 -0700 | [diff] [blame] | 982 | // Precondition: thread has a valid transitiveDigest. |
brandjon | a0c6f81 | 2020-06-06 14:13:19 -0700 | [diff] [blame] | 983 | // TODO(adonovan): executeBzlFile would make a better public API than this function. |
Googler | a3421e2 | 2019-09-26 06:48:32 -0700 | [diff] [blame] | 984 | public static void execAndExport( |
adonovan | d1b1c4c | 2020-08-26 18:39:53 -0700 | [diff] [blame] | 985 | Program prog, Label label, EventHandler handler, Module module, StarlarkThread thread) |
Googler | a3421e2 | 2019-09-26 06:48:32 -0700 | [diff] [blame] | 986 | throws InterruptedException { |
Dmitry Lomov | 950310f | 2017-03-01 17:45:12 +0000 | [diff] [blame] | 987 | |
Googler | ba86854 | 2019-10-09 07:26:27 -0700 | [diff] [blame] | 988 | // 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) -> { |
gregce | d281df7 | 2020-05-11 12:27:06 -0700 | [diff] [blame] | 993 | if (value instanceof StarlarkExportable) { |
| 994 | StarlarkExportable exp = (StarlarkExportable) value; |
Googler | ba86854 | 2019-10-09 07:26:27 -0700 | [diff] [blame] | 995 | if (!exp.isExported()) { |
| 996 | try { |
brandjon | 7154be0 | 2020-05-22 09:47:32 -0700 | [diff] [blame] | 997 | exp.export(label, name); |
Googler | ba86854 | 2019-10-09 07:26:27 -0700 | [diff] [blame] | 998 | } catch (EvalException ex) { |
adonovan | 3391e17 | 2020-08-05 14:21:14 -0700 | [diff] [blame] | 999 | handler.handle(Event.error(null, ex.getMessageWithStack())); |
Googler | ba86854 | 2019-10-09 07:26:27 -0700 | [diff] [blame] | 1000 | } |
| 1001 | } |
Dmitry Lomov | 950310f | 2017-03-01 17:45:12 +0000 | [diff] [blame] | 1002 | } |
Googler | ba86854 | 2019-10-09 07:26:27 -0700 | [diff] [blame] | 1003 | }); |
| 1004 | |
| 1005 | try { |
adonovan | d1b1c4c | 2020-08-26 18:39:53 -0700 | [diff] [blame] | 1006 | Starlark.execFileProgram(prog, module, thread); |
Googler | ba86854 | 2019-10-09 07:26:27 -0700 | [diff] [blame] | 1007 | } catch (EvalException ex) { |
adonovan | 3391e17 | 2020-08-05 14:21:14 -0700 | [diff] [blame] | 1008 | handler.handle(Event.error(null, ex.getMessageWithStack())); |
Dmitry Lomov | 950310f | 2017-03-01 17:45:12 +0000 | [diff] [blame] | 1009 | } |
| 1010 | } |
| 1011 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1012 | @Override |
| 1013 | public String extractTag(SkyKey skyKey) { |
| 1014 | return null; |
| 1015 | } |
| 1016 | |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 1017 | /** |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1018 | * A manager abstracting over the method for obtaining {@code BzlCompileValue}s. See comment in |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 1019 | * {@link #create}. |
| 1020 | */ |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1021 | private interface ValueGetter { |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1022 | @Nullable |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1023 | BzlCompileValue getBzlCompileValue(BzlCompileValue.Key key, Environment env) |
| 1024 | throws BzlCompileFunction.FailedIOException, InterruptedException; |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1025 | |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1026 | void doneWithBzlCompileValue(BzlCompileValue.Key key); |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1027 | } |
| 1028 | |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1029 | /** 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(); |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1032 | |
| 1033 | @Nullable |
| 1034 | @Override |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1035 | public BzlCompileValue getBzlCompileValue(BzlCompileValue.Key key, Environment env) |
| 1036 | throws BzlCompileFunction.FailedIOException, InterruptedException { |
| 1037 | return (BzlCompileValue) env.getValueOrThrow(key, BzlCompileFunction.FailedIOException.class); |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1038 | } |
| 1039 | |
| 1040 | @Override |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1041 | public void doneWithBzlCompileValue(BzlCompileValue.Key key) {} |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1042 | } |
| 1043 | |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 1044 | /** |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1045 | * 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}. |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 1048 | */ |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1049 | private static class InliningAndCachingGetter implements ValueGetter { |
brandjon | db4d1ea | 2020-08-14 11:31:08 -0700 | [diff] [blame] | 1050 | private final PackageFactory packageFactory; |
janakr | 3af783a | 2020-09-08 14:12:14 -0700 | [diff] [blame] | 1051 | private final HashFunction hashFunction; |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1052 | // 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 |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 1054 | // of Skyframe restarts. (If we weren't inlining, Skyframe would cache this for us.) |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1055 | private final Cache<BzlCompileValue.Key, BzlCompileValue> bzlCompileCache; |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1056 | |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1057 | private InliningAndCachingGetter( |
brandjon | db4d1ea | 2020-08-14 11:31:08 -0700 | [diff] [blame] | 1058 | PackageFactory packageFactory, |
janakr | 3af783a | 2020-09-08 14:12:14 -0700 | [diff] [blame] | 1059 | HashFunction hashFunction, |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1060 | Cache<BzlCompileValue.Key, BzlCompileValue> bzlCompileCache) { |
brandjon | db4d1ea | 2020-08-14 11:31:08 -0700 | [diff] [blame] | 1061 | this.packageFactory = packageFactory; |
janakr | 3af783a | 2020-09-08 14:12:14 -0700 | [diff] [blame] | 1062 | this.hashFunction = hashFunction; |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1063 | this.bzlCompileCache = bzlCompileCache; |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1064 | } |
| 1065 | |
| 1066 | @Nullable |
| 1067 | @Override |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1068 | public BzlCompileValue getBzlCompileValue(BzlCompileValue.Key key, Environment env) |
| 1069 | throws BzlCompileFunction.FailedIOException, InterruptedException { |
| 1070 | BzlCompileValue value = bzlCompileCache.getIfPresent(key); |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1071 | if (value == null) { |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1072 | value = BzlCompileFunction.computeInline(key, env, packageFactory, hashFunction); |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1073 | if (value != null) { |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1074 | bzlCompileCache.put(key, value); |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1075 | } |
| 1076 | } |
| 1077 | return value; |
| 1078 | } |
| 1079 | |
| 1080 | @Override |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1081 | public void doneWithBzlCompileValue(BzlCompileValue.Key key) { |
| 1082 | bzlCompileCache.invalidate(key); |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1083 | } |
| 1084 | } |
| 1085 | |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 1086 | /** |
| 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 = |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 1094 | new CachedBzlLoadDataBuilderFactory(); |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1095 | |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 1096 | private CachedBzlLoadDataManager(int cacheSize) { |
| 1097 | this.cacheSize = cacheSize; |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1098 | } |
| 1099 | |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 1100 | private CachedBzlLoadData.Builder cachedDataBuilder() { |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 1101 | return cachedDataBuilderFactory.newCachedBzlLoadDataBuilder(); |
brandjon | 2491264 | 2020-05-27 11:28:45 -0700 | [diff] [blame] | 1102 | } |
| 1103 | |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1104 | private void reset() { |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 1105 | if (cache != null) { |
| 1106 | logger.atInfo().log("Starlark inlining cache stats from earlier build: " + cache.stats()); |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1107 | } |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 1108 | cachedDataBuilderFactory = new CachedBzlLoadDataBuilderFactory(); |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1109 | Preconditions.checkState( |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 1110 | cacheSize >= 0, "Expected positive Starlark cache size if caching. %s", cacheSize); |
| 1111 | cache = |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1112 | CacheBuilder.newBuilder() |
| 1113 | .concurrencyLevel(BlazeInterners.concurrencyLevel()) |
brandjon | 8d66929 | 2020-05-28 15:27:38 -0700 | [diff] [blame] | 1114 | .maximumSize(cacheSize) |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1115 | .recordStats() |
| 1116 | .build(); |
| 1117 | } |
| 1118 | } |
| 1119 | |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1120 | static final class BzlLoadFailedException extends Exception implements SaneAnalysisException { |
| 1121 | private final Transience transience; |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1122 | private final DetailedExitCode detailedExitCode; |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1123 | |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1124 | private BzlLoadFailedException( |
| 1125 | String errorMessage, DetailedExitCode detailedExitCode, Transience transience) { |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1126 | super(errorMessage); |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1127 | this.transience = transience; |
| 1128 | this.detailedExitCode = detailedExitCode; |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1129 | } |
| 1130 | |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1131 | 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) { |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1140 | super(errorMessage, cause); |
| 1141 | this.transience = transience; |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1142 | 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); |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1152 | } |
| 1153 | |
| 1154 | Transience getTransience() { |
| 1155 | return transience; |
| 1156 | } |
| 1157 | |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1158 | @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 | |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1171 | // 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. |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 1176 | // TODO(brandjon): Normalize the requestingFile part to use the same convention for printing |
| 1177 | // the bzl file as in errors(), starlarkErrors(). |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1178 | return new BzlLoadFailedException( |
| 1179 | "in " + requestingFile + ": " + cause.getMessage(), cause.getDetailedExitCode()); |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1180 | } |
| 1181 | |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 1182 | 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. |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1185 | return new BzlLoadFailedException( |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 1186 | String.format( |
| 1187 | "Extension file '%s'%s has errors", |
| 1188 | label.toPathFragment(), |
| 1189 | StarlarkBuiltinsValue.isBuiltinsRepo(label.getRepository()) ? " (internal)" : ""), |
| 1190 | Code.EVAL_ERROR); |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1191 | } |
| 1192 | |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1193 | static BzlLoadFailedException errorFindingContainingPackage( |
| 1194 | PathFragment file, Exception cause) { |
| 1195 | String errorMessage = |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1196 | String.format( |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1197 | "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); |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1204 | } |
| 1205 | |
| 1206 | static BzlLoadFailedException errorReadingBzl( |
adonovan | 0a1f0f5 | 2020-09-16 11:11:04 -0700 | [diff] [blame] | 1207 | PathFragment file, BzlCompileFunction.FailedIOException cause) { |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1208 | String errorMessage = |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1209 | String.format( |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1210 | "Encountered error while reading extension file '%s': %s", file, cause.getMessage()); |
| 1211 | return new BzlLoadFailedException(errorMessage, Code.IO_ERROR, cause, cause.getTransience()); |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1212 | } |
| 1213 | |
| 1214 | static BzlLoadFailedException noBuildFile(Label file, @Nullable String reason) { |
| 1215 | if (reason != null) { |
| 1216 | return new BzlLoadFailedException( |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1217 | String.format("Unable to find package for %s: %s.", file, reason), |
| 1218 | Code.PACKAGE_NOT_FOUND); |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1219 | } |
| 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.", |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1225 | file), |
| 1226 | Code.PACKAGE_NOT_FOUND); |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1227 | } |
| 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, |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1239 | containingPackageLookupValue), |
| 1240 | Code.LABEL_CROSSES_PACKAGE_BOUNDARY); |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1241 | } |
| 1242 | |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 1243 | static BzlLoadFailedException starlarkErrors(Label label) { |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1244 | return new BzlLoadFailedException( |
brandjon | d5d8677 | 2020-10-26 16:21:22 -0700 | [diff] [blame] | 1245 | String.format( |
| 1246 | "Extension '%s'%s has errors", |
| 1247 | label.toPathFragment(), |
| 1248 | StarlarkBuiltinsValue.isBuiltinsRepo(label.getRepository()) ? " (internal)" : ""), |
| 1249 | Code.PARSE_ERROR); |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1250 | } |
| 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()), |
mschaller | 859c9ac | 2020-09-25 16:09:19 -0700 | [diff] [blame] | 1257 | Code.BUILTINS_ERROR, |
brandjon | b9c81cd | 2020-08-24 11:00:58 -0700 | [diff] [blame] | 1258 | cause, |
| 1259 | cause.getTransience()); |
| 1260 | } |
| 1261 | } |
| 1262 | |
brandjon | 771a029 | 2020-05-26 12:04:16 -0700 | [diff] [blame] | 1263 | private static final class BzlLoadFunctionException extends SkyFunctionException { |
| 1264 | private BzlLoadFunctionException(BzlLoadFailedException cause) { |
nharmata | dc1d9dc | 2020-04-18 16:53:28 -0700 | [diff] [blame] | 1265 | super(cause, cause.transience); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1266 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 1267 | } |
| 1268 | } |