blob: 5aed23d0f98b56ba11bf93d963a462b5957f0611 [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;
29import com.google.devtools.build.lib.analysis.config.BinTools;
Janak Ramakrishnanf27f4382015-06-04 19:50:15 +000030import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031import com.google.devtools.build.lib.events.EventHandler;
32import com.google.devtools.build.lib.events.StoredEventHandler;
33import com.google.devtools.build.lib.packages.Target;
34import com.google.devtools.build.lib.skyframe.BuildInfoCollectionValue;
Janak Ramakrishnanf27f4382015-06-04 19:50:15 +000035import com.google.devtools.build.lib.skyframe.PrecomputedValue;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010036import com.google.devtools.build.lib.skyframe.WorkspaceStatusValue;
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;
82 private EventHandler errorEventListener;
83 private SkyFunction.Environment skyframeEnv;
84 private Map<Artifact, String> artifacts;
85 private final BinTools binTools;
86
87 /**
88 * The list of actions registered by the configured target this analysis environment is
89 * responsible for. May get cleared out at the end of the analysis of said target.
90 */
Rumou Duan33bab462016-04-25 17:55:12 +000091 final List<ActionAnalysisMetadata> actions = new ArrayList<>();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010092
93 public CachingAnalysisEnvironment(ArtifactFactory artifactFactory,
94 ArtifactOwner owner, boolean isSystemEnv, boolean extendedSanityChecks,
95 EventHandler errorEventListener, SkyFunction.Environment env, boolean allowRegisteringActions,
96 BinTools binTools) {
97 this.artifactFactory = artifactFactory;
98 this.owner = Preconditions.checkNotNull(owner);
99 this.isSystemEnv = isSystemEnv;
100 this.extendedSanityChecks = extendedSanityChecks;
101 this.errorEventListener = errorEventListener;
102 this.skyframeEnv = env;
103 this.allowRegisteringActions = allowRegisteringActions;
Ulf Adams89eefd72015-09-23 08:00:43 +0000104 this.binTools = Preconditions.checkNotNull(binTools);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100105 middlemanFactory = new MiddlemanFactory(artifactFactory, this);
106 artifacts = new HashMap<>();
107 }
108
109 public void disable(Target target) {
110 if (!hasErrors() && allowRegisteringActions) {
111 verifyGeneratedArtifactHaveActions(target);
112 }
113 artifacts = null;
114 middlemanFactory = null;
115 enabled = false;
116 errorEventListener = null;
117 skyframeEnv = null;
118 }
119
Rumou Duan33bab462016-04-25 17:55:12 +0000120 private static StringBuilder shortDescription(ActionAnalysisMetadata action) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100121 if (action == null) {
122 return new StringBuilder("null Action");
123 }
124 return new StringBuilder()
125 .append(action.getClass().getName())
126 .append(' ')
127 .append(action.getMnemonic());
128 }
129
130 /**
131 * Sanity checks that all generated artifacts have a generating action.
132 * @param target for error reporting
133 */
134 public void verifyGeneratedArtifactHaveActions(Target target) {
135 Collection<String> orphanArtifacts = getOrphanArtifactMap().values();
136 List<String> checkedActions = null;
137 if (!orphanArtifacts.isEmpty()) {
138 checkedActions = Lists.newArrayListWithCapacity(actions.size());
Rumou Duan33bab462016-04-25 17:55:12 +0000139 for (ActionAnalysisMetadata action : actions) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100140 StringBuilder sb = shortDescription(action);
141 for (Artifact o : action.getOutputs()) {
142 sb.append("\n ");
143 sb.append(o.getExecPathString());
144 }
145 checkedActions.add(sb.toString());
146 }
147 throw new IllegalStateException(
148 String.format(
Alex Humesky14f79d52015-07-08 18:39:15 +0000149 "%s %s : These artifacts do not have a generating action:\n%s\n"
150 + "These actions were checked:\n%s\n",
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100151 target.getTargetKind(), target.getLabel(),
152 Joiner.on('\n').join(orphanArtifacts), Joiner.on('\n').join(checkedActions)));
153 }
154 }
155
156 @Override
157 public ImmutableSet<Artifact> getOrphanArtifacts() {
Ulf Adamsd68626a2016-02-01 14:58:45 +0000158 if (!allowRegisteringActions) {
159 return ImmutableSet.<Artifact>of();
160 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100161 return ImmutableSet.copyOf(getOrphanArtifactMap().keySet());
162 }
163
164 private Map<Artifact, String> getOrphanArtifactMap() {
165 // Construct this set to avoid poor performance under large --runs_per_test.
166 Set<Artifact> artifactsWithActions = new HashSet<>();
Rumou Duan33bab462016-04-25 17:55:12 +0000167 for (ActionAnalysisMetadata action : actions) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100168 // Don't bother checking that every Artifact only appears once; that test is performed
169 // elsewhere (see #testNonUniqueOutputs in ActionListenerIntegrationTest).
170 artifactsWithActions.addAll(action.getOutputs());
171 }
172 // The order of the artifacts.entrySet iteration is unspecified - we use a TreeMap here to
173 // guarantee that the return value of this method is deterministic.
Lukacs Berki8ae34f12015-04-10 14:54:19 +0000174 Map<Artifact, String> orphanArtifacts = new TreeMap<>(Artifact.EXEC_PATH_COMPARATOR);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100175 for (Map.Entry<Artifact, String> entry : artifacts.entrySet()) {
176 Artifact a = entry.getKey();
177 if (!a.isSourceArtifact() && !artifactsWithActions.contains(a)) {
178 orphanArtifacts.put(a, String.format("%s\n%s",
179 a.getExecPathString(), // uncovered artifact
180 entry.getValue())); // origin of creation
181 }
182 }
183 return orphanArtifacts;
184 }
185
186 @Override
187 public EventHandler getEventHandler() {
188 return errorEventListener;
189 }
190
191 @Override
192 public boolean hasErrors() {
193 // The system analysis environment never has errors.
194 if (isSystemEnv) {
195 return false;
196 }
197 Preconditions.checkState(enabled);
198 return ((StoredEventHandler) errorEventListener).hasErrors();
199 }
200
201 @Override
202 public MiddlemanFactory getMiddlemanFactory() {
203 Preconditions.checkState(enabled);
204 return middlemanFactory;
205 }
206
207 /**
208 * Keeps track of artifacts. We check that all of them have an owner when the environment is
209 * sealed (disable()). For performance reasons we only track the originating stacktrace when
210 * running with --experimental_extended_sanity_checks.
211 */
212 private Artifact trackArtifactAndOrigin(Artifact a, @Nullable Throwable e) {
213 if ((e != null) && !artifacts.containsKey(a)) {
214 StringWriter sw = new StringWriter();
215 e.printStackTrace(new PrintWriter(sw));
216 artifacts.put(a, sw.toString());
217 } else {
218 artifacts.put(a, "No origin, run with --experimental_extended_sanity_checks");
219 }
220 return a;
221 }
222
223 @Override
224 public Artifact getDerivedArtifact(PathFragment rootRelativePath, Root root) {
225 Preconditions.checkState(enabled);
226 return trackArtifactAndOrigin(
227 artifactFactory.getDerivedArtifact(rootRelativePath, root, getOwner()),
228 extendedSanityChecks ? new Throwable() : null);
229 }
230
231 @Override
Michael Thvedte3b1cb72016-02-08 23:32:27 +0000232 public Artifact getTreeArtifact(PathFragment rootRelativePath, Root root) {
233 Preconditions.checkState(enabled);
234 return trackArtifactAndOrigin(
235 artifactFactory.getTreeArtifact(rootRelativePath, root, getOwner()),
236 extendedSanityChecks ? new Throwable() : null);
237 }
238
239 @Override
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100240 public Artifact getFilesetArtifact(PathFragment rootRelativePath, Root root) {
241 Preconditions.checkState(enabled);
242 return trackArtifactAndOrigin(
243 artifactFactory.getFilesetArtifact(rootRelativePath, root, getOwner()),
244 extendedSanityChecks ? new Throwable() : null);
245 }
246
247 @Override
248 public Artifact getConstantMetadataArtifact(PathFragment rootRelativePath, Root root) {
249 return artifactFactory.getConstantMetadataArtifact(rootRelativePath, root, getOwner());
250 }
251
252 @Override
253 public Artifact getEmbeddedToolArtifact(String embeddedPath) {
254 Preconditions.checkState(enabled);
255 return binTools.getEmbeddedArtifact(embeddedPath, artifactFactory);
256 }
257
258 @Override
Rumou Duan33bab462016-04-25 17:55:12 +0000259 public void registerAction(ActionAnalysisMetadata... actions) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100260 Preconditions.checkState(enabled);
261 if (allowRegisteringActions) {
Ulf Adams07dba942015-03-05 14:47:37 +0000262 Collections.addAll(this.actions, actions);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100263 }
264 }
265
266 @Override
Rumou Duan33bab462016-04-25 17:55:12 +0000267 public ActionAnalysisMetadata getLocalGeneratingAction(Artifact artifact) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100268 Preconditions.checkState(allowRegisteringActions);
Rumou Duan33bab462016-04-25 17:55:12 +0000269 for (ActionAnalysisMetadata action : actions) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100270 if (action.getOutputs().contains(artifact)) {
271 return action;
272 }
273 }
274 return null;
275 }
276
277 @Override
Rumou Duan33bab462016-04-25 17:55:12 +0000278 public Collection<ActionAnalysisMetadata> getRegisteredActions() {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100279 return Collections.unmodifiableCollection(actions);
280 }
281
282 @Override
283 public SkyFunction.Environment getSkyframeEnv() {
284 return skyframeEnv;
285 }
286
287 @Override
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000288 public Artifact getStableWorkspaceStatusArtifact() throws InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100289 return ((WorkspaceStatusValue) skyframeEnv.getValue(WorkspaceStatusValue.SKY_KEY))
290 .getStableArtifact();
291 }
292
293 @Override
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000294 public Artifact getVolatileWorkspaceStatusArtifact() throws InterruptedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100295 return ((WorkspaceStatusValue) skyframeEnv.getValue(WorkspaceStatusValue.SKY_KEY))
296 .getVolatileArtifact();
297 }
298
Janak Ramakrishnanf27f4382015-06-04 19:50:15 +0000299 // See SkyframeBuildView#getWorkspaceStatusValues for the code that this method is attempting to
300 // verify.
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000301 private NullPointerException collectDebugInfoAndCrash(BuildInfoKey key, BuildConfiguration config)
302 throws InterruptedException {
Janak Ramakrishnanf27f4382015-06-04 19:50:15 +0000303 String debugInfo = key + " " + config;
304 Preconditions.checkState(skyframeEnv.valuesMissing(), debugInfo);
305 Map<BuildInfoKey, BuildInfoFactory> buildInfoFactories = Preconditions.checkNotNull(
306 PrecomputedValue.BUILD_INFO_FACTORIES.get(skyframeEnv), debugInfo);
307 BuildInfoFactory buildInfoFactory =
308 Preconditions.checkNotNull(buildInfoFactories.get(key), debugInfo);
309 Preconditions.checkState(buildInfoFactory.isEnabled(config), debugInfo);
310 throw new NullPointerException("BuildInfoCollectionValue shouldn't have been null");
311 }
312
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100313 @Override
Googler38ad0bf2016-07-01 05:00:14 +0000314 public ImmutableList<Artifact> getBuildInfo(
Janak Ramakrishnan3c0adb22016-08-15 21:54:55 +0000315 RuleContext ruleContext, BuildInfoKey key, BuildConfiguration config)
316 throws InterruptedException {
Googler38ad0bf2016-07-01 05:00:14 +0000317 boolean stamp = AnalysisUtils.isStampingEnabled(ruleContext, config);
Janak Ramakrishnanf27f4382015-06-04 19:50:15 +0000318 BuildInfoCollectionValue collectionValue =
319 (BuildInfoCollectionValue) skyframeEnv.getValue(BuildInfoCollectionValue.key(
Googler38ad0bf2016-07-01 05:00:14 +0000320 new BuildInfoCollectionValue.BuildInfoKeyAndConfig(key, config)));
Janak Ramakrishnanf27f4382015-06-04 19:50:15 +0000321 if (collectionValue == null) {
Googler38ad0bf2016-07-01 05:00:14 +0000322 throw collectDebugInfoAndCrash(key, config);
Janak Ramakrishnanf27f4382015-06-04 19:50:15 +0000323 }
324 BuildInfoCollection collection = collectionValue.getCollection();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100325 return stamp ? collection.getStampedBuildInfo() : collection.getRedactedBuildInfo();
326 }
327
328 @Override
329 public ArtifactOwner getOwner() {
330 return owner;
331 }
332}