// Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.analysis; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.VisibleForTesting; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.test.TestProvider; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.profiler.AutoProfiler; import com.google.devtools.build.lib.skyframe.AspectValue; import com.google.devtools.build.lib.util.RegexFilter; import java.util.logging.Logger; import javax.annotation.Nullable; /** * A small static class containing utility methods for handling the inclusion of * extra top-level artifacts into the build. */ public final class TopLevelArtifactHelper { private static Logger logger = Logger.getLogger(TopLevelArtifactHelper.class.getName()); /** Set of {@link Artifact}s in an output group. */ @Immutable public static final class ArtifactsInOutputGroup { private final String outputGroup; private final boolean important; private final NestedSet artifacts; private ArtifactsInOutputGroup( String outputGroup, boolean important, NestedSet artifacts) { this.outputGroup = checkNotNull(outputGroup); this.important = important; this.artifacts = checkNotNull(artifacts); } public String getOutputGroup() { return outputGroup; } public NestedSet getArtifacts() { return artifacts; } /** Returns {@code true} if the user should know about this output group. */ public boolean areImportant() { return important; } } /** * The set of artifacts to build. * *

There are two kinds: the ones that the user cares about (e.g. files to build) and the ones * they don't (e.g. baseline coverage artifacts). The latter type doesn't get reported on various * outputs, e.g. on the console output listing the output artifacts of targets on the command * line. */ @Immutable public static final class ArtifactsToBuild { private NestedSet artifacts; private ArtifactsToBuild(NestedSet artifacts) { this.artifacts = checkNotNull(artifacts); } /** * Returns the artifacts that the user should know about. */ public NestedSet getImportantArtifacts() { NestedSetBuilder builder = new NestedSetBuilder<>(artifacts.getOrder()); for (ArtifactsInOutputGroup artifactsInOutputGroup : artifacts) { if (artifactsInOutputGroup.areImportant()) { builder.addTransitive(artifactsInOutputGroup.getArtifacts()); } } return builder.build(); } /** * Returns the actual set of artifacts that need to be built. */ public NestedSet getAllArtifacts() { NestedSetBuilder builder = new NestedSetBuilder<>(artifacts.getOrder()); for (ArtifactsInOutputGroup artifactsInOutputGroup : artifacts) { builder.addTransitive(artifactsInOutputGroup.getArtifacts()); } return builder.build(); } /** * Returns the set of all {@link Artifact}s grouped by their corresponding output group. * *

If an {@link Artifact} belongs to two or more output groups, it appears once in each * output group. */ public NestedSet getAllArtifactsByOutputGroup() { return artifacts; } } private TopLevelArtifactHelper() { // Prevent instantiation. } @VisibleForTesting public static ArtifactsToOwnerLabels makeTopLevelArtifactsToOwnerLabels( AnalysisResult analysisResult, Iterable aspects) { try (AutoProfiler ignored = AutoProfiler.logged("assigning owner labels", logger, 10)) { ArtifactsToOwnerLabels.Builder artifactsToOwnerLabelsBuilder = analysisResult.getTopLevelArtifactsToOwnerLabels().toBuilder(); TopLevelArtifactContext artifactContext = analysisResult.getTopLevelContext(); for (ConfiguredTarget target : analysisResult.getTargetsToBuild()) { addArtifactsWithOwnerLabel( getAllArtifactsToBuild(target, artifactContext).getAllArtifacts(), null, target.getLabel(), artifactsToOwnerLabelsBuilder); } for (AspectValue aspect : aspects) { addArtifactsWithOwnerLabel( getAllArtifactsToBuild(aspect, artifactContext).getAllArtifacts(), null, aspect.getLabel(), artifactsToOwnerLabelsBuilder); } if (analysisResult.getTargetsToTest() != null) { for (ConfiguredTarget target : analysisResult.getTargetsToTest()) { addArtifactsWithOwnerLabel( TestProvider.getTestStatusArtifacts(target), null, target.getLabel(), artifactsToOwnerLabelsBuilder); } } // TODO(dslomov): Artifacts to test from aspects? return artifactsToOwnerLabelsBuilder.build(); } } static void addArtifactsWithOwnerLabel( Iterable artifacts, @Nullable RegexFilter filter, Label ownerLabel, ArtifactsToOwnerLabels.Builder artifactsToOwnerLabelsBuilder) { for (Artifact artifact : artifacts) { if (filter == null || filter.isIncluded(artifact.getOwnerLabel().toString())) { artifactsToOwnerLabelsBuilder.addArtifact(artifact, ownerLabel); } } } /** * Returns all artifacts to build if this target is requested as a top-level target. The resulting * set includes the temps and either the files to compile, if * {@code context.compileOnly() == true}, or the files to run. * *

Calls to this method should generally return quickly; however, the runfiles computation can * be lazy, in which case it can be expensive on the first call. Subsequent calls may or may not * return the same {@code Iterable} instance. */ public static ArtifactsToBuild getAllArtifactsToBuild(TransitiveInfoCollection target, TopLevelArtifactContext context) { return getAllArtifactsToBuild( OutputGroupInfo.get(target), target.getProvider(FileProvider.class), context ); } public static ArtifactsToBuild getAllArtifactsToBuild( AspectValue aspectValue, TopLevelArtifactContext context) { ConfiguredAspect configuredAspect = aspectValue.getConfiguredAspect(); return getAllArtifactsToBuild( OutputGroupInfo.get(configuredAspect), configuredAspect.getProvider(FileProvider.class), context); } static ArtifactsToBuild getAllArtifactsToBuild( @Nullable OutputGroupInfo outputGroupInfo, @Nullable FileProvider fileProvider, TopLevelArtifactContext context) { NestedSetBuilder allBuilder = NestedSetBuilder.stableOrder(); for (String outputGroup : context.outputGroups()) { NestedSetBuilder results = NestedSetBuilder.stableOrder(); if (outputGroup.equals(OutputGroupInfo.DEFAULT) && fileProvider != null) { results.addTransitive(fileProvider.getFilesToBuild()); } if (outputGroupInfo != null) { results.addTransitive(outputGroupInfo.getOutputGroup(outputGroup)); } // Ignore output groups that have no artifacts. if (results.isEmpty()) { continue; } boolean isImportantGroup = !outputGroup.startsWith(OutputGroupInfo.HIDDEN_OUTPUT_GROUP_PREFIX); ArtifactsInOutputGroup artifacts = new ArtifactsInOutputGroup(outputGroup, isImportantGroup, results.build()); allBuilder.add(artifacts); } return new ArtifactsToBuild(allBuilder.build()); } }