blob: 3b6c73b26a0d5ef261260ef2df08cbea7f3f926e [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.
14
15package com.google.devtools.build.lib.analysis;
16
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +000017import static com.google.devtools.build.lib.syntax.EvalUtils.SKYLARK_COMPARATOR;
18
dslomovf9697342017-05-02 16:26:39 +020019import com.google.common.collect.ImmutableCollection;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010020import com.google.common.collect.ImmutableMap;
Alex Humesky152feb02016-06-20 19:43:11 +000021import com.google.common.collect.ImmutableSortedSet;
22import com.google.common.collect.Sets;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010023import com.google.devtools.build.lib.actions.Artifact;
ulfjack35625252017-08-08 19:45:46 +020024import com.google.devtools.build.lib.analysis.skylark.SkylarkRuleConfiguredTargetUtil;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010025import com.google.devtools.build.lib.collect.nestedset.NestedSet;
Lukacs Berki6916be22015-02-19 13:36:06 +000026import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
27import com.google.devtools.build.lib.collect.nestedset.Order;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010028import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
cparsons2d67cf92018-05-24 14:02:09 -070029import com.google.devtools.build.lib.packages.BuiltinProvider;
adonovan7202d102019-12-09 13:58:57 -080030import com.google.devtools.build.lib.packages.StructImpl;
cpeyser10b1a5f2018-02-15 09:37:15 -080031import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
cparsons2d67cf92018-05-24 14:02:09 -070032import com.google.devtools.build.lib.skylarkbuildapi.OutputGroupInfoApi;
Googlerd21a0d12019-11-21 13:52:30 -080033import com.google.devtools.build.lib.syntax.Depset;
Googlera9c93632019-11-13 10:48:07 -080034import com.google.devtools.build.lib.syntax.Dict;
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +000035import com.google.devtools.build.lib.syntax.EvalException;
adonovanf5262c52020-04-02 09:25:14 -070036import com.google.devtools.build.lib.syntax.Location;
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +000037import com.google.devtools.build.lib.syntax.SkylarkIndexable;
adonovan47bf59e2020-01-13 14:46:59 -080038import com.google.devtools.build.lib.syntax.Starlark;
Googler292a5c02019-11-25 14:53:08 -080039import com.google.devtools.build.lib.syntax.StarlarkIterable;
adonovan47bf59e2020-01-13 14:46:59 -080040import com.google.devtools.build.lib.syntax.StarlarkSemantics;
Dmitry Lomove2033b12015-08-19 16:57:49 +000041import java.util.HashSet;
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +000042import java.util.Iterator;
Dmitry Lomove2033b12015-08-19 16:57:49 +000043import java.util.List;
dslomovf9697342017-05-02 16:26:39 +020044import java.util.Map;
Dmitry Lomove2033b12015-08-19 16:57:49 +000045import java.util.Set;
Dmitry Lomove2033b12015-08-19 16:57:49 +000046import javax.annotation.Nullable;
47
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010048/**
Lukacs Berkic1f894e2015-02-17 13:00:26 +000049 * {@code ConfiguredTarget}s implementing this interface can provide artifacts that <b>can</b> be
50 * built when the target is mentioned on the command line (as opposed to being always built, like
51 * {@link com.google.devtools.build.lib.analysis.FileProvider})
52 *
dslomovde965ac2017-07-31 21:07:51 +020053 * <p>The artifacts are grouped into "output groups". Which output groups are built is controlled by
54 * the {@code --output_groups} undocumented command line option, which in turn is added to the
Lukacs Berkic1f894e2015-02-17 13:00:26 +000055 * command line at the discretion of the build command being run.
Lukacs Berki6916be22015-02-19 13:36:06 +000056 *
57 * <p>Output groups starting with an underscore are "not important". This means that artifacts built
58 * because such an output group is mentioned in a {@code --output_groups} command line option are
59 * not mentioned on the output.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010060 */
61@Immutable
cpeyser10b1a5f2018-02-15 09:37:15 -080062@AutoCodec
adonovan7202d102019-12-09 13:58:57 -080063public final class OutputGroupInfo extends StructImpl
Googler292a5c02019-11-25 14:53:08 -080064 implements SkylarkIndexable, StarlarkIterable<String>, OutputGroupInfoApi {
vladmos360fb4d2017-04-11 11:14:22 +000065 public static final String SKYLARK_NAME = "output_groups";
Lukacs Berki1b18ae92015-02-24 10:48:38 +000066
cparsons2d67cf92018-05-24 14:02:09 -070067 public static final OutputGroupInfoProvider SKYLARK_CONSTRUCTOR = new OutputGroupInfoProvider();
dslomovf9697342017-05-02 16:26:39 +020068
Lukacs Berki1b18ae92015-02-24 10:48:38 +000069 /**
70 * Prefix for output groups that are not reported to the user on the terminal output of Blaze when
71 * they are built.
72 */
Lukacs Berki6916be22015-02-19 13:36:06 +000073 public static final String HIDDEN_OUTPUT_GROUP_PREFIX = "_";
Lukacs Berki1b18ae92015-02-24 10:48:38 +000074
75 /**
Cal Peysereb856432016-06-22 14:25:36 +000076 * Suffix for output groups that are internal to bazel and may not be referenced from a filegroup.
77 */
78 public static final String INTERNAL_SUFFIX = "_INTERNAL_";
79
80 /**
Lukacs Berki1b18ae92015-02-24 10:48:38 +000081 * Building these artifacts only results in the compilation (and not e.g. linking) of the
82 * associated target. Mostly useful for C++, less so for e.g. Java.
83 */
lberki1dcd8f02018-04-04 03:13:55 -070084 public static final String FILES_TO_COMPILE = "compilation_outputs";
Lukacs Berki1b18ae92015-02-24 10:48:38 +000085
86 /**
87 * These artifacts are the direct requirements for compilation, but building these does not
88 * actually compile the target. Mostly useful when IDEs want Blaze to emit generated code so that
89 * they can do the compilation in their own way.
90 */
Cal Peysereb856432016-06-22 14:25:36 +000091 public static final String COMPILATION_PREREQUISITES =
92 "compilation_prerequisites" + INTERNAL_SUFFIX;
Lukacs Berki6916be22015-02-19 13:36:06 +000093
Lukacs Berki1b18ae92015-02-24 10:48:38 +000094 /**
95 * These files are built when a target is mentioned on the command line, but are not reported to
96 * the user. This is mostly runfiles, which is necessary because we don't want a target to
97 * successfully build if a file in its runfiles is broken.
98 */
Cal Peysereb856432016-06-22 14:25:36 +000099 public static final String HIDDEN_TOP_LEVEL =
100 HIDDEN_OUTPUT_GROUP_PREFIX + "hidden_top_level" + INTERNAL_SUFFIX;
Lukacs Berki1b18ae92015-02-24 10:48:38 +0000101
Lukacs Berkib9e51302015-02-25 09:47:29 +0000102 /**
ahumesky60205bb2019-10-14 13:50:00 -0700103 * This output group contains artifacts that are the outputs of validation actions. These actions
104 * should be run even if no other action depends on their outputs, therefore this output group is:
105 *
106 * <ul>
107 * <li>built even if <code>--output_groups</code> overrides the default output groups
108 * <li>not affected by the subtraction operation of <code>--output_groups</code> (i.e. <code>
109 * "--output_groups=-_validation"</code>)
110 * </ul>
111 *
112 * The only way to disable this output group is with <code>--run_validations=false</code>.
113 */
114 public static final String VALIDATION = HIDDEN_OUTPUT_GROUP_PREFIX + "validation";
115
116 /**
Lukacs Berkib9e51302015-02-25 09:47:29 +0000117 * Temporary files created during building a rule, for example, .i, .d and .s files for C++
118 * compilation.
119 *
120 * <p>This output group is somewhat special: it is always built, but it only contains files when
121 * the {@code --save_temps} command line option present. I'm not sure if this is to save RAM by
122 * not creating the associated actions and artifacts if we don't need them or just historical
123 * baggage.
124 */
Cal Peysereb856432016-06-22 14:25:36 +0000125 public static final String TEMP_FILES = "temp_files" + INTERNAL_SUFFIX;
Lukacs Berkib9e51302015-02-25 09:47:29 +0000126
Lukacs Berki97f47ba2015-02-25 10:38:04 +0000127 /**
Lukacs Berki91ec2742015-03-10 09:09:20 +0000128 * The default group of files built by a target when it is mentioned on the command line.
Lukacs Berki97f47ba2015-02-25 10:38:04 +0000129 */
130 public static final String DEFAULT = "default";
131
Michajlo Matijkiwc1d94502015-03-18 12:17:22 +0000132 /**
133 * The default set of OutputGroups we typically want to build.
134 */
ulfjackf0009962018-07-23 01:19:20 -0700135 public static final ImmutableSortedSet<String> DEFAULT_GROUPS =
136 ImmutableSortedSet.of(DEFAULT, TEMP_FILES, HIDDEN_TOP_LEVEL);
Michajlo Matijkiwc1d94502015-03-18 12:17:22 +0000137
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100138 private final ImmutableMap<String, NestedSet<Artifact>> outputGroups;
139
dslomov69c45f82017-12-14 11:15:43 -0500140 public OutputGroupInfo(ImmutableMap<String, NestedSet<Artifact>> outputGroups) {
adonovan7202d102019-12-09 13:58:57 -0800141 super(SKYLARK_CONSTRUCTOR, Location.BUILTIN);
Lukacs Berkic1f894e2015-02-17 13:00:26 +0000142 this.outputGroups = outputGroups;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100143 }
144
dslomovf9697342017-05-02 16:26:39 +0200145 @Nullable
janakr6d185b82020-03-30 14:08:01 -0700146 public static OutputGroupInfo get(ProviderCollection collection) {
147 return collection.get(SKYLARK_CONSTRUCTOR);
dslomovf9697342017-05-02 16:26:39 +0200148 }
149
Lukacs Berki6916be22015-02-19 13:36:06 +0000150 /** Return the artifacts in a particular output group.
151 *
152 * @return the artifacts in the output group with the given name. The return value is never null.
153 * If the specified output group is not present, the empty set is returned.
154 */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100155 public NestedSet<Artifact> getOutputGroup(String outputGroupName) {
Lukacs Berki6916be22015-02-19 13:36:06 +0000156 return outputGroups.containsKey(outputGroupName)
157 ? outputGroups.get(outputGroupName)
158 : NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100159 }
Dmitry Lomove2033b12015-08-19 16:57:49 +0000160
161 /**
162 * Merges output groups from two output providers. The set of output groups must be disjoint.
163 *
164 * @param providers providers to merge {@code this} with.
165 */
166 @Nullable
dslomov69c45f82017-12-14 11:15:43 -0500167 public static OutputGroupInfo merge(List<OutputGroupInfo> providers)
Dmitry Lomov9b2fc5c2016-11-11 11:18:48 +0000168 throws DuplicateException {
Dmitry Lomove2033b12015-08-19 16:57:49 +0000169 if (providers.size() == 0) {
170 return null;
171 }
172 if (providers.size() == 1) {
173 return providers.get(0);
174 }
175
176 ImmutableMap.Builder<String, NestedSet<Artifact>> resultBuilder = new ImmutableMap.Builder<>();
177 Set<String> seenGroups = new HashSet<>();
dslomov69c45f82017-12-14 11:15:43 -0500178 for (OutputGroupInfo provider : providers) {
Dmitry Lomove2033b12015-08-19 16:57:49 +0000179 for (String outputGroup : provider.outputGroups.keySet()) {
180 if (!seenGroups.add(outputGroup)) {
Dmitry Lomov9b2fc5c2016-11-11 11:18:48 +0000181 throw new DuplicateException(
182 "Output group " + outputGroup + " provided twice");
Dmitry Lomove2033b12015-08-19 16:57:49 +0000183 }
184
185 resultBuilder.put(outputGroup, provider.getOutputGroup(outputGroup));
186 }
187 }
dslomov69c45f82017-12-14 11:15:43 -0500188 return new OutputGroupInfo(resultBuilder.build());
Dmitry Lomove2033b12015-08-19 16:57:49 +0000189 }
Alex Humesky152feb02016-06-20 19:43:11 +0000190
ahumesky60205bb2019-10-14 13:50:00 -0700191 public static ImmutableSortedSet<String> determineOutputGroups(
192 List<String> outputGroups, boolean includeValidationOutputGroup) {
193 return determineOutputGroups(DEFAULT_GROUPS, outputGroups, includeValidationOutputGroup);
Alex Humesky152feb02016-06-20 19:43:11 +0000194 }
195
196 public static ImmutableSortedSet<String> determineOutputGroups(
ahumesky60205bb2019-10-14 13:50:00 -0700197 Set<String> defaultOutputGroups,
198 List<String> outputGroups,
199 boolean includeValidationOutputGroup) {
Alex Humesky152feb02016-06-20 19:43:11 +0000200
201 Set<String> current = Sets.newHashSet();
202
203 // If all of the requested output groups start with "+" or "-", then these are added or
204 // subtracted to the set of default output groups.
205 // If any of them don't start with "+" or "-", then the list of requested output groups
ahumesky60205bb2019-10-14 13:50:00 -0700206 // overrides the default set of output groups, except for the validation output group.
Alex Humesky152feb02016-06-20 19:43:11 +0000207 boolean addDefaultOutputGroups = true;
208 for (String outputGroup : outputGroups) {
209 if (!(outputGroup.startsWith("+") || outputGroup.startsWith("-"))) {
210 addDefaultOutputGroups = false;
211 break;
212 }
213 }
214 if (addDefaultOutputGroups) {
215 current.addAll(defaultOutputGroups);
216 }
217
218 for (String outputGroup : outputGroups) {
219 if (outputGroup.startsWith("+")) {
220 current.add(outputGroup.substring(1));
221 } else if (outputGroup.startsWith("-")) {
222 current.remove(outputGroup.substring(1));
223 } else {
224 current.add(outputGroup);
225 }
226 }
227
ahumesky60205bb2019-10-14 13:50:00 -0700228 // Add the validation output group regardless of the additions and subtractions above.
229 if (includeValidationOutputGroup) {
230 current.add(VALIDATION);
231 }
232
Alex Humesky152feb02016-06-20 19:43:11 +0000233 return ImmutableSortedSet.copyOf(current);
234 }
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +0000235
236 @Override
adonovan47bf59e2020-01-13 14:46:59 -0800237 public Object getIndex(StarlarkSemantics semantics, Object key) throws EvalException {
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +0000238 if (!(key instanceof String)) {
adonovan47bf59e2020-01-13 14:46:59 -0800239 throw Starlark.errorf(
240 "Output group names must be strings, got %s instead", Starlark.type(key));
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +0000241 }
242
243 NestedSet<Artifact> result = outputGroups.get(key);
244 if (result != null) {
Googler1a6a5a12019-11-22 11:54:01 -0800245 return Depset.of(Artifact.TYPE, result);
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +0000246 } else {
adonovan47bf59e2020-01-13 14:46:59 -0800247 throw Starlark.errorf("Output group %s not present", key);
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +0000248 }
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +0000249 }
250
251 @Override
adonovan47bf59e2020-01-13 14:46:59 -0800252 public boolean containsKey(StarlarkSemantics semantics, Object key) throws EvalException {
Dmitry Lomov8f45b7c2016-11-18 15:14:56 +0000253 return outputGroups.containsKey(key);
254 }
255
256 @Override
257 public Iterator<String> iterator() {
258 return SKYLARK_COMPARATOR.sortedCopy(outputGroups.keySet()).iterator();
259 }
dslomovf9697342017-05-02 16:26:39 +0200260
261 @Override
262 public Object getValue(String name) {
263 NestedSet<Artifact> result = outputGroups.get(name);
264 if (result == null) {
265 return null;
266 }
Googler1a6a5a12019-11-22 11:54:01 -0800267 return Depset.of(Artifact.TYPE, result);
dslomovf9697342017-05-02 16:26:39 +0200268 }
269
270 @Override
brandjond331fa72017-12-28 07:38:31 -0800271 public ImmutableCollection<String> getFieldNames() {
dslomovf9697342017-05-02 16:26:39 +0200272 return outputGroups.keySet();
273 }
274
cparsons2d67cf92018-05-24 14:02:09 -0700275 /**
276 * Provider implementation for {@link OutputGroupInfoApi.OutputGroupInfoApiProvider}.
277 */
278 public static class OutputGroupInfoProvider extends BuiltinProvider<OutputGroupInfo>
279 implements OutputGroupInfoApi.OutputGroupInfoApiProvider {
280 private OutputGroupInfoProvider() {
281 super("OutputGroupInfo", OutputGroupInfo.class);
dslomovf9697342017-05-02 16:26:39 +0200282 }
283
284 @Override
adonovan7891d3b2020-01-22 12:40:50 -0800285 public OutputGroupInfoApi constructor(Dict<?, ?> kwargs) throws EvalException {
cparsons2d67cf92018-05-24 14:02:09 -0700286 Map<String, Object> kwargsMap = kwargs.getContents(String.class, Object.class, "kwargs");
dslomovf9697342017-05-02 16:26:39 +0200287
288 ImmutableMap.Builder<String, NestedSet<Artifact>> builder = ImmutableMap.builder();
cparsons2d67cf92018-05-24 14:02:09 -0700289 for (Map.Entry<String, Object> entry : kwargsMap.entrySet()) {
vladmos97d67082017-07-13 14:54:03 +0200290 builder.put(
291 entry.getKey(),
292 SkylarkRuleConfiguredTargetUtil.convertToOutputGroupValue(
adonovan7891d3b2020-01-22 12:40:50 -0800293 entry.getKey(), entry.getValue()));
dslomovf9697342017-05-02 16:26:39 +0200294 }
dslomov69c45f82017-12-14 11:15:43 -0500295 return new OutputGroupInfo(builder.build());
dslomovf9697342017-05-02 16:26:39 +0200296 }
dslomovf9697342017-05-02 16:26:39 +0200297 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100298}