blob: 4198e0ca45cb483682641c80cc1d638eefb937c3 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14package com.google.devtools.build.lib.analysis;
15
16import com.google.common.base.Joiner;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010017import com.google.common.collect.ImmutableList;
18import com.google.common.collect.ImmutableSet;
19import com.google.common.collect.Lists;
Rumou Duan33bab462016-04-25 17:55:12 +000020import com.google.devtools.build.lib.actions.ActionAnalysisMetadata;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010021import com.google.devtools.build.lib.actions.Artifact;
22import com.google.devtools.build.lib.actions.ArtifactFactory;
23import com.google.devtools.build.lib.actions.ArtifactOwner;
24import com.google.devtools.build.lib.actions.MiddlemanFactory;
25import com.google.devtools.build.lib.actions.Root;
26import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoCollection;
Janak Ramakrishnanf27f4382015-06-04 19:50:15 +000027import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoFactory;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010028import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoFactory.BuildInfoKey;
Janak Ramakrishnanf27f4382015-06-04 19:50:15 +000029import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
Klaus Aehlig777b30d2017-02-24 16:30:15 +000030import com.google.devtools.build.lib.events.ExtendedEventHandler;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031import com.google.devtools.build.lib.events.StoredEventHandler;
32import com.google.devtools.build.lib.packages.Target;
33import com.google.devtools.build.lib.skyframe.BuildInfoCollectionValue;
Janak Ramakrishnanf27f4382015-06-04 19:50:15 +000034import com.google.devtools.build.lib.skyframe.PrecomputedValue;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010035import com.google.devtools.build.lib.skyframe.WorkspaceStatusValue;
brandjonb712f332017-04-29 16:03:32 +020036import com.google.devtools.build.lib.syntax.SkylarkSemanticsOptions;
Mark Schaller6df81792015-12-10 18:47:47 +000037import com.google.devtools.build.lib.util.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010038import com.google.devtools.build.lib.vfs.PathFragment;
39import com.google.devtools.build.skyframe.SkyFunction;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010040import java.io.PrintWriter;
41import java.io.StringWriter;
42import java.util.ArrayList;
43import java.util.Collection;
44import java.util.Collections;
45import java.util.HashMap;
46import java.util.HashSet;
47import java.util.List;
48import java.util.Map;
49import java.util.Set;
50import java.util.TreeMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010051import javax.annotation.Nullable;
52
53/**
54 * The implementation of AnalysisEnvironment used for analysis. It tracks metadata for each
55 * configured target, such as the errors and warnings emitted by that target. It is intended that
56 * a separate instance is used for each configured target, so that these don't mix up.
57 */
58public class CachingAnalysisEnvironment implements AnalysisEnvironment {
59 private final ArtifactFactory artifactFactory;
60
61 private final ArtifactOwner owner;
62 /**
63 * If this is the system analysis environment, then errors and warnings are directly reported
64 * to the global reporter, rather than stored, i.e., we don't track here whether there are any
65 * errors.
66 */
67 private final boolean isSystemEnv;
68 private final boolean extendedSanityChecks;
69
70 /**
71 * If false, no actions will be registered, they'll all be just dropped.
72 *
73 * <p>Usually, an analysis environment should register all actions. However, in some scenarios we
74 * analyze some targets twice, but the first one only serves the purpose of collecting information
75 * for the second analysis. In this case we don't register actions created by the first pass in
76 * order to avoid action conflicts.
77 */
78 private final boolean allowRegisteringActions;
79
80 private boolean enabled = true;
81 private MiddlemanFactory middlemanFactory;
Klaus Aehlig777b30d2017-02-24 16:30:15 +000082 private ExtendedEventHandler errorEventListener;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010083 private SkyFunction.Environment skyframeEnv;
84 private Map<Artifact, String> artifacts;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010085
86 /**
87 * The list of actions registered by the configured target this analysis environment is
88 * responsible for. May get cleared out at the end of the analysis of said target.
89 */
Rumou Duan33bab462016-04-25 17:55:12 +000090 final List<ActionAnalysisMetadata> actions = new ArrayList<>();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010091
Klaus Aehlig777b30d2017-02-24 16:30:15 +000092 public CachingAnalysisEnvironment(
93 ArtifactFactory artifactFactory,
94 ArtifactOwner owner,
95 boolean isSystemEnv,
96 boolean extendedSanityChecks,
97 ExtendedEventHandler errorEventListener,
98 SkyFunction.Environment env,
Lukacs Berki76cb02e2017-02-17 14:06:11 +000099 boolean allowRegisteringActions) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100100 this.artifactFactory = artifactFactory;
101 this.owner = Preconditions.checkNotNull(owner);
102 this.isSystemEnv = isSystemEnv;
103 this.extendedSanityChecks = extendedSanityChecks;
104 this.errorEventListener = errorEventListener;
105 this.skyframeEnv = env;
106 this.allowRegisteringActions = allowRegisteringActions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100107 middlemanFactory = new MiddlemanFactory(artifactFactory, this);
108 artifacts = new HashMap<>();
109 }
110
111 public void disable(Target target) {
112 if (!hasErrors() && allowRegisteringActions) {
113 verifyGeneratedArtifactHaveActions(target);
114 }
115 artifacts = null;
116 middlemanFactory = null;
117 enabled = false;
118 errorEventListener = null;
119 skyframeEnv = null;
120 }
121
Rumou Duan33bab462016-04-25 17:55:12 +0000122 private static StringBuilder shortDescription(ActionAnalysisMetadata action) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100123 if (action == null) {
124 return new StringBuilder("null Action");
125 }
126 return new StringBuilder()
127 .append(action.getClass().getName())
128 .append(' ')
129 .append(action.getMnemonic());
130 }
131
132 /**
133 * Sanity checks that all generated artifacts have a generating action.
134 * @param target for error reporting
135 */
136 public void verifyGeneratedArtifactHaveActions(Target target) {
137 Collection<String> orphanArtifacts = getOrphanArtifactMap().values();
138 List<String> checkedActions = null;
139 if (!orphanArtifacts.isEmpty()) {
140 checkedActions = Lists.newArrayListWithCapacity(actions.size());
Rumou Duan33bab462016-04-25 17:55:12 +0000141 for (ActionAnalysisMetadata action : actions) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100142 StringBuilder sb = shortDescription(action);
143 for (Artifact o : action.getOutputs()) {
144 sb.append("\n ");
145 sb.append(o.getExecPathString());
146 }
147 checkedActions.add(sb.toString());
148 }
149 throw new IllegalStateException(
150 String.format(
Alex Humesky14f79d52015-07-08 18:39:15 +0000151 "%s %s : These artifacts do not have a generating action:\n%s\n"
152 + "These actions were checked:\n%s\n",
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100153 target.getTargetKind(), target.getLabel(),
154 Joiner.on('\n').join(orphanArtifacts), Joiner.on('\n').join(checkedActions)));
155 }
156 }
157
158 @Override
159 public ImmutableSet<Artifact> getOrphanArtifacts() {
Ulf Adamsd68626a2016-02-01 14:58:45 +0000160 if (!allowRegisteringActions) {
161 return ImmutableSet.<Artifact>of();
162 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100163 return ImmutableSet.copyOf(getOrphanArtifactMap().keySet());
164 }
165
166 private Map<Artifact, String> getOrphanArtifactMap() {
167 // Construct this set to avoid poor performance under large --runs_per_test.
168 Set<Artifact> artifactsWithActions = new HashSet<>();
Rumou Duan33bab462016-04-25 17:55:12 +0000169 for (ActionAnalysisMetadata action : actions) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100170 // Don't bother checking that every Artifact only appears once; that test is performed
171 // elsewhere (see #testNonUniqueOutputs in ActionListenerIntegrationTest).
172 artifactsWithActions.addAll(action.getOutputs());
173 }
174 // The order of the artifacts.entrySet iteration is unspecified - we use a TreeMap here to
175 // guarantee that the return value of this method is deterministic.
Lukacs Berki8ae34f12015-04-10 14:54:19 +0000176 Map<Artifact, String> orphanArtifacts = new TreeMap<>(Artifact.EXEC_PATH_COMPARATOR);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100177 for (Map.Entry<Artifact, String> entry : artifacts.entrySet()) {
178 Artifact a = entry.getKey();
179 if (!a.isSourceArtifact() && !artifactsWithActions.contains(a)) {
180 orphanArtifacts.put(a, String.format("%s\n%s",
181 a.getExecPathString(), // uncovered artifact
182 entry.getValue())); // origin of creation
183 }
184 }
185 return orphanArtifacts;
186 }
187
188 @Override
Klaus Aehlig777b30d2017-02-24 16:30:15 +0000189 public ExtendedEventHandler getEventHandler() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100190 return errorEventListener;
191 }
192
193 @Override
194 public boolean hasErrors() {
195 // The system analysis environment never has errors.
196 if (isSystemEnv) {
197 return false;
198 }
199 Preconditions.checkState(enabled);
200 return ((StoredEventHandler) errorEventListener).hasErrors();
201 }
202
203 @Override
204 public MiddlemanFactory getMiddlemanFactory() {
205 Preconditions.checkState(enabled);
206 return middlemanFactory;
207 }
208
209 /**
210 * Keeps track of artifacts. We check that all of them have an owner when the environment is
211 * sealed (disable()). For performance reasons we only track the originating stacktrace when
212 * running with --experimental_extended_sanity_checks.
213 */
214 private Artifact trackArtifactAndOrigin(Artifact a, @Nullable Throwable e) {
215 if ((e != null) && !artifacts.containsKey(a)) {
216 StringWriter sw = new StringWriter();
217 e.printStackTrace(new PrintWriter(sw));
218 artifacts.put(a, sw.toString());
219 } else {
220 artifacts.put(a, "No origin, run with --experimental_extended_sanity_checks");
221 }
222 return a;
223 }
224
225 @Override
226 public Artifact getDerivedArtifact(PathFragment rootRelativePath, Root root) {
227 Preconditions.checkState(enabled);
228 return trackArtifactAndOrigin(
229 artifactFactory.getDerivedArtifact(rootRelativePath, root, getOwner()),
230 extendedSanityChecks ? new Throwable() : null);
231 }
232
233 @Override
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000234 public Artifact getTreeArtifact(PathFragment rootRelativePath, Root root) {
235 Preconditions.checkState(enabled);
236 return trackArtifactAndOrigin(
237 artifactFactory.getTreeArtifact(rootRelativePath, root, getOwner()),
238 extendedSanityChecks ? new Throwable() : null);
239 }
240
241 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100242 public Artifact getFilesetArtifact(PathFragment rootRelativePath, Root root) {
243 Preconditions.checkState(enabled);
244 return trackArtifactAndOrigin(
245 artifactFactory.getFilesetArtifact(rootRelativePath, root, getOwner()),
246 extendedSanityChecks ? new Throwable() : null);
247 }
248
249 @Override
250 public Artifact getConstantMetadataArtifact(PathFragment rootRelativePath, Root root) {
251 return artifactFactory.getConstantMetadataArtifact(rootRelativePath, root, getOwner());
252 }
253
254 @Override
Rumou Duan33bab462016-04-25 17:55:12 +0000255 public void registerAction(ActionAnalysisMetadata... actions) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100256 Preconditions.checkState(enabled);
257 if (allowRegisteringActions) {
Ulf Adams07dba942015-03-05 14:47:37 +0000258 Collections.addAll(this.actions, actions);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100259 }
260 }
261
262 @Override
Rumou Duan33bab462016-04-25 17:55:12 +0000263 public ActionAnalysisMetadata getLocalGeneratingAction(Artifact artifact) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100264 Preconditions.checkState(allowRegisteringActions);
Rumou Duan33bab462016-04-25 17:55:12 +0000265 for (ActionAnalysisMetadata action : actions) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100266 if (action.getOutputs().contains(artifact)) {
267 return action;
268 }
269 }
270 return null;
271 }
272
273 @Override
janakr93e3eea2017-03-30 22:09:37 +0000274 public List<ActionAnalysisMetadata> getRegisteredActions() {
275 return Collections.unmodifiableList(actions);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100276 }
277
278 @Override
279 public SkyFunction.Environment getSkyframeEnv() {
280 return skyframeEnv;
281 }
282
283 @Override
brandjonb712f332017-04-29 16:03:32 +0200284 public SkylarkSemanticsOptions getSkylarkSemantics() throws InterruptedException {
285 return PrecomputedValue.SKYLARK_SEMANTICS.get(skyframeEnv);
286 }
287
288 @Override
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000289 public Artifact getStableWorkspaceStatusArtifact() throws InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100290 return ((WorkspaceStatusValue) skyframeEnv.getValue(WorkspaceStatusValue.SKY_KEY))
291 .getStableArtifact();
292 }
293
294 @Override
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000295 public Artifact getVolatileWorkspaceStatusArtifact() throws InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100296 return ((WorkspaceStatusValue) skyframeEnv.getValue(WorkspaceStatusValue.SKY_KEY))
297 .getVolatileArtifact();
298 }
299
Janak Ramakrishnanf27f4382015-06-04 19:50:15 +0000300 // See SkyframeBuildView#getWorkspaceStatusValues for the code that this method is attempting to
301 // verify.
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000302 private NullPointerException collectDebugInfoAndCrash(BuildInfoKey key, BuildConfiguration config)
303 throws InterruptedException {
Janak Ramakrishnanf27f4382015-06-04 19:50:15 +0000304 String debugInfo = key + " " + config;
305 Preconditions.checkState(skyframeEnv.valuesMissing(), debugInfo);
306 Map<BuildInfoKey, BuildInfoFactory> buildInfoFactories = Preconditions.checkNotNull(
307 PrecomputedValue.BUILD_INFO_FACTORIES.get(skyframeEnv), debugInfo);
308 BuildInfoFactory buildInfoFactory =
309 Preconditions.checkNotNull(buildInfoFactories.get(key), debugInfo);
310 Preconditions.checkState(buildInfoFactory.isEnabled(config), debugInfo);
311 throw new NullPointerException("BuildInfoCollectionValue shouldn't have been null");
312 }
313
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100314 @Override
Googler38ad0bf2016-07-01 05:00:14 +0000315 public ImmutableList<Artifact> getBuildInfo(
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000316 RuleContext ruleContext, BuildInfoKey key, BuildConfiguration config)
317 throws InterruptedException {
Googler38ad0bf2016-07-01 05:00:14 +0000318 boolean stamp = AnalysisUtils.isStampingEnabled(ruleContext, config);
Janak Ramakrishnanf27f4382015-06-04 19:50:15 +0000319 BuildInfoCollectionValue collectionValue =
320 (BuildInfoCollectionValue) skyframeEnv.getValue(BuildInfoCollectionValue.key(
Googler38ad0bf2016-07-01 05:00:14 +0000321 new BuildInfoCollectionValue.BuildInfoKeyAndConfig(key, config)));
Janak Ramakrishnanf27f4382015-06-04 19:50:15 +0000322 if (collectionValue == null) {
Googler38ad0bf2016-07-01 05:00:14 +0000323 throw collectDebugInfoAndCrash(key, config);
Janak Ramakrishnanf27f4382015-06-04 19:50:15 +0000324 }
325 BuildInfoCollection collection = collectionValue.getCollection();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100326 return stamp ? collection.getStampedBuildInfo() : collection.getRedactedBuildInfo();
327 }
328
329 @Override
330 public ArtifactOwner getOwner() {
331 return owner;
332 }
333}