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.analysis; |
| 15 | |
| 16 | import com.google.common.base.Joiner; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 17 | import com.google.common.collect.ImmutableList; |
| 18 | import com.google.common.collect.ImmutableSet; |
| 19 | import com.google.common.collect.Lists; |
Rumou Duan | 33bab46 | 2016-04-25 17:55:12 +0000 | [diff] [blame] | 20 | import com.google.devtools.build.lib.actions.ActionAnalysisMetadata; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 21 | import com.google.devtools.build.lib.actions.Artifact; |
| 22 | import com.google.devtools.build.lib.actions.ArtifactFactory; |
| 23 | import com.google.devtools.build.lib.actions.ArtifactOwner; |
| 24 | import com.google.devtools.build.lib.actions.MiddlemanFactory; |
| 25 | import com.google.devtools.build.lib.actions.Root; |
| 26 | import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoCollection; |
Janak Ramakrishnan | f27f438 | 2015-06-04 19:50:15 +0000 | [diff] [blame] | 27 | import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoFactory; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 28 | import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoFactory.BuildInfoKey; |
| 29 | import com.google.devtools.build.lib.analysis.config.BinTools; |
Janak Ramakrishnan | f27f438 | 2015-06-04 19:50:15 +0000 | [diff] [blame] | 30 | import com.google.devtools.build.lib.analysis.config.BuildConfiguration; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 31 | import com.google.devtools.build.lib.events.EventHandler; |
| 32 | import com.google.devtools.build.lib.events.StoredEventHandler; |
| 33 | import com.google.devtools.build.lib.packages.Target; |
| 34 | import com.google.devtools.build.lib.skyframe.BuildInfoCollectionValue; |
Janak Ramakrishnan | f27f438 | 2015-06-04 19:50:15 +0000 | [diff] [blame] | 35 | import com.google.devtools.build.lib.skyframe.PrecomputedValue; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 36 | import com.google.devtools.build.lib.skyframe.WorkspaceStatusValue; |
Mark Schaller | 6df8179 | 2015-12-10 18:47:47 +0000 | [diff] [blame] | 37 | import com.google.devtools.build.lib.util.Preconditions; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 38 | import com.google.devtools.build.lib.vfs.PathFragment; |
| 39 | import com.google.devtools.build.skyframe.SkyFunction; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 40 | import java.io.PrintWriter; |
| 41 | import java.io.StringWriter; |
| 42 | import java.util.ArrayList; |
| 43 | import java.util.Collection; |
| 44 | import java.util.Collections; |
| 45 | import java.util.HashMap; |
| 46 | import java.util.HashSet; |
| 47 | import java.util.List; |
| 48 | import java.util.Map; |
| 49 | import java.util.Set; |
| 50 | import java.util.TreeMap; |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 51 | import 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 | */ |
| 58 | public 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 Duan | 33bab46 | 2016-04-25 17:55:12 +0000 | [diff] [blame] | 91 | final List<ActionAnalysisMetadata> actions = new ArrayList<>(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 92 | |
| 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 Adams | 89eefd7 | 2015-09-23 08:00:43 +0000 | [diff] [blame] | 104 | this.binTools = Preconditions.checkNotNull(binTools); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 105 | 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 Duan | 33bab46 | 2016-04-25 17:55:12 +0000 | [diff] [blame] | 120 | private static StringBuilder shortDescription(ActionAnalysisMetadata action) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 121 | 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 Duan | 33bab46 | 2016-04-25 17:55:12 +0000 | [diff] [blame] | 139 | for (ActionAnalysisMetadata action : actions) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 140 | 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 Humesky | 14f79d5 | 2015-07-08 18:39:15 +0000 | [diff] [blame] | 149 | "%s %s : These artifacts do not have a generating action:\n%s\n" |
| 150 | + "These actions were checked:\n%s\n", |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 151 | 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 Adams | d68626a | 2016-02-01 14:58:45 +0000 | [diff] [blame] | 158 | if (!allowRegisteringActions) { |
| 159 | return ImmutableSet.<Artifact>of(); |
| 160 | } |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 161 | 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 Duan | 33bab46 | 2016-04-25 17:55:12 +0000 | [diff] [blame] | 167 | for (ActionAnalysisMetadata action : actions) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 168 | // 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 Berki | 8ae34f1 | 2015-04-10 14:54:19 +0000 | [diff] [blame] | 174 | Map<Artifact, String> orphanArtifacts = new TreeMap<>(Artifact.EXEC_PATH_COMPARATOR); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 175 | 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 Thvedt | e3b1cb7 | 2016-02-08 23:32:27 +0000 | [diff] [blame] | 232 | 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 Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 240 | 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 Duan | 33bab46 | 2016-04-25 17:55:12 +0000 | [diff] [blame] | 259 | public void registerAction(ActionAnalysisMetadata... actions) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 260 | Preconditions.checkState(enabled); |
| 261 | if (allowRegisteringActions) { |
Ulf Adams | 07dba94 | 2015-03-05 14:47:37 +0000 | [diff] [blame] | 262 | Collections.addAll(this.actions, actions); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 263 | } |
| 264 | } |
| 265 | |
| 266 | @Override |
Rumou Duan | 33bab46 | 2016-04-25 17:55:12 +0000 | [diff] [blame] | 267 | public ActionAnalysisMetadata getLocalGeneratingAction(Artifact artifact) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 268 | Preconditions.checkState(allowRegisteringActions); |
Rumou Duan | 33bab46 | 2016-04-25 17:55:12 +0000 | [diff] [blame] | 269 | for (ActionAnalysisMetadata action : actions) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 270 | if (action.getOutputs().contains(artifact)) { |
| 271 | return action; |
| 272 | } |
| 273 | } |
| 274 | return null; |
| 275 | } |
| 276 | |
| 277 | @Override |
Rumou Duan | 33bab46 | 2016-04-25 17:55:12 +0000 | [diff] [blame] | 278 | public Collection<ActionAnalysisMetadata> getRegisteredActions() { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 279 | return Collections.unmodifiableCollection(actions); |
| 280 | } |
| 281 | |
| 282 | @Override |
| 283 | public SkyFunction.Environment getSkyframeEnv() { |
| 284 | return skyframeEnv; |
| 285 | } |
| 286 | |
| 287 | @Override |
Janak Ramakrishnan | 3c0adb2 | 2016-08-15 21:54:55 +0000 | [diff] [blame] | 288 | public Artifact getStableWorkspaceStatusArtifact() throws InterruptedException { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 289 | return ((WorkspaceStatusValue) skyframeEnv.getValue(WorkspaceStatusValue.SKY_KEY)) |
| 290 | .getStableArtifact(); |
| 291 | } |
| 292 | |
| 293 | @Override |
Janak Ramakrishnan | 3c0adb2 | 2016-08-15 21:54:55 +0000 | [diff] [blame] | 294 | public Artifact getVolatileWorkspaceStatusArtifact() throws InterruptedException { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 295 | return ((WorkspaceStatusValue) skyframeEnv.getValue(WorkspaceStatusValue.SKY_KEY)) |
| 296 | .getVolatileArtifact(); |
| 297 | } |
| 298 | |
Janak Ramakrishnan | f27f438 | 2015-06-04 19:50:15 +0000 | [diff] [blame] | 299 | // See SkyframeBuildView#getWorkspaceStatusValues for the code that this method is attempting to |
| 300 | // verify. |
Janak Ramakrishnan | 3c0adb2 | 2016-08-15 21:54:55 +0000 | [diff] [blame] | 301 | private NullPointerException collectDebugInfoAndCrash(BuildInfoKey key, BuildConfiguration config) |
| 302 | throws InterruptedException { |
Janak Ramakrishnan | f27f438 | 2015-06-04 19:50:15 +0000 | [diff] [blame] | 303 | 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 Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 313 | @Override |
Googler | 38ad0bf | 2016-07-01 05:00:14 +0000 | [diff] [blame] | 314 | public ImmutableList<Artifact> getBuildInfo( |
Janak Ramakrishnan | 3c0adb2 | 2016-08-15 21:54:55 +0000 | [diff] [blame] | 315 | RuleContext ruleContext, BuildInfoKey key, BuildConfiguration config) |
| 316 | throws InterruptedException { |
Googler | 38ad0bf | 2016-07-01 05:00:14 +0000 | [diff] [blame] | 317 | boolean stamp = AnalysisUtils.isStampingEnabled(ruleContext, config); |
Janak Ramakrishnan | f27f438 | 2015-06-04 19:50:15 +0000 | [diff] [blame] | 318 | BuildInfoCollectionValue collectionValue = |
| 319 | (BuildInfoCollectionValue) skyframeEnv.getValue(BuildInfoCollectionValue.key( |
Googler | 38ad0bf | 2016-07-01 05:00:14 +0000 | [diff] [blame] | 320 | new BuildInfoCollectionValue.BuildInfoKeyAndConfig(key, config))); |
Janak Ramakrishnan | f27f438 | 2015-06-04 19:50:15 +0000 | [diff] [blame] | 321 | if (collectionValue == null) { |
Googler | 38ad0bf | 2016-07-01 05:00:14 +0000 | [diff] [blame] | 322 | throw collectDebugInfoAndCrash(key, config); |
Janak Ramakrishnan | f27f438 | 2015-06-04 19:50:15 +0000 | [diff] [blame] | 323 | } |
| 324 | BuildInfoCollection collection = collectionValue.getCollection(); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 325 | return stamp ? collection.getStampedBuildInfo() : collection.getRedactedBuildInfo(); |
| 326 | } |
| 327 | |
| 328 | @Override |
| 329 | public ArtifactOwner getOwner() { |
| 330 | return owner; |
| 331 | } |
| 332 | } |