blob: d9ce45786356839f7e7e8ae71d70632c120098ce [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.rules;
16
17import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.DATA;
18import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST;
19import static com.google.devtools.build.lib.packages.Attribute.attr;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000020import static com.google.devtools.build.lib.packages.BuildType.LABEL;
21import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
22import static com.google.devtools.build.lib.packages.BuildType.LICENSE;
Francois-Rene Rideau48670f82015-03-19 17:23:35 +000023import static com.google.devtools.build.lib.syntax.SkylarkType.castMap;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000024import static com.google.devtools.build.lib.syntax.Type.BOOLEAN;
25import static com.google.devtools.build.lib.syntax.Type.INTEGER;
26import static com.google.devtools.build.lib.syntax.Type.STRING;
27import static com.google.devtools.build.lib.syntax.Type.STRING_LIST;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010028
29import com.google.common.annotations.VisibleForTesting;
30import com.google.common.cache.CacheBuilder;
31import com.google.common.cache.CacheLoader;
32import com.google.common.cache.LoadingCache;
33import com.google.common.collect.ImmutableList;
Lukacs Berki57892bd2015-04-13 14:42:28 +000034import com.google.common.collect.ImmutableMap;
Michael Staibcd48cd52016-01-15 20:11:11 +000035import com.google.common.collect.ImmutableSet;
Lukacs Berki3fbcc612015-10-05 12:30:56 +000036import com.google.devtools.build.lib.Constants;
Dmitry Lomov4be58932015-12-23 13:21:32 +000037import com.google.devtools.build.lib.actions.Artifact;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010038import com.google.devtools.build.lib.analysis.BaseRuleClasses;
Dmitry Lomov4be58932015-12-23 13:21:32 +000039import com.google.devtools.build.lib.analysis.OutputGroupProvider;
40import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010041import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
42import com.google.devtools.build.lib.analysis.config.RunUnder;
Lukacs Berki6e91eb92015-09-21 09:12:37 +000043import com.google.devtools.build.lib.cmdline.Label;
Lukacs Berkia6434362015-09-15 13:56:14 +000044import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
Dmitry Lomov4be58932015-12-23 13:21:32 +000045import com.google.devtools.build.lib.collect.nestedset.NestedSet;
46import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
47import com.google.devtools.build.lib.collect.nestedset.Order;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +000048import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010049import com.google.devtools.build.lib.events.Location;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +000050import com.google.devtools.build.lib.packages.AspectDefinition;
Dmitry Lomov6231d082015-11-02 17:17:20 +000051import com.google.devtools.build.lib.packages.AspectParameters;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010052import com.google.devtools.build.lib.packages.Attribute;
53import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
54import com.google.devtools.build.lib.packages.Attribute.LateBoundLabel;
Googler492472f2016-02-09 21:53:57 +000055import com.google.devtools.build.lib.packages.Attribute.LateBoundLabelList;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010056import com.google.devtools.build.lib.packages.AttributeMap;
57import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImplicitOutputsFunctionWithCallback;
58import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImplicitOutputsFunctionWithMap;
59import com.google.devtools.build.lib.packages.Package.NameConflictException;
60import com.google.devtools.build.lib.packages.PackageFactory;
61import com.google.devtools.build.lib.packages.PackageFactory.PackageContext;
62import com.google.devtools.build.lib.packages.Rule;
63import com.google.devtools.build.lib.packages.RuleClass;
64import com.google.devtools.build.lib.packages.RuleClass.Builder;
65import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
66import com.google.devtools.build.lib.packages.RuleFactory;
Mark Schalleree624452016-01-13 18:41:24 +000067import com.google.devtools.build.lib.packages.RuleFactory.BuildLangTypedAttributeValuesMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010068import com.google.devtools.build.lib.packages.RuleFactory.InvalidRuleException;
Dmitry Lomov82e03772015-11-30 12:13:22 +000069import com.google.devtools.build.lib.packages.SkylarkAspectClass;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010070import com.google.devtools.build.lib.packages.TargetUtils;
71import com.google.devtools.build.lib.packages.TestSize;
Dmitry Lomovace678e2015-12-16 15:10:20 +000072import com.google.devtools.build.lib.rules.SkylarkAttr.Descriptor;
Francois-Rene Rideauab049e02016-02-17 16:13:46 +000073import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
John Field585d1a02015-12-16 16:03:52 +000074import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature;
75import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature.Param;
76import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +000077import com.google.devtools.build.lib.syntax.BaseFunction;
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +000078import com.google.devtools.build.lib.syntax.BuiltinFunction;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010079import com.google.devtools.build.lib.syntax.ClassObject;
80import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject;
81import com.google.devtools.build.lib.syntax.Environment;
82import com.google.devtools.build.lib.syntax.Environment.NoSuchVariableException;
83import com.google.devtools.build.lib.syntax.EvalException;
84import com.google.devtools.build.lib.syntax.EvalUtils;
85import com.google.devtools.build.lib.syntax.FuncallExpression;
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +000086import com.google.devtools.build.lib.syntax.FunctionSignature;
Dmitry Lomov0b832ce2015-10-20 10:03:14 +000087import com.google.devtools.build.lib.syntax.Printer;
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000088import com.google.devtools.build.lib.syntax.Runtime;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010089import com.google.devtools.build.lib.syntax.SkylarkCallbackFunction;
Francois-Rene Rideauab049e02016-02-17 16:13:46 +000090import com.google.devtools.build.lib.syntax.SkylarkDict;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010091import com.google.devtools.build.lib.syntax.SkylarkList;
Dmitry Lomov4be58932015-12-23 13:21:32 +000092import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +000093import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000094import com.google.devtools.build.lib.syntax.Type;
95import com.google.devtools.build.lib.syntax.Type.ConversionException;
Dmitry Lomov7b599452015-11-26 10:07:32 +000096import com.google.devtools.build.lib.util.Pair;
Mark Schaller6df81792015-12-10 18:47:47 +000097import com.google.devtools.build.lib.util.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010098
Googler492472f2016-02-09 21:53:57 +000099import java.util.List;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100100import java.util.Map;
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000101import java.util.Set;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100102import java.util.concurrent.ExecutionException;
103
104/**
105 * A helper class to provide an easier API for Skylark rule definitions.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100106 */
107public class SkylarkRuleClassFunctions {
108
109 //TODO(bazel-team): proper enum support
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000110 @SkylarkSignature(name = "DATA_CFG", returnType = ConfigurationTransition.class,
Laurent Le Brund90db7f2015-04-01 14:16:30 +0000111 doc = "Experimental. Specifies a transition to the data configuration.")
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100112 private static final Object dataTransition = ConfigurationTransition.DATA;
113
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000114 @SkylarkSignature(name = "HOST_CFG", returnType = ConfigurationTransition.class,
Laurent Le Brund90db7f2015-04-01 14:16:30 +0000115 doc = "Specifies a transition to the host configuration.")
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100116 private static final Object hostTransition = ConfigurationTransition.HOST;
117
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100118 private static final LateBoundLabel<BuildConfiguration> RUN_UNDER =
119 new LateBoundLabel<BuildConfiguration>() {
120 @Override
Greg Estren33ec1912016-02-10 15:57:58 +0000121 public Label getDefault(Rule rule, AttributeMap attributes,
122 BuildConfiguration configuration) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100123 RunUnder runUnder = configuration.getRunUnder();
124 return runUnder == null ? null : runUnder.getLabel();
125 }
126 };
127
Googler492472f2016-02-09 21:53:57 +0000128 private static final Label COVERAGE_SUPPORT_LABEL =
129 Label.parseAbsoluteUnchecked("//tools/defaults:coverage");
130
131 private static final LateBoundLabelList<BuildConfiguration> GCOV =
132 new LateBoundLabelList<BuildConfiguration>(ImmutableList.of(COVERAGE_SUPPORT_LABEL)) {
133 @Override
Greg Estren33ec1912016-02-10 15:57:58 +0000134 public List<Label> getDefault(Rule rule, AttributeMap attributes,
135 BuildConfiguration configuration) {
Googler492472f2016-02-09 21:53:57 +0000136 return configuration.isCodeCoverageEnabled()
137 ? ImmutableList.copyOf(configuration.getGcovLabels())
138 : ImmutableList.<Label>of();
139 }
140 };
141
142 private static final LateBoundLabelList<BuildConfiguration> COVERAGE_REPORT_GENERATOR =
143 new LateBoundLabelList<BuildConfiguration>(ImmutableList.of(COVERAGE_SUPPORT_LABEL)) {
144 @Override
Greg Estren33ec1912016-02-10 15:57:58 +0000145 public List<Label> getDefault(Rule rule, AttributeMap attributes,
146 BuildConfiguration configuration) {
Googler492472f2016-02-09 21:53:57 +0000147 return configuration.isCodeCoverageEnabled()
148 ? ImmutableList.copyOf(configuration.getCoverageReportGeneratorLabels())
149 : ImmutableList.<Label>of();
150 }
151 };
152
153 private static final LateBoundLabelList<BuildConfiguration> COVERAGE_SUPPORT =
154 new LateBoundLabelList<BuildConfiguration>(ImmutableList.of(COVERAGE_SUPPORT_LABEL)) {
155 @Override
Greg Estren33ec1912016-02-10 15:57:58 +0000156 public List<Label> getDefault(Rule rule, AttributeMap attributes,
157 BuildConfiguration configuration) {
Googler492472f2016-02-09 21:53:57 +0000158 return configuration.isCodeCoverageEnabled()
159 ? ImmutableList.copyOf(configuration.getCoverageLabels())
160 : ImmutableList.<Label>of();
161 }
162 };
163
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100164 // TODO(bazel-team): Copied from ConfiguredRuleClassProvider for the transition from built-in
165 // rules to skylark extensions. Using the same instance would require a large refactoring.
166 // If we don't want to support old built-in rules and Skylark simultaneously
167 // (except for transition phase) it's probably OK.
168 private static LoadingCache<String, Label> labelCache =
169 CacheBuilder.newBuilder().build(new CacheLoader<String, Label>() {
170 @Override
171 public Label load(String from) throws Exception {
172 try {
173 return Label.parseAbsolute(from);
Lukacs Berkia6434362015-09-15 13:56:14 +0000174 } catch (LabelSyntaxException e) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100175 throw new Exception(from);
176 }
177 }
178 });
179
180 // TODO(bazel-team): Remove the code duplication (BaseRuleClasses and this class).
Florian Weikert70265ec2015-08-31 14:37:27 +0000181 /** Parent rule class for non-executable non-test Skylark rules. */
Mark Schaller4fa83ac2015-07-10 16:59:37 +0000182 public static final RuleClass baseRule =
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100183 BaseRuleClasses.commonCoreAndSkylarkAttributes(
184 new RuleClass.Builder("$base_rule", RuleClassType.ABSTRACT, true))
185 .add(attr("expect_failure", STRING))
186 .build();
187
Florian Weikert70265ec2015-08-31 14:37:27 +0000188 /** Parent rule class for executable non-test Skylark rules. */
189 public static final RuleClass binaryBaseRule =
190 new RuleClass.Builder("$binary_base_rule", RuleClassType.ABSTRACT, true, baseRule)
191 .add(
192 attr("args", STRING_LIST)
193 .nonconfigurable("policy decision: should be consistent across configurations"))
194 .add(attr("output_licenses", LICENSE))
195 .build();
196
Mark Schaller4fa83ac2015-07-10 16:59:37 +0000197 /** Parent rule class for test Skylark rules. */
198 public static final RuleClass testBaseRule =
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100199 new RuleClass.Builder("$test_base_rule", RuleClassType.ABSTRACT, true, baseRule)
200 .add(attr("size", STRING).value("medium").taggable()
201 .nonconfigurable("used in loading phase rule validation logic"))
202 .add(attr("timeout", STRING).taggable()
203 .nonconfigurable("used in loading phase rule validation logic").value(
204 new Attribute.ComputedDefault() {
205 @Override
206 public Object getDefault(AttributeMap rule) {
207 TestSize size = TestSize.getTestSize(rule.get("size", Type.STRING));
208 if (size != null) {
209 String timeout = size.getDefaultTimeout().toString();
210 if (timeout != null) {
211 return timeout;
212 }
213 }
214 return "illegal";
215 }
216 }))
217 .add(attr("flaky", BOOLEAN).value(false).taggable()
218 .nonconfigurable("taggable - called in Rule.getRuleTags"))
219 .add(attr("shard_count", INTEGER).value(-1))
220 .add(attr("local", BOOLEAN).value(false).taggable()
221 .nonconfigurable("policy decision: this should be consistent across configurations"))
Laurent Le Brun8d4ffc42015-06-24 15:21:50 +0000222 .add(attr("args", STRING_LIST)
223 .nonconfigurable("policy decision: should be consistent across configurations"))
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100224 .add(attr("$test_runtime", LABEL_LIST).cfg(HOST).value(ImmutableList.of(
Lukacs Berki3fbcc612015-10-05 12:30:56 +0000225 labelCache.getUnchecked(Constants.TOOLS_REPOSITORY + "//tools/test:runtime"))))
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100226 .add(attr(":run_under", LABEL).cfg(DATA).value(RUN_UNDER))
Googler492472f2016-02-09 21:53:57 +0000227 .add(attr(":gcov", LABEL_LIST).cfg(HOST).value(GCOV))
228 .add(attr(":coverage_support", LABEL_LIST).cfg(HOST).value(COVERAGE_SUPPORT))
229 .add(
230 attr(":coverage_report_generator", LABEL_LIST)
231 .cfg(HOST)
232 .value(COVERAGE_REPORT_GENERATOR))
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100233 .build();
234
235 /**
Han-Wen Nienhuysd39e1e12015-08-20 13:52:19 +0000236 * In native code, private values start with $.
237 * In Skylark, private values start with _, because of the grammar.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100238 */
239 private static String attributeToNative(String oldName, Location loc, boolean isLateBound)
240 throws EvalException {
241 if (oldName.isEmpty()) {
242 throw new EvalException(loc, "Attribute name cannot be empty");
243 }
244 if (isLateBound) {
245 if (oldName.charAt(0) != '_') {
246 throw new EvalException(loc, "When an attribute value is a function, "
247 + "the attribute must be private (start with '_')");
248 }
249 return ":" + oldName.substring(1);
250 }
251 if (oldName.charAt(0) == '_') {
252 return "$" + oldName.substring(1);
253 }
254 return oldName;
255 }
256
257 // TODO(bazel-team): implement attribute copy and other rule properties
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000258 @SkylarkSignature(name = "rule", doc =
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100259 "Creates a new rule. Store it in a global value, so that it can be loaded and called "
260 + "from BUILD files.",
Francois-Rene Rideau95b0d0c2015-04-22 16:52:13 +0000261 returnType = BaseFunction.class,
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000262 mandatoryPositionals = {
Francois-Rene Rideau95b0d0c2015-04-22 16:52:13 +0000263 @Param(name = "implementation", type = BaseFunction.class,
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000264 doc = "the function implementing this rule, must have exactly one parameter: "
David Chen24f2d992015-08-17 17:25:46 +0000265 + "<a href=\"ctx.html\">ctx</a>. The function is called during the analysis phase "
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000266 + "for each instance of the rule. It can access the attributes provided by the user. "
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000267 + "It must create actions to generate all the declared outputs.")
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100268 },
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000269 optionalPositionals = {
270 @Param(name = "test", type = Boolean.class, defaultValue = "False",
271 doc = "Whether this rule is a test rule. "
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000272 + "If True, the rule must end with <code>_test</code> (otherwise it must not), "
Laurent Le Brun9178bc22015-06-19 16:00:34 +0000273 + "and there must be an action that generates <code>ctx.outputs.executable</code>."),
Francois-Rene Rideauab049e02016-02-17 16:13:46 +0000274 @Param(name = "attrs", type = SkylarkDict.class, noneable = true, defaultValue = "None",
275 doc =
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000276 "dictionary to declare all the attributes of the rule. It maps from an attribute name "
David Chen24f2d992015-08-17 17:25:46 +0000277 + "to an attribute object (see <a href=\"attr.html\">attr</a> module). "
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000278 + "Attributes starting with <code>_</code> are private, and can be used to add "
Laurent Le Brun9ba067d2015-05-22 13:55:23 +0000279 + "an implicit dependency on a label. The attribute <code>name</code> is implicitly "
280 + "added and must not be specified. Attributes <code>visibility</code>, "
281 + "<code>deprecation</code>, <code>tags</code>, <code>testonly</code>, and "
282 + "<code>features</code> are implicitly added and might be overriden."),
283 // TODO(bazel-team): need to give the types of these builtin attributes
Francois-Rene Rideauab049e02016-02-17 16:13:46 +0000284 @Param(name = "outputs", type = SkylarkDict.class, callbackEnabled = true, noneable = true,
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000285 defaultValue = "None", doc = "outputs of this rule. "
286 + "It is a dictionary mapping from string to a template name. "
Brian Silvermane0adfc62015-08-03 09:04:31 +0000287 + "For example: <code>{\"ext\": \"%{name}.ext\"}</code>. <br>"
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000288 + "The dictionary key becomes an attribute in <code>ctx.outputs</code>. "
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000289 // TODO(bazel-team): Make doc more clear, wrt late-bound attributes.
290 + "It may also be a function (which receives <code>ctx.attr</code> as argument) "
291 + "returning such a dictionary."),
292 @Param(name = "executable", type = Boolean.class, defaultValue = "False",
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000293 doc = "whether this rule is marked as executable or not. If True, "
294 + "there must be an action that generates <code>ctx.outputs.executable</code>."),
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000295 @Param(name = "output_to_genfiles", type = Boolean.class, defaultValue = "False",
296 doc = "If true, the files will be generated in the genfiles directory instead of the "
Laurent Le Brun9c8b3702015-09-23 13:53:59 +0000297 + "bin directory. Unless you need it for compatibility with existing rules "
298 + "(e.g. when generating header files for C++), do not set this flag."),
Dmitry Lomovace678e2015-12-16 15:10:20 +0000299 @Param(name = "fragments", type = SkylarkList.class, generic1 = String.class,
300 defaultValue = "[]",
301 doc = "List of names of configuration fragments that the rule requires "
302 + "in target configuration."),
303 @Param(name = "host_fragments", type = SkylarkList.class, generic1 = String.class,
304 defaultValue = "[]",
305 doc = "List of names of configuration fragments that the rule requires "
306 + "in host configuration.")},
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000307 useAst = true, useEnvironment = true)
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000308 private static final BuiltinFunction rule = new BuiltinFunction("rule") {
Florian Weikert3f8aac92015-09-07 12:06:02 +0000309 @SuppressWarnings({"rawtypes", "unchecked"}) // castMap produces
310 // an Attribute.Builder instead of a Attribute.Builder<?> but it's OK.
311 public BaseFunction invoke(BaseFunction implementation, Boolean test, Object attrs,
312 Object implicitOutputs, Boolean executable, Boolean outputToGenfiles, SkylarkList fragments,
313 SkylarkList hostFragments, FuncallExpression ast, Environment funcallEnv)
314 throws EvalException, ConversionException {
315 funcallEnv.checkLoadingPhase("rule", ast.getLocation());
316 RuleClassType type = test ? RuleClassType.TEST : RuleClassType.NORMAL;
317 RuleClass parent = test ? testBaseRule : (executable ? binaryBaseRule : baseRule);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100318
Florian Weikert3f8aac92015-09-07 12:06:02 +0000319 // We'll set the name later, pass the empty string for now.
320 RuleClass.Builder builder = new RuleClass.Builder("", type, true, parent);
Dmitry Lomovace678e2015-12-16 15:10:20 +0000321 ImmutableList<Pair<String, SkylarkAttr.Descriptor>> attributes =
322 attrObjectToAttributesList(attrs, ast);
Florian Weikert3f8aac92015-09-07 12:06:02 +0000323 if (executable || test) {
Florian Weikerte96b0b82015-09-25 11:35:11 +0000324 addAttribute(
Dmitry Lomov7b599452015-11-26 10:07:32 +0000325 ast.getLocation(),
Florian Weikerte96b0b82015-09-25 11:35:11 +0000326 builder,
Florian Weikert3f8aac92015-09-07 12:06:02 +0000327 attr("$is_executable", BOOLEAN)
328 .value(true)
329 .nonconfigurable("Called from RunCommand.isExecutable, which takes a Target")
330 .build());
331 builder.setOutputsDefaultExecutable();
332 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100333
Florian Weikert3f8aac92015-09-07 12:06:02 +0000334 if (implicitOutputs != Runtime.NONE) {
335 if (implicitOutputs instanceof BaseFunction) {
336 BaseFunction func = (BaseFunction) implicitOutputs;
Dmitry Lomovace678e2015-12-16 15:10:20 +0000337 SkylarkCallbackFunction callback = new SkylarkCallbackFunction(func, ast, funcallEnv);
Florian Weikert3f8aac92015-09-07 12:06:02 +0000338 builder.setImplicitOutputsFunction(
339 new SkylarkImplicitOutputsFunctionWithCallback(callback, ast.getLocation()));
340 } else {
341 builder.setImplicitOutputsFunction(
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000342 new SkylarkImplicitOutputsFunctionWithMap(
Dmitry Lomovace678e2015-12-16 15:10:20 +0000343 ImmutableMap.copyOf(
344 castMap(
345 implicitOutputs,
346 String.class,
347 String.class,
348 "implicit outputs of the rule class"))));
Han-Wen Nienhuysd39e1e12015-08-20 13:52:19 +0000349 }
Florian Weikert3f8aac92015-09-07 12:06:02 +0000350 }
Laurent Le Brunc2efe452015-04-08 16:27:21 +0000351
Florian Weikert3f8aac92015-09-07 12:06:02 +0000352 if (outputToGenfiles) {
353 builder.setOutputToGenfiles();
354 }
Florian Weikert3f53fbb2015-08-07 22:25:50 +0000355
Michael Staibff66c192016-01-14 22:40:37 +0000356 builder.requiresConfigurationFragmentsBySkylarkModuleName(
357 fragments.getContents(String.class, "fragments"));
358 builder.requiresHostConfigurationFragmentsBySkylarkModuleName(
359 hostFragments.getContents(String.class, "host_fragments"));
Florian Weikert3f8aac92015-09-07 12:06:02 +0000360 builder.setConfiguredTargetFunction(implementation);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000361 builder.setRuleDefinitionEnvironment(funcallEnv);
Dmitry Lomovace678e2015-12-16 15:10:20 +0000362 return new RuleFunction(builder, type, attributes, ast.getLocation());
Florian Weikert3f8aac92015-09-07 12:06:02 +0000363 }
Dmitry Lomovace678e2015-12-16 15:10:20 +0000364 };
Florian Weikert3f8aac92015-09-07 12:06:02 +0000365
Dmitry Lomovace678e2015-12-16 15:10:20 +0000366 protected static ImmutableList<Pair<String, Descriptor>> attrObjectToAttributesList(
367 Object attrs, FuncallExpression ast) throws EvalException {
368 ImmutableList.Builder<Pair<String, Descriptor>> attributes = ImmutableList.builder();
369
370 if (attrs != Runtime.NONE) {
371 for (Map.Entry<String, Descriptor> attr :
372 castMap(attrs, String.class, Descriptor.class, "attrs").entrySet()) {
373 Descriptor attrDescriptor = attr.getValue();
374 String attrName =
375 attributeToNative(
376 attr.getKey(),
377 ast.getLocation(),
378 attrDescriptor.getAttributeBuilder().hasLateBoundValue());
379 attributes.add(Pair.of(attrName, attrDescriptor));
Florian Weikert3f8aac92015-09-07 12:06:02 +0000380 }
381 }
Dmitry Lomovace678e2015-12-16 15:10:20 +0000382 return attributes.build();
383 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100384
Dmitry Lomov7b599452015-11-26 10:07:32 +0000385 private static void addAttribute(
386 Location location, RuleClass.Builder builder, Attribute attribute) throws EvalException {
387 try {
388 builder.addOrOverrideAttribute(attribute);
389 } catch (IllegalArgumentException ex) {
390 throw new EvalException(location, ex);
391 }
392 }
393
394
Dmitry Lomov99f43722015-12-22 13:04:46 +0000395 @SkylarkSignature(name = "aspect", doc =
396 "Creates a new aspect. The result of this fucntion must be stored in a global value.",
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000397 returnType = SkylarkAspect.class,
Dmitry Lomov99f43722015-12-22 13:04:46 +0000398 mandatoryPositionals = {
399 @Param(name = "implementation", type = BaseFunction.class,
400 doc = "the function implementing this aspect. Must have two parameters: "
401 + "<a href=\"Target.html\">Target</a> (the target to which the aspect is applied) and"
402 + "<a href=\"ctx.html\">ctx</a>. Attributes of the target are available via ctx.rule "
403 + " field. The function is called during the analysis phase for each application of "
404 + "an aspect to a target."
405 ),
406 },
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000407 optionalPositionals = {
Dmitry Lomov99f43722015-12-22 13:04:46 +0000408 @Param(name = "attr_aspects", type = SkylarkList.class, generic1 = String.class,
409 defaultValue = "[]",
410 doc = "List of attribute names. The aspect propagates along dependencies specified by "
411 + " attributes of a target with this name"
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000412 ),
Francois-Rene Rideauab049e02016-02-17 16:13:46 +0000413 @Param(name = "attrs", type = SkylarkDict.class, noneable = true, defaultValue = "None",
Dmitry Lomov99f43722015-12-22 13:04:46 +0000414 doc = "dictionary to declare all the attributes of the aspect. "
415 + "It maps from an attribute name to an attribute object "
416 + "(see <a href=\"attr.html\">attr</a> module). "
417 + "Aspect attributes are available to implementation function as fields of ctx parameter. "
418 + "All aspect attributes must be private, so their names must start with <code>_</code>. "
419 + "All aspect attributes must be have default values, and be of type "
420 + "<code>label</code> or <code>label_list</code>"
Michael Staibcd48cd52016-01-15 20:11:11 +0000421 ),
422 @Param(
423 name = "fragments",
424 type = SkylarkList.class,
425 generic1 = String.class,
426 defaultValue = "[]",
427 doc =
428 "List of names of configuration fragments that the aspect requires "
429 + "in target configuration."
430 ),
431 @Param(
432 name = "host_fragments",
433 type = SkylarkList.class,
434 generic1 = String.class,
435 defaultValue = "[]",
436 doc =
437 "List of names of configuration fragments that the aspect requires "
438 + "in host configuration."
Dmitry Lomov99f43722015-12-22 13:04:46 +0000439 )
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000440 },
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000441 useEnvironment = true,
442 useAst = true
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000443 )
444 private static final BuiltinFunction aspect =
445 new BuiltinFunction("aspect") {
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000446 public SkylarkAspect invoke(
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000447 BaseFunction implementation,
448 SkylarkList attributeAspects,
Dmitry Lomovace678e2015-12-16 15:10:20 +0000449 Object attrs,
Michael Staibcd48cd52016-01-15 20:11:11 +0000450 SkylarkList fragments,
451 SkylarkList hostFragments,
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000452 FuncallExpression ast,
Dmitry Lomovace678e2015-12-16 15:10:20 +0000453 Environment funcallEnv)
454 throws EvalException {
455 ImmutableList.Builder<String> attrAspects = ImmutableList.builder();
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000456 for (Object attributeAspect : attributeAspects) {
Dmitry Lomov975fa322016-02-01 15:43:44 +0000457 String attrName = STRING.convert(attributeAspect, "attr_aspects");
458 if (!attrName.startsWith("_")) {
459 attrAspects.add(attrName);
460 } else {
461 // Implicit attribute names mean ether implicit or late-bound attributes
462 // (``$attr`` or ``:attr``). Depend on both.
463 attrAspects.add(attributeToNative(attrName, location, false));
464 attrAspects.add(attributeToNative(attrName, location, true));
465 }
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000466 }
467
Dmitry Lomovace678e2015-12-16 15:10:20 +0000468 ImmutableList<Pair<String, SkylarkAttr.Descriptor>> attributes =
469 attrObjectToAttributesList(attrs, ast);
470 for (Pair<String, Descriptor> attribute : attributes) {
471 String nativeName = attribute.getFirst();
472 if (!Attribute.isImplicit(nativeName)) {
473 throw new EvalException(
474 ast.getLocation(),
475 String.format(
476 "Aspect attribute '%s' must be implicit (its name should start with '_').",
477 nativeName));
478 }
479
480 String skylarkName = "_" + nativeName.substring(1);
481
482 if (!attribute.getSecond().getAttributeBuilder().isValueSet()) {
483 throw new EvalException(
484 ast.getLocation(),
485 String.format("Aspect attribute '%s' has no default value.", skylarkName));
486 }
487 }
488
Michael Staibcd48cd52016-01-15 20:11:11 +0000489 return new SkylarkAspect(
490 implementation,
491 attrAspects.build(),
492 attributes,
493 ImmutableSet.copyOf(fragments.getContents(String.class, "fragments")),
494 ImmutableSet.copyOf(hostFragments.getContents(String.class, "host_fragments")),
495 funcallEnv);
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000496 }
497 };
498
499
Francois-Rene Rideau93ed7f12015-10-20 15:39:33 +0000500 /** The implementation for the magic function "rule" that creates Skylark rule classes */
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000501 public static final class RuleFunction extends BaseFunction {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000502 private RuleClass.Builder builder;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100503
Dmitry Lomov7b599452015-11-26 10:07:32 +0000504 private RuleClass ruleClass;
505 private final RuleClassType type;
506 private ImmutableList<Pair<String, SkylarkAttr.Descriptor>> attributes;
507 private final Location definitionLocation;
508 private Label skylarkLabel;
509
510 public RuleFunction(Builder builder, RuleClassType type,
511 ImmutableList<Pair<String, SkylarkAttr.Descriptor>> attributes,
512 Location definitionLocation) {
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000513 super("rule", FunctionSignature.KWARGS);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100514 this.builder = builder;
515 this.type = type;
Dmitry Lomov7b599452015-11-26 10:07:32 +0000516 this.attributes = attributes;
517 this.definitionLocation = definitionLocation;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100518 }
519
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000520 @Override
Francois-Rene Rideau76023b92015-04-17 15:31:59 +0000521 @SuppressWarnings("unchecked") // the magic hidden $pkg_context variable is guaranteed
522 // to be a PackageContext
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000523 public Object call(Object[] args, FuncallExpression ast, Environment env)
524 throws EvalException, InterruptedException, ConversionException {
Francois-Rene Rideau3a8ddcc2015-08-26 22:30:38 +0000525 env.checkLoadingPhase(getName(), ast.getLocation());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100526 try {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000527 if (ruleClass == null) {
Francois-Rene Rideaua8352e92015-06-30 16:59:42 +0000528 throw new EvalException(ast.getLocation(),
529 "Invalid rule class hasn't been exported by a Skylark file");
530 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100531 PackageContext pkgContext = (PackageContext) env.lookup(PackageFactory.PKG_CONTEXT);
Mark Schalleree624452016-01-13 18:41:24 +0000532 BuildLangTypedAttributeValuesMap attributeValues =
533 new BuildLangTypedAttributeValuesMap((Map<String, Object>) args[0]);
534 return RuleFactory.createAndAddRule(pkgContext, ruleClass, attributeValues, ast, env);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100535 } catch (InvalidRuleException | NameConflictException | NoSuchVariableException e) {
536 throw new EvalException(ast.getLocation(), e.getMessage());
537 }
538 }
539
Francois-Rene Rideaua8352e92015-06-30 16:59:42 +0000540 /**
541 * Export a RuleFunction from a Skylark file with a given name.
542 */
Dmitry Lomov7b599452015-11-26 10:07:32 +0000543 void export(Label skylarkLabel, String ruleClassName) throws EvalException {
544 Preconditions.checkState(ruleClass == null && builder != null);
John Fielda97e17f2015-11-13 02:19:52 +0000545 this.skylarkLabel = skylarkLabel;
Dmitry Lomov7b599452015-11-26 10:07:32 +0000546 if (type == RuleClassType.TEST != TargetUtils.isTestRuleName(ruleClassName)) {
547 throw new EvalException(definitionLocation, "Invalid rule class name '" + ruleClassName
548 + "', test rule class names must end with '_test' and other rule classes must not");
549 }
550 for (Pair<String, SkylarkAttr.Descriptor> attribute : attributes) {
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000551 SkylarkAttr.Descriptor descriptor = attribute.getSecond();
552 Attribute.Builder<?> attributeBuilder = descriptor.getAttributeBuilder();
553 for (SkylarkAspect skylarkAspect : descriptor.getAspects()) {
554 if (!skylarkAspect.isExported()) {
555 throw new EvalException(definitionLocation,
556 "All aspects applied to rule dependencies must be top-level values");
557 }
Dmitry Lomov82e03772015-11-30 12:13:22 +0000558 attributeBuilder.aspect(skylarkAspect.getAspectClass());
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000559 }
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000560
Dmitry Lomov7b599452015-11-26 10:07:32 +0000561 addAttribute(definitionLocation, builder,
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000562 descriptor.getAttributeBuilder().build(attribute.getFirst()));
Dmitry Lomov7b599452015-11-26 10:07:32 +0000563 }
564 this.ruleClass = builder.build(ruleClassName);
565
566 this.builder = null;
567 this.attributes = null;
Francois-Rene Rideaua8352e92015-06-30 16:59:42 +0000568 }
569
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100570 @VisibleForTesting
Dmitry Lomov7b599452015-11-26 10:07:32 +0000571 public RuleClass getRuleClass() {
572 Preconditions.checkState(ruleClass != null && builder == null);
573 return ruleClass;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100574 }
575 }
576
Dmitry Lomov7b599452015-11-26 10:07:32 +0000577 public static void exportRuleFunctionsAndAspects(Environment env, Label skylarkLabel)
578 throws EvalException {
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000579 Set<String> globalNames = env.getGlobals().getDirectVariableNames();
580
581 // Export aspects first since rules can depend on aspects.
582 for (String name : globalNames) {
583 Object value;
584 try {
585 value = env.lookup(name);
586 } catch (NoSuchVariableException e) {
587 throw new AssertionError(e);
588 }
589 if (value instanceof SkylarkAspect) {
590 SkylarkAspect skylarkAspect = (SkylarkAspect) value;
591 if (!skylarkAspect.isExported()) {
592 skylarkAspect.export(skylarkLabel, name);
593 }
594 }
595 }
596
597 for (String name : globalNames) {
Francois-Rene Rideaua8352e92015-06-30 16:59:42 +0000598 try {
599 Object value = env.lookup(name);
600 if (value instanceof RuleFunction) {
601 RuleFunction function = (RuleFunction) value;
John Fielda97e17f2015-11-13 02:19:52 +0000602 if (function.skylarkLabel == null) {
603 function.export(skylarkLabel, name);
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000604 }
605 }
Francois-Rene Rideaua8352e92015-06-30 16:59:42 +0000606 } catch (NoSuchVariableException e) {
607 throw new AssertionError(e);
608 }
609 }
610 }
611
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000612 @SkylarkSignature(name = "Label", doc = "Creates a Label referring to a BUILD target. Use "
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100613 + "this function only when you want to give a default value for the label attributes. "
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000614 + "The argument must refer to an absolute label. "
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100615 + "Example: <br><pre class=language-python>Label(\"//tools:default\")</pre>",
616 returnType = Label.class,
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000617 mandatoryPositionals = {@Param(name = "label_string", type = String.class,
618 doc = "the label string")},
619 useLocation = true)
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000620 private static final BuiltinFunction label = new BuiltinFunction("Label") {
621 public Label invoke(String labelString,
622 Location loc) throws EvalException, ConversionException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100623 try {
624 return labelCache.get(labelString);
625 } catch (ExecutionException e) {
626 throw new EvalException(loc, "Illegal absolute label syntax: " + labelString);
627 }
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000628 }
629 };
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100630
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000631 @SkylarkSignature(name = "FileType",
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100632 doc = "Creates a file filter from a list of strings. For example, to match files ending "
633 + "with .cc or .cpp, use: <pre class=language-python>FileType([\".cc\", \".cpp\"])</pre>",
634 returnType = SkylarkFileType.class,
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000635 mandatoryPositionals = {
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000636 @Param(name = "types", type = SkylarkList.class, generic1 = String.class, defaultValue = "[]",
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100637 doc = "a list of the accepted file extensions")})
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000638 private static final BuiltinFunction fileType = new BuiltinFunction("FileType") {
Francois-Rene Rideau93ed7f12015-10-20 15:39:33 +0000639 public SkylarkFileType invoke(SkylarkList types) throws EvalException {
640 return SkylarkFileType.of(types.getContents(String.class, "types"));
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000641 }
642 };
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100643
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000644 @SkylarkSignature(name = "to_proto",
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100645 doc = "Creates a text message from the struct parameter. This method only works if all "
646 + "struct elements (recursively) are strings, ints, booleans, other structs or a "
647 + "list of these types. Quotes and new lines in strings are escaped. "
648 + "Examples:<br><pre class=language-python>"
649 + "struct(key=123).to_proto()\n# key: 123\n\n"
650 + "struct(key=True).to_proto()\n# key: true\n\n"
651 + "struct(key=[1, 2, 3]).to_proto()\n# key: 1\n# key: 2\n# key: 3\n\n"
652 + "struct(key='text').to_proto()\n# key: \"text\"\n\n"
653 + "struct(key=struct(inner_key='text')).to_proto()\n"
654 + "# key {\n# inner_key: \"text\"\n# }\n\n"
655 + "struct(key=[struct(inner_key=1), struct(inner_key=2)]).to_proto()\n"
656 + "# key {\n# inner_key: 1\n# }\n# key {\n# inner_key: 2\n# }\n\n"
657 + "struct(key=struct(inner_key=struct(inner_inner_key='text'))).to_proto()\n"
658 + "# key {\n# inner_key {\n# inner_inner_key: \"text\"\n# }\n# }\n</pre>",
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000659 objectType = SkylarkClassObject.class, returnType = String.class,
660 mandatoryPositionals = {
661 // TODO(bazel-team): shouldn't we accept any ClassObject?
662 @Param(name = "self", type = SkylarkClassObject.class,
663 doc = "this struct")},
664 useLocation = true)
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000665 private static final BuiltinFunction toProto = new BuiltinFunction("to_proto") {
666 public String invoke(SkylarkClassObject self, Location loc) throws EvalException {
667 StringBuilder sb = new StringBuilder();
668 printTextMessage(self, sb, 0, loc);
669 return sb.toString();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100670 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100671
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000672 private void printTextMessage(ClassObject object, StringBuilder sb,
673 int indent, Location loc) throws EvalException {
674 for (String key : object.getKeys()) {
675 printTextMessage(key, object.getValue(key), sb, indent, loc);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100676 }
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000677 }
678
679 private void printSimpleTextMessage(String key, Object value, StringBuilder sb,
680 int indent, Location loc, String container) throws EvalException {
681 if (value instanceof ClassObject) {
682 print(sb, key + " {", indent);
683 printTextMessage((ClassObject) value, sb, indent + 1, loc);
684 print(sb, "}", indent);
685 } else if (value instanceof String) {
686 print(sb, key + ": \"" + escape((String) value) + "\"", indent);
687 } else if (value instanceof Integer) {
688 print(sb, key + ": " + value, indent);
689 } else if (value instanceof Boolean) {
690 // We're relying on the fact that Java converts Booleans to Strings in the same way
691 // as the protocol buffers do.
692 print(sb, key + ": " + value, indent);
693 } else {
694 throw new EvalException(loc,
695 "Invalid text format, expected a struct, a string, a bool, or an int but got a "
696 + EvalUtils.getDataTypeName(value) + " for " + container + " '" + key + "'");
697 }
698 }
699
700 private void printTextMessage(String key, Object value, StringBuilder sb,
701 int indent, Location loc) throws EvalException {
702 if (value instanceof SkylarkList) {
703 for (Object item : ((SkylarkList) value)) {
704 // TODO(bazel-team): There should be some constraint on the fields of the structs
705 // in the same list but we ignore that for now.
706 printSimpleTextMessage(key, item, sb, indent, loc, "list element in struct field");
707 }
708 } else {
Francois-Rene Rideau93ed7f12015-10-20 15:39:33 +0000709 printSimpleTextMessage(key, value, sb, indent, loc, "struct field");
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000710 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100711 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100712
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000713 private String escape(String string) {
714 // TODO(bazel-team): use guava's SourceCodeEscapers when it's released.
715 return string.replace("\"", "\\\"").replace("\n", "\\n");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100716 }
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000717
718 private void print(StringBuilder sb, String text, int indent) {
719 for (int i = 0; i < indent; i++) {
720 sb.append(" ");
721 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100722 sb.append(text);
723 sb.append("\n");
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000724 }
725 };
726
Dmitry Lomov4be58932015-12-23 13:21:32 +0000727 @SkylarkSignature(name = "output_group",
728 documented = false, // TODO(dslomov): document.
729 objectType = TransitiveInfoCollection.class,
730 returnType = SkylarkNestedSet.class,
731 mandatoryPositionals = {
732 @Param(name = "self", type = TransitiveInfoCollection.class, doc =
733 "this target"
734 ),
735 @Param(name = "group_name", type = String.class, doc =
736 "Output group name"
737 )
738 }
739 )
740 public static final BuiltinFunction output_group = new BuiltinFunction("output_group") {
741 public SkylarkNestedSet invoke(TransitiveInfoCollection self, String group) {
742 OutputGroupProvider provider = self.getProvider(OutputGroupProvider.class);
743 NestedSet<Artifact> result = provider != null
744 ? provider.getOutputGroup(group)
745 : NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER);
746 return SkylarkNestedSet.of(Artifact.class, result);
747 }
748 };
749
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000750 static {
751 SkylarkSignatureProcessor.configureSkylarkFunctions(SkylarkRuleClassFunctions.class);
752 }
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000753
754 /**
755 * A Skylark value that is a result of 'aspect(..)' function call.
756 */
Francois-Rene Rideauab049e02016-02-17 16:13:46 +0000757 @SkylarkModule(name = "aspect", doc = "", documented = false)
Dmitry Lomov82e03772015-11-30 12:13:22 +0000758 public static final class SkylarkAspect implements SkylarkValue {
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000759 private final BaseFunction implementation;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000760 private final ImmutableList<String> attributeAspects;
Dmitry Lomovace678e2015-12-16 15:10:20 +0000761 private final ImmutableList<Pair<String, Descriptor>> attributes;
Michael Staibcd48cd52016-01-15 20:11:11 +0000762 private final ImmutableSet<String> fragments;
763 private final ImmutableSet<String> hostFragments;
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000764 private final Environment funcallEnv;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000765 private Exported exported;
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000766
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000767 public SkylarkAspect(
768 BaseFunction implementation,
769 ImmutableList<String> attributeAspects,
Dmitry Lomovace678e2015-12-16 15:10:20 +0000770 ImmutableList<Pair<String, Descriptor>> attributes,
Michael Staibcd48cd52016-01-15 20:11:11 +0000771 ImmutableSet<String> fragments,
772 ImmutableSet<String> hostFragments,
Dmitry Lomovace678e2015-12-16 15:10:20 +0000773 Environment funcallEnv) {
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000774 this.implementation = implementation;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000775 this.attributeAspects = attributeAspects;
Dmitry Lomovace678e2015-12-16 15:10:20 +0000776 this.attributes = attributes;
Michael Staibcd48cd52016-01-15 20:11:11 +0000777 this.fragments = fragments;
778 this.hostFragments = hostFragments;
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000779 this.funcallEnv = funcallEnv;
780 }
781
782 public BaseFunction getImplementation() {
783 return implementation;
784 }
785
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000786 public ImmutableList<String> getAttributeAspects() {
787 return attributeAspects;
788 }
789
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000790 public Environment getFuncallEnv() {
791 return funcallEnv;
792 }
793
Dmitry Lomovace678e2015-12-16 15:10:20 +0000794 public ImmutableList<Pair<String, Descriptor>> getAttributes() {
795 return attributes;
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000796 }
797
Michael Staibcd48cd52016-01-15 20:11:11 +0000798 /**
799 * Gets the set of configuration fragment names needed in the target configuration.
800 */
801 public ImmutableSet<String> getFragments() {
802 return fragments;
803 }
804
805 /**
806 * Gets the set of configuration fragment names needed in the host configuration.
807 */
808 public ImmutableSet<String> getHostFragments() {
809 return hostFragments;
810 }
811
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000812 @Override
813 public boolean isImmutable() {
814 return implementation.isImmutable();
815 }
816
817 @Override
818 public void write(Appendable buffer, char quotationMark) {
819 Printer.append(buffer, "Aspect:");
820 implementation.write(buffer, quotationMark);
821 }
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000822
823 public String getName() {
Dmitry Lomov82e03772015-11-30 12:13:22 +0000824 return getAspectClass().getName();
825 }
826
827 public SkylarkAspectClass getAspectClass() {
828 Preconditions.checkState(isExported());
829 return new SkylarkAspectClassImpl(this);
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000830 }
831
John Fielda97e17f2015-11-13 02:19:52 +0000832 void export(Label extensionLabel, String name) {
833 this.exported = new Exported(extensionLabel, name);
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000834 }
835
836 public boolean isExported() {
837 return exported != null;
838 }
839
John Fielda97e17f2015-11-13 02:19:52 +0000840 private Label getExtensionLabel() {
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000841 Preconditions.checkArgument(isExported());
John Fielda97e17f2015-11-13 02:19:52 +0000842 return exported.extensionLabel;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000843 }
844
845 private String getExportedName() {
846 Preconditions.checkArgument(isExported());
847 return exported.name;
848 }
849
850 @Immutable
851 private static class Exported {
John Fielda97e17f2015-11-13 02:19:52 +0000852 private final Label extensionLabel;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000853 private final String name;
854
John Fielda97e17f2015-11-13 02:19:52 +0000855 public Exported(Label extensionLabel, String name) {
856 this.extensionLabel = extensionLabel;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000857 this.name = name;
858 }
859
John Fielda97e17f2015-11-13 02:19:52 +0000860 @Override
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000861 public String toString() {
John Fielda97e17f2015-11-13 02:19:52 +0000862 return extensionLabel.toString() + "%" + name;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000863 }
864 }
865 }
866
867 /**
868 * Implementation of an aspect class defined in Skylark.
869 */
870 @Immutable
Dmitry Lomov82e03772015-11-30 12:13:22 +0000871 private static final class SkylarkAspectClassImpl extends SkylarkAspectClass {
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000872 private final AspectDefinition aspectDefinition;
John Fielda97e17f2015-11-13 02:19:52 +0000873 private final Label extensionLabel;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000874 private final String exportedName;
875
Dmitry Lomov82e03772015-11-30 12:13:22 +0000876 public SkylarkAspectClassImpl(SkylarkAspect skylarkAspect) {
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000877 Preconditions.checkArgument(skylarkAspect.isExported(), "Skylark aspects must be exported");
Dmitry Lomov82e03772015-11-30 12:13:22 +0000878 this.extensionLabel = skylarkAspect.getExtensionLabel();
879 this.exportedName = skylarkAspect.getExportedName();
880
881 AspectDefinition.Builder builder = new AspectDefinition.Builder(getName());
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000882 for (String attributeAspect : skylarkAspect.getAttributeAspects()) {
883 builder.attributeAspect(attributeAspect, this);
884 }
Dmitry Lomovace678e2015-12-16 15:10:20 +0000885 ImmutableList<Pair<String, Descriptor>> attributes = skylarkAspect.getAttributes();
886 for (Pair<String, Descriptor> attribute : attributes) {
887 builder.add(attribute.second.getAttributeBuilder().build(attribute.first));
888 }
Michael Staibcd48cd52016-01-15 20:11:11 +0000889 builder.requiresConfigurationFragmentsBySkylarkModuleName(skylarkAspect.getFragments());
890 builder.requiresHostConfigurationFragmentsBySkylarkModuleName(
891 skylarkAspect.getHostFragments());
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000892 this.aspectDefinition = builder.build();
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000893 }
894
895 @Override
Dmitry Lomov6231d082015-11-02 17:17:20 +0000896 public AspectDefinition getDefinition(AspectParameters aspectParameters) {
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000897 return aspectDefinition;
898 }
899
Francois-Rene Rideauab049e02016-02-17 16:13:46 +0000900 @Override
John Fielda97e17f2015-11-13 02:19:52 +0000901 public Label getExtensionLabel() {
902 return extensionLabel;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000903 }
904
Francois-Rene Rideauab049e02016-02-17 16:13:46 +0000905 @Override
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000906 public String getExportedName() {
907 return exportedName;
908 }
909
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000910 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100911}