blob: 2c5cc0140ba345d749fbf5d6471c23ce8e9f8e0f [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;
55import com.google.devtools.build.lib.packages.AttributeMap;
56import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImplicitOutputsFunctionWithCallback;
57import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SkylarkImplicitOutputsFunctionWithMap;
58import com.google.devtools.build.lib.packages.Package.NameConflictException;
59import com.google.devtools.build.lib.packages.PackageFactory;
60import com.google.devtools.build.lib.packages.PackageFactory.PackageContext;
61import com.google.devtools.build.lib.packages.Rule;
62import com.google.devtools.build.lib.packages.RuleClass;
63import com.google.devtools.build.lib.packages.RuleClass.Builder;
64import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
65import com.google.devtools.build.lib.packages.RuleFactory;
Mark Schalleree624452016-01-13 18:41:24 +000066import com.google.devtools.build.lib.packages.RuleFactory.BuildLangTypedAttributeValuesMap;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010067import com.google.devtools.build.lib.packages.RuleFactory.InvalidRuleException;
Dmitry Lomov82e03772015-11-30 12:13:22 +000068import com.google.devtools.build.lib.packages.SkylarkAspectClass;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010069import com.google.devtools.build.lib.packages.TargetUtils;
70import com.google.devtools.build.lib.packages.TestSize;
Dmitry Lomovace678e2015-12-16 15:10:20 +000071import com.google.devtools.build.lib.rules.SkylarkAttr.Descriptor;
John Field585d1a02015-12-16 16:03:52 +000072import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature;
73import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature.Param;
74import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +000075import com.google.devtools.build.lib.syntax.BaseFunction;
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +000076import com.google.devtools.build.lib.syntax.BuiltinFunction;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010077import com.google.devtools.build.lib.syntax.ClassObject;
78import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject;
79import com.google.devtools.build.lib.syntax.Environment;
80import com.google.devtools.build.lib.syntax.Environment.NoSuchVariableException;
81import com.google.devtools.build.lib.syntax.EvalException;
82import com.google.devtools.build.lib.syntax.EvalUtils;
83import com.google.devtools.build.lib.syntax.FuncallExpression;
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +000084import com.google.devtools.build.lib.syntax.FunctionSignature;
Dmitry Lomov0b832ce2015-10-20 10:03:14 +000085import com.google.devtools.build.lib.syntax.Printer;
Francois-Rene Rideau0f7ba342015-08-31 16:16:21 +000086import com.google.devtools.build.lib.syntax.Runtime;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010087import com.google.devtools.build.lib.syntax.SkylarkCallbackFunction;
Francois-Rene Rideauc0a8c582016-01-28 18:36:22 +000088import com.google.devtools.build.lib.syntax.SkylarkDict;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010089import com.google.devtools.build.lib.syntax.SkylarkList;
Dmitry Lomov4be58932015-12-23 13:21:32 +000090import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +000091import com.google.devtools.build.lib.syntax.SkylarkSignatureProcessor;
Lukacs Berkiffa73ad2015-09-18 11:40:12 +000092import com.google.devtools.build.lib.syntax.Type;
93import com.google.devtools.build.lib.syntax.Type.ConversionException;
Dmitry Lomov7b599452015-11-26 10:07:32 +000094import com.google.devtools.build.lib.util.Pair;
Mark Schaller6df81792015-12-10 18:47:47 +000095import com.google.devtools.build.lib.util.Preconditions;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010096
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010097import java.util.Map;
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +000098import java.util.Set;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010099import java.util.concurrent.ExecutionException;
100
101/**
102 * A helper class to provide an easier API for Skylark rule definitions.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100103 */
104public class SkylarkRuleClassFunctions {
105
106 //TODO(bazel-team): proper enum support
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000107 @SkylarkSignature(name = "DATA_CFG", returnType = ConfigurationTransition.class,
Laurent Le Brund90db7f2015-04-01 14:16:30 +0000108 doc = "Experimental. Specifies a transition to the data configuration.")
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100109 private static final Object dataTransition = ConfigurationTransition.DATA;
110
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000111 @SkylarkSignature(name = "HOST_CFG", returnType = ConfigurationTransition.class,
Laurent Le Brund90db7f2015-04-01 14:16:30 +0000112 doc = "Specifies a transition to the host configuration.")
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100113 private static final Object hostTransition = ConfigurationTransition.HOST;
114
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100115 private static final LateBoundLabel<BuildConfiguration> RUN_UNDER =
116 new LateBoundLabel<BuildConfiguration>() {
117 @Override
118 public Label getDefault(Rule rule, BuildConfiguration configuration) {
119 RunUnder runUnder = configuration.getRunUnder();
120 return runUnder == null ? null : runUnder.getLabel();
121 }
122 };
123
124 // TODO(bazel-team): Copied from ConfiguredRuleClassProvider for the transition from built-in
125 // rules to skylark extensions. Using the same instance would require a large refactoring.
126 // If we don't want to support old built-in rules and Skylark simultaneously
127 // (except for transition phase) it's probably OK.
128 private static LoadingCache<String, Label> labelCache =
129 CacheBuilder.newBuilder().build(new CacheLoader<String, Label>() {
130 @Override
131 public Label load(String from) throws Exception {
132 try {
133 return Label.parseAbsolute(from);
Lukacs Berkia6434362015-09-15 13:56:14 +0000134 } catch (LabelSyntaxException e) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100135 throw new Exception(from);
136 }
137 }
138 });
139
140 // TODO(bazel-team): Remove the code duplication (BaseRuleClasses and this class).
Florian Weikert70265ec2015-08-31 14:37:27 +0000141 /** Parent rule class for non-executable non-test Skylark rules. */
Mark Schaller4fa83ac2015-07-10 16:59:37 +0000142 public static final RuleClass baseRule =
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100143 BaseRuleClasses.commonCoreAndSkylarkAttributes(
144 new RuleClass.Builder("$base_rule", RuleClassType.ABSTRACT, true))
145 .add(attr("expect_failure", STRING))
146 .build();
147
Florian Weikert70265ec2015-08-31 14:37:27 +0000148 /** Parent rule class for executable non-test Skylark rules. */
149 public static final RuleClass binaryBaseRule =
150 new RuleClass.Builder("$binary_base_rule", RuleClassType.ABSTRACT, true, baseRule)
151 .add(
152 attr("args", STRING_LIST)
153 .nonconfigurable("policy decision: should be consistent across configurations"))
154 .add(attr("output_licenses", LICENSE))
155 .build();
156
Mark Schaller4fa83ac2015-07-10 16:59:37 +0000157 /** Parent rule class for test Skylark rules. */
158 public static final RuleClass testBaseRule =
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100159 new RuleClass.Builder("$test_base_rule", RuleClassType.ABSTRACT, true, baseRule)
160 .add(attr("size", STRING).value("medium").taggable()
161 .nonconfigurable("used in loading phase rule validation logic"))
162 .add(attr("timeout", STRING).taggable()
163 .nonconfigurable("used in loading phase rule validation logic").value(
164 new Attribute.ComputedDefault() {
165 @Override
166 public Object getDefault(AttributeMap rule) {
167 TestSize size = TestSize.getTestSize(rule.get("size", Type.STRING));
168 if (size != null) {
169 String timeout = size.getDefaultTimeout().toString();
170 if (timeout != null) {
171 return timeout;
172 }
173 }
174 return "illegal";
175 }
176 }))
177 .add(attr("flaky", BOOLEAN).value(false).taggable()
178 .nonconfigurable("taggable - called in Rule.getRuleTags"))
179 .add(attr("shard_count", INTEGER).value(-1))
180 .add(attr("local", BOOLEAN).value(false).taggable()
181 .nonconfigurable("policy decision: this should be consistent across configurations"))
Laurent Le Brun8d4ffc42015-06-24 15:21:50 +0000182 .add(attr("args", STRING_LIST)
183 .nonconfigurable("policy decision: should be consistent across configurations"))
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100184 .add(attr("$test_runtime", LABEL_LIST).cfg(HOST).value(ImmutableList.of(
Lukacs Berki3fbcc612015-10-05 12:30:56 +0000185 labelCache.getUnchecked(Constants.TOOLS_REPOSITORY + "//tools/test:runtime"))))
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100186 .add(attr(":run_under", LABEL).cfg(DATA).value(RUN_UNDER))
187 .build();
188
189 /**
Han-Wen Nienhuysd39e1e12015-08-20 13:52:19 +0000190 * In native code, private values start with $.
191 * In Skylark, private values start with _, because of the grammar.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100192 */
193 private static String attributeToNative(String oldName, Location loc, boolean isLateBound)
194 throws EvalException {
195 if (oldName.isEmpty()) {
196 throw new EvalException(loc, "Attribute name cannot be empty");
197 }
198 if (isLateBound) {
199 if (oldName.charAt(0) != '_') {
200 throw new EvalException(loc, "When an attribute value is a function, "
201 + "the attribute must be private (start with '_')");
202 }
203 return ":" + oldName.substring(1);
204 }
205 if (oldName.charAt(0) == '_') {
206 return "$" + oldName.substring(1);
207 }
208 return oldName;
209 }
210
211 // TODO(bazel-team): implement attribute copy and other rule properties
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000212 @SkylarkSignature(name = "rule", doc =
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100213 "Creates a new rule. Store it in a global value, so that it can be loaded and called "
214 + "from BUILD files.",
Francois-Rene Rideau95b0d0c2015-04-22 16:52:13 +0000215 returnType = BaseFunction.class,
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000216 mandatoryPositionals = {
Francois-Rene Rideau95b0d0c2015-04-22 16:52:13 +0000217 @Param(name = "implementation", type = BaseFunction.class,
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000218 doc = "the function implementing this rule, must have exactly one parameter: "
David Chen24f2d992015-08-17 17:25:46 +0000219 + "<a href=\"ctx.html\">ctx</a>. The function is called during the analysis phase "
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000220 + "for each instance of the rule. It can access the attributes provided by the user. "
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000221 + "It must create actions to generate all the declared outputs.")
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100222 },
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000223 optionalPositionals = {
224 @Param(name = "test", type = Boolean.class, defaultValue = "False",
225 doc = "Whether this rule is a test rule. "
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000226 + "If True, the rule must end with <code>_test</code> (otherwise it must not), "
Laurent Le Brun9178bc22015-06-19 16:00:34 +0000227 + "and there must be an action that generates <code>ctx.outputs.executable</code>."),
Francois-Rene Rideauc0a8c582016-01-28 18:36:22 +0000228 @Param(name = "attrs", type = SkylarkDict.class, noneable = true, defaultValue = "None",
229 doc =
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000230 "dictionary to declare all the attributes of the rule. It maps from an attribute name "
David Chen24f2d992015-08-17 17:25:46 +0000231 + "to an attribute object (see <a href=\"attr.html\">attr</a> module). "
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000232 + "Attributes starting with <code>_</code> are private, and can be used to add "
Laurent Le Brun9ba067d2015-05-22 13:55:23 +0000233 + "an implicit dependency on a label. The attribute <code>name</code> is implicitly "
234 + "added and must not be specified. Attributes <code>visibility</code>, "
235 + "<code>deprecation</code>, <code>tags</code>, <code>testonly</code>, and "
236 + "<code>features</code> are implicitly added and might be overriden."),
237 // TODO(bazel-team): need to give the types of these builtin attributes
Francois-Rene Rideauc0a8c582016-01-28 18:36:22 +0000238 @Param(name = "outputs", type = SkylarkDict.class, callbackEnabled = true, noneable = true,
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000239 defaultValue = "None", doc = "outputs of this rule. "
240 + "It is a dictionary mapping from string to a template name. "
Brian Silvermane0adfc62015-08-03 09:04:31 +0000241 + "For example: <code>{\"ext\": \"%{name}.ext\"}</code>. <br>"
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000242 + "The dictionary key becomes an attribute in <code>ctx.outputs</code>. "
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000243 // TODO(bazel-team): Make doc more clear, wrt late-bound attributes.
244 + "It may also be a function (which receives <code>ctx.attr</code> as argument) "
245 + "returning such a dictionary."),
246 @Param(name = "executable", type = Boolean.class, defaultValue = "False",
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000247 doc = "whether this rule is marked as executable or not. If True, "
248 + "there must be an action that generates <code>ctx.outputs.executable</code>."),
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000249 @Param(name = "output_to_genfiles", type = Boolean.class, defaultValue = "False",
250 doc = "If true, the files will be generated in the genfiles directory instead of the "
Laurent Le Brun9c8b3702015-09-23 13:53:59 +0000251 + "bin directory. Unless you need it for compatibility with existing rules "
252 + "(e.g. when generating header files for C++), do not set this flag."),
Dmitry Lomovace678e2015-12-16 15:10:20 +0000253 @Param(name = "fragments", type = SkylarkList.class, generic1 = String.class,
254 defaultValue = "[]",
255 doc = "List of names of configuration fragments that the rule requires "
256 + "in target configuration."),
257 @Param(name = "host_fragments", type = SkylarkList.class, generic1 = String.class,
258 defaultValue = "[]",
259 doc = "List of names of configuration fragments that the rule requires "
260 + "in host configuration.")},
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000261 useAst = true, useEnvironment = true)
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000262 private static final BuiltinFunction rule = new BuiltinFunction("rule") {
Florian Weikert3f8aac92015-09-07 12:06:02 +0000263 @SuppressWarnings({"rawtypes", "unchecked"}) // castMap produces
264 // an Attribute.Builder instead of a Attribute.Builder<?> but it's OK.
265 public BaseFunction invoke(BaseFunction implementation, Boolean test, Object attrs,
266 Object implicitOutputs, Boolean executable, Boolean outputToGenfiles, SkylarkList fragments,
267 SkylarkList hostFragments, FuncallExpression ast, Environment funcallEnv)
268 throws EvalException, ConversionException {
269 funcallEnv.checkLoadingPhase("rule", ast.getLocation());
270 RuleClassType type = test ? RuleClassType.TEST : RuleClassType.NORMAL;
271 RuleClass parent = test ? testBaseRule : (executable ? binaryBaseRule : baseRule);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100272
Florian Weikert3f8aac92015-09-07 12:06:02 +0000273 // We'll set the name later, pass the empty string for now.
274 RuleClass.Builder builder = new RuleClass.Builder("", type, true, parent);
Dmitry Lomovace678e2015-12-16 15:10:20 +0000275 ImmutableList<Pair<String, SkylarkAttr.Descriptor>> attributes =
276 attrObjectToAttributesList(attrs, ast);
Florian Weikert3f8aac92015-09-07 12:06:02 +0000277 if (executable || test) {
Florian Weikerte96b0b82015-09-25 11:35:11 +0000278 addAttribute(
Dmitry Lomov7b599452015-11-26 10:07:32 +0000279 ast.getLocation(),
Florian Weikerte96b0b82015-09-25 11:35:11 +0000280 builder,
Florian Weikert3f8aac92015-09-07 12:06:02 +0000281 attr("$is_executable", BOOLEAN)
282 .value(true)
283 .nonconfigurable("Called from RunCommand.isExecutable, which takes a Target")
284 .build());
285 builder.setOutputsDefaultExecutable();
286 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100287
Florian Weikert3f8aac92015-09-07 12:06:02 +0000288 if (implicitOutputs != Runtime.NONE) {
289 if (implicitOutputs instanceof BaseFunction) {
290 BaseFunction func = (BaseFunction) implicitOutputs;
Dmitry Lomovace678e2015-12-16 15:10:20 +0000291 SkylarkCallbackFunction callback = new SkylarkCallbackFunction(func, ast, funcallEnv);
Florian Weikert3f8aac92015-09-07 12:06:02 +0000292 builder.setImplicitOutputsFunction(
293 new SkylarkImplicitOutputsFunctionWithCallback(callback, ast.getLocation()));
294 } else {
295 builder.setImplicitOutputsFunction(
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000296 new SkylarkImplicitOutputsFunctionWithMap(
Dmitry Lomovace678e2015-12-16 15:10:20 +0000297 ImmutableMap.copyOf(
298 castMap(
299 implicitOutputs,
300 String.class,
301 String.class,
302 "implicit outputs of the rule class"))));
Han-Wen Nienhuysd39e1e12015-08-20 13:52:19 +0000303 }
Florian Weikert3f8aac92015-09-07 12:06:02 +0000304 }
Laurent Le Brunc2efe452015-04-08 16:27:21 +0000305
Florian Weikert3f8aac92015-09-07 12:06:02 +0000306 if (outputToGenfiles) {
307 builder.setOutputToGenfiles();
308 }
Florian Weikert3f53fbb2015-08-07 22:25:50 +0000309
Michael Staibff66c192016-01-14 22:40:37 +0000310 builder.requiresConfigurationFragmentsBySkylarkModuleName(
311 fragments.getContents(String.class, "fragments"));
312 builder.requiresHostConfigurationFragmentsBySkylarkModuleName(
313 hostFragments.getContents(String.class, "host_fragments"));
Florian Weikert3f8aac92015-09-07 12:06:02 +0000314 builder.setConfiguredTargetFunction(implementation);
Francois-Rene Rideau89312fb2015-09-10 18:53:03 +0000315 builder.setRuleDefinitionEnvironment(funcallEnv);
Dmitry Lomovace678e2015-12-16 15:10:20 +0000316 return new RuleFunction(builder, type, attributes, ast.getLocation());
Florian Weikert3f8aac92015-09-07 12:06:02 +0000317 }
Dmitry Lomovace678e2015-12-16 15:10:20 +0000318 };
Florian Weikert3f8aac92015-09-07 12:06:02 +0000319
Dmitry Lomovace678e2015-12-16 15:10:20 +0000320 protected static ImmutableList<Pair<String, Descriptor>> attrObjectToAttributesList(
321 Object attrs, FuncallExpression ast) throws EvalException {
322 ImmutableList.Builder<Pair<String, Descriptor>> attributes = ImmutableList.builder();
323
324 if (attrs != Runtime.NONE) {
325 for (Map.Entry<String, Descriptor> attr :
326 castMap(attrs, String.class, Descriptor.class, "attrs").entrySet()) {
327 Descriptor attrDescriptor = attr.getValue();
328 String attrName =
329 attributeToNative(
330 attr.getKey(),
331 ast.getLocation(),
332 attrDescriptor.getAttributeBuilder().hasLateBoundValue());
333 attributes.add(Pair.of(attrName, attrDescriptor));
Florian Weikert3f8aac92015-09-07 12:06:02 +0000334 }
335 }
Dmitry Lomovace678e2015-12-16 15:10:20 +0000336 return attributes.build();
337 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100338
Dmitry Lomov7b599452015-11-26 10:07:32 +0000339 private static void addAttribute(
340 Location location, RuleClass.Builder builder, Attribute attribute) throws EvalException {
341 try {
342 builder.addOrOverrideAttribute(attribute);
343 } catch (IllegalArgumentException ex) {
344 throw new EvalException(location, ex);
345 }
346 }
347
348
Dmitry Lomov99f43722015-12-22 13:04:46 +0000349 @SkylarkSignature(name = "aspect", doc =
350 "Creates a new aspect. The result of this fucntion must be stored in a global value.",
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000351 returnType = SkylarkAspect.class,
Dmitry Lomov99f43722015-12-22 13:04:46 +0000352 mandatoryPositionals = {
353 @Param(name = "implementation", type = BaseFunction.class,
354 doc = "the function implementing this aspect. Must have two parameters: "
355 + "<a href=\"Target.html\">Target</a> (the target to which the aspect is applied) and"
356 + "<a href=\"ctx.html\">ctx</a>. Attributes of the target are available via ctx.rule "
357 + " field. The function is called during the analysis phase for each application of "
358 + "an aspect to a target."
359 ),
360 },
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000361 optionalPositionals = {
Dmitry Lomov99f43722015-12-22 13:04:46 +0000362 @Param(name = "attr_aspects", type = SkylarkList.class, generic1 = String.class,
363 defaultValue = "[]",
364 doc = "List of attribute names. The aspect propagates along dependencies specified by "
365 + " attributes of a target with this name"
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000366 ),
Francois-Rene Rideauc0a8c582016-01-28 18:36:22 +0000367 @Param(name = "attrs", type = SkylarkDict.class, noneable = true, defaultValue = "None",
Dmitry Lomov99f43722015-12-22 13:04:46 +0000368 doc = "dictionary to declare all the attributes of the aspect. "
369 + "It maps from an attribute name to an attribute object "
370 + "(see <a href=\"attr.html\">attr</a> module). "
371 + "Aspect attributes are available to implementation function as fields of ctx parameter. "
372 + "All aspect attributes must be private, so their names must start with <code>_</code>. "
373 + "All aspect attributes must be have default values, and be of type "
374 + "<code>label</code> or <code>label_list</code>"
Michael Staibcd48cd52016-01-15 20:11:11 +0000375 ),
376 @Param(
377 name = "fragments",
378 type = SkylarkList.class,
379 generic1 = String.class,
380 defaultValue = "[]",
381 doc =
382 "List of names of configuration fragments that the aspect requires "
383 + "in target configuration."
384 ),
385 @Param(
386 name = "host_fragments",
387 type = SkylarkList.class,
388 generic1 = String.class,
389 defaultValue = "[]",
390 doc =
391 "List of names of configuration fragments that the aspect requires "
392 + "in host configuration."
Dmitry Lomov99f43722015-12-22 13:04:46 +0000393 )
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000394 },
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000395 useEnvironment = true,
396 useAst = true
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000397 )
398 private static final BuiltinFunction aspect =
399 new BuiltinFunction("aspect") {
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000400 public SkylarkAspect invoke(
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000401 BaseFunction implementation,
402 SkylarkList attributeAspects,
Dmitry Lomovace678e2015-12-16 15:10:20 +0000403 Object attrs,
Michael Staibcd48cd52016-01-15 20:11:11 +0000404 SkylarkList fragments,
405 SkylarkList hostFragments,
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000406 FuncallExpression ast,
Dmitry Lomovace678e2015-12-16 15:10:20 +0000407 Environment funcallEnv)
408 throws EvalException {
409 ImmutableList.Builder<String> attrAspects = ImmutableList.builder();
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000410 for (Object attributeAspect : attributeAspects) {
Dmitry Lomovace678e2015-12-16 15:10:20 +0000411 attrAspects.add(STRING.convert(attributeAspect, "attr_aspects"));
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000412 }
413
Dmitry Lomovace678e2015-12-16 15:10:20 +0000414 ImmutableList<Pair<String, SkylarkAttr.Descriptor>> attributes =
415 attrObjectToAttributesList(attrs, ast);
416 for (Pair<String, Descriptor> attribute : attributes) {
417 String nativeName = attribute.getFirst();
418 if (!Attribute.isImplicit(nativeName)) {
419 throw new EvalException(
420 ast.getLocation(),
421 String.format(
422 "Aspect attribute '%s' must be implicit (its name should start with '_').",
423 nativeName));
424 }
425
426 String skylarkName = "_" + nativeName.substring(1);
427
428 if (!attribute.getSecond().getAttributeBuilder().isValueSet()) {
429 throw new EvalException(
430 ast.getLocation(),
431 String.format("Aspect attribute '%s' has no default value.", skylarkName));
432 }
433 }
434
Michael Staibcd48cd52016-01-15 20:11:11 +0000435 return new SkylarkAspect(
436 implementation,
437 attrAspects.build(),
438 attributes,
439 ImmutableSet.copyOf(fragments.getContents(String.class, "fragments")),
440 ImmutableSet.copyOf(hostFragments.getContents(String.class, "host_fragments")),
441 funcallEnv);
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000442 }
443 };
444
445
Francois-Rene Rideau93ed7f12015-10-20 15:39:33 +0000446 /** The implementation for the magic function "rule" that creates Skylark rule classes */
Han-Wen Nienhuysceae8c52015-09-22 16:24:45 +0000447 public static final class RuleFunction extends BaseFunction {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000448 private RuleClass.Builder builder;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100449
Dmitry Lomov7b599452015-11-26 10:07:32 +0000450 private RuleClass ruleClass;
451 private final RuleClassType type;
452 private ImmutableList<Pair<String, SkylarkAttr.Descriptor>> attributes;
453 private final Location definitionLocation;
454 private Label skylarkLabel;
455
456 public RuleFunction(Builder builder, RuleClassType type,
457 ImmutableList<Pair<String, SkylarkAttr.Descriptor>> attributes,
458 Location definitionLocation) {
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000459 super("rule", FunctionSignature.KWARGS);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100460 this.builder = builder;
461 this.type = type;
Dmitry Lomov7b599452015-11-26 10:07:32 +0000462 this.attributes = attributes;
463 this.definitionLocation = definitionLocation;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100464 }
465
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000466 @Override
Francois-Rene Rideau76023b92015-04-17 15:31:59 +0000467 @SuppressWarnings("unchecked") // the magic hidden $pkg_context variable is guaranteed
468 // to be a PackageContext
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000469 public Object call(Object[] args, FuncallExpression ast, Environment env)
470 throws EvalException, InterruptedException, ConversionException {
Francois-Rene Rideau3a8ddcc2015-08-26 22:30:38 +0000471 env.checkLoadingPhase(getName(), ast.getLocation());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100472 try {
Dmitry Lomov7b599452015-11-26 10:07:32 +0000473 if (ruleClass == null) {
Francois-Rene Rideaua8352e92015-06-30 16:59:42 +0000474 throw new EvalException(ast.getLocation(),
475 "Invalid rule class hasn't been exported by a Skylark file");
476 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100477 PackageContext pkgContext = (PackageContext) env.lookup(PackageFactory.PKG_CONTEXT);
Mark Schalleree624452016-01-13 18:41:24 +0000478 BuildLangTypedAttributeValuesMap attributeValues =
479 new BuildLangTypedAttributeValuesMap((Map<String, Object>) args[0]);
480 return RuleFactory.createAndAddRule(pkgContext, ruleClass, attributeValues, ast, env);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100481 } catch (InvalidRuleException | NameConflictException | NoSuchVariableException e) {
482 throw new EvalException(ast.getLocation(), e.getMessage());
483 }
484 }
485
Francois-Rene Rideaua8352e92015-06-30 16:59:42 +0000486 /**
487 * Export a RuleFunction from a Skylark file with a given name.
488 */
Dmitry Lomov7b599452015-11-26 10:07:32 +0000489 void export(Label skylarkLabel, String ruleClassName) throws EvalException {
490 Preconditions.checkState(ruleClass == null && builder != null);
John Fielda97e17f2015-11-13 02:19:52 +0000491 this.skylarkLabel = skylarkLabel;
Dmitry Lomov7b599452015-11-26 10:07:32 +0000492 if (type == RuleClassType.TEST != TargetUtils.isTestRuleName(ruleClassName)) {
493 throw new EvalException(definitionLocation, "Invalid rule class name '" + ruleClassName
494 + "', test rule class names must end with '_test' and other rule classes must not");
495 }
496 for (Pair<String, SkylarkAttr.Descriptor> attribute : attributes) {
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000497 SkylarkAttr.Descriptor descriptor = attribute.getSecond();
498 Attribute.Builder<?> attributeBuilder = descriptor.getAttributeBuilder();
499 for (SkylarkAspect skylarkAspect : descriptor.getAspects()) {
500 if (!skylarkAspect.isExported()) {
501 throw new EvalException(definitionLocation,
502 "All aspects applied to rule dependencies must be top-level values");
503 }
Dmitry Lomov82e03772015-11-30 12:13:22 +0000504 attributeBuilder.aspect(skylarkAspect.getAspectClass());
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000505 }
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000506
Dmitry Lomov7b599452015-11-26 10:07:32 +0000507 addAttribute(definitionLocation, builder,
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000508 descriptor.getAttributeBuilder().build(attribute.getFirst()));
Dmitry Lomov7b599452015-11-26 10:07:32 +0000509 }
510 this.ruleClass = builder.build(ruleClassName);
511
512 this.builder = null;
513 this.attributes = null;
Francois-Rene Rideaua8352e92015-06-30 16:59:42 +0000514 }
515
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100516 @VisibleForTesting
Dmitry Lomov7b599452015-11-26 10:07:32 +0000517 public RuleClass getRuleClass() {
518 Preconditions.checkState(ruleClass != null && builder == null);
519 return ruleClass;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100520 }
521 }
522
Dmitry Lomov7b599452015-11-26 10:07:32 +0000523 public static void exportRuleFunctionsAndAspects(Environment env, Label skylarkLabel)
524 throws EvalException {
Dmitry Lomov5a8f1c02015-11-26 10:49:16 +0000525 Set<String> globalNames = env.getGlobals().getDirectVariableNames();
526
527 // Export aspects first since rules can depend on aspects.
528 for (String name : globalNames) {
529 Object value;
530 try {
531 value = env.lookup(name);
532 } catch (NoSuchVariableException e) {
533 throw new AssertionError(e);
534 }
535 if (value instanceof SkylarkAspect) {
536 SkylarkAspect skylarkAspect = (SkylarkAspect) value;
537 if (!skylarkAspect.isExported()) {
538 skylarkAspect.export(skylarkLabel, name);
539 }
540 }
541 }
542
543 for (String name : globalNames) {
Francois-Rene Rideaua8352e92015-06-30 16:59:42 +0000544 try {
545 Object value = env.lookup(name);
546 if (value instanceof RuleFunction) {
547 RuleFunction function = (RuleFunction) value;
John Fielda97e17f2015-11-13 02:19:52 +0000548 if (function.skylarkLabel == null) {
549 function.export(skylarkLabel, name);
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000550 }
551 }
Francois-Rene Rideaua8352e92015-06-30 16:59:42 +0000552 } catch (NoSuchVariableException e) {
553 throw new AssertionError(e);
554 }
555 }
556 }
557
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000558 @SkylarkSignature(name = "Label", doc = "Creates a Label referring to a BUILD target. Use "
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100559 + "this function only when you want to give a default value for the label attributes. "
Laurent Le Brunfaf78412015-07-28 16:13:00 +0000560 + "The argument must refer to an absolute label. "
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100561 + "Example: <br><pre class=language-python>Label(\"//tools:default\")</pre>",
562 returnType = Label.class,
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000563 mandatoryPositionals = {@Param(name = "label_string", type = String.class,
564 doc = "the label string")},
565 useLocation = true)
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000566 private static final BuiltinFunction label = new BuiltinFunction("Label") {
567 public Label invoke(String labelString,
568 Location loc) throws EvalException, ConversionException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100569 try {
570 return labelCache.get(labelString);
571 } catch (ExecutionException e) {
572 throw new EvalException(loc, "Illegal absolute label syntax: " + labelString);
573 }
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000574 }
575 };
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100576
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000577 @SkylarkSignature(name = "FileType",
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100578 doc = "Creates a file filter from a list of strings. For example, to match files ending "
579 + "with .cc or .cpp, use: <pre class=language-python>FileType([\".cc\", \".cpp\"])</pre>",
580 returnType = SkylarkFileType.class,
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000581 mandatoryPositionals = {
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000582 @Param(name = "types", type = SkylarkList.class, generic1 = String.class, defaultValue = "[]",
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100583 doc = "a list of the accepted file extensions")})
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000584 private static final BuiltinFunction fileType = new BuiltinFunction("FileType") {
Francois-Rene Rideau93ed7f12015-10-20 15:39:33 +0000585 public SkylarkFileType invoke(SkylarkList types) throws EvalException {
586 return SkylarkFileType.of(types.getContents(String.class, "types"));
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000587 }
588 };
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100589
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000590 @SkylarkSignature(name = "to_proto",
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100591 doc = "Creates a text message from the struct parameter. This method only works if all "
592 + "struct elements (recursively) are strings, ints, booleans, other structs or a "
593 + "list of these types. Quotes and new lines in strings are escaped. "
594 + "Examples:<br><pre class=language-python>"
595 + "struct(key=123).to_proto()\n# key: 123\n\n"
596 + "struct(key=True).to_proto()\n# key: true\n\n"
597 + "struct(key=[1, 2, 3]).to_proto()\n# key: 1\n# key: 2\n# key: 3\n\n"
598 + "struct(key='text').to_proto()\n# key: \"text\"\n\n"
599 + "struct(key=struct(inner_key='text')).to_proto()\n"
600 + "# key {\n# inner_key: \"text\"\n# }\n\n"
601 + "struct(key=[struct(inner_key=1), struct(inner_key=2)]).to_proto()\n"
602 + "# key {\n# inner_key: 1\n# }\n# key {\n# inner_key: 2\n# }\n\n"
603 + "struct(key=struct(inner_key=struct(inner_inner_key='text'))).to_proto()\n"
604 + "# key {\n# inner_key {\n# inner_inner_key: \"text\"\n# }\n# }\n</pre>",
Francois-Rene Rideaua3ac2022015-04-20 18:35:05 +0000605 objectType = SkylarkClassObject.class, returnType = String.class,
606 mandatoryPositionals = {
607 // TODO(bazel-team): shouldn't we accept any ClassObject?
608 @Param(name = "self", type = SkylarkClassObject.class,
609 doc = "this struct")},
610 useLocation = true)
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000611 private static final BuiltinFunction toProto = new BuiltinFunction("to_proto") {
612 public String invoke(SkylarkClassObject self, Location loc) throws EvalException {
613 StringBuilder sb = new StringBuilder();
614 printTextMessage(self, sb, 0, loc);
615 return sb.toString();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100616 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100617
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000618 private void printTextMessage(ClassObject object, StringBuilder sb,
619 int indent, Location loc) throws EvalException {
620 for (String key : object.getKeys()) {
621 printTextMessage(key, object.getValue(key), sb, indent, loc);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100622 }
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000623 }
624
625 private void printSimpleTextMessage(String key, Object value, StringBuilder sb,
626 int indent, Location loc, String container) throws EvalException {
627 if (value instanceof ClassObject) {
628 print(sb, key + " {", indent);
629 printTextMessage((ClassObject) value, sb, indent + 1, loc);
630 print(sb, "}", indent);
631 } else if (value instanceof String) {
632 print(sb, key + ": \"" + escape((String) value) + "\"", indent);
633 } else if (value instanceof Integer) {
634 print(sb, key + ": " + value, indent);
635 } else if (value instanceof Boolean) {
636 // We're relying on the fact that Java converts Booleans to Strings in the same way
637 // as the protocol buffers do.
638 print(sb, key + ": " + value, indent);
639 } else {
640 throw new EvalException(loc,
641 "Invalid text format, expected a struct, a string, a bool, or an int but got a "
642 + EvalUtils.getDataTypeName(value) + " for " + container + " '" + key + "'");
643 }
644 }
645
646 private void printTextMessage(String key, Object value, StringBuilder sb,
647 int indent, Location loc) throws EvalException {
648 if (value instanceof SkylarkList) {
649 for (Object item : ((SkylarkList) value)) {
650 // TODO(bazel-team): There should be some constraint on the fields of the structs
651 // in the same list but we ignore that for now.
652 printSimpleTextMessage(key, item, sb, indent, loc, "list element in struct field");
653 }
654 } else {
Francois-Rene Rideau93ed7f12015-10-20 15:39:33 +0000655 printSimpleTextMessage(key, value, sb, indent, loc, "struct field");
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000656 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100657 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100658
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000659 private String escape(String string) {
660 // TODO(bazel-team): use guava's SourceCodeEscapers when it's released.
661 return string.replace("\"", "\\\"").replace("\n", "\\n");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100662 }
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000663
664 private void print(StringBuilder sb, String text, int indent) {
665 for (int i = 0; i < indent; i++) {
666 sb.append(" ");
667 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100668 sb.append(text);
669 sb.append("\n");
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000670 }
671 };
672
Dmitry Lomov4be58932015-12-23 13:21:32 +0000673 @SkylarkSignature(name = "output_group",
674 documented = false, // TODO(dslomov): document.
675 objectType = TransitiveInfoCollection.class,
676 returnType = SkylarkNestedSet.class,
677 mandatoryPositionals = {
678 @Param(name = "self", type = TransitiveInfoCollection.class, doc =
679 "this target"
680 ),
681 @Param(name = "group_name", type = String.class, doc =
682 "Output group name"
683 )
684 }
685 )
686 public static final BuiltinFunction output_group = new BuiltinFunction("output_group") {
687 public SkylarkNestedSet invoke(TransitiveInfoCollection self, String group) {
688 OutputGroupProvider provider = self.getProvider(OutputGroupProvider.class);
689 NestedSet<Artifact> result = provider != null
690 ? provider.getOutputGroup(group)
691 : NestedSetBuilder.<Artifact>emptySet(Order.STABLE_ORDER);
692 return SkylarkNestedSet.of(Artifact.class, result);
693 }
694 };
695
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000696 static {
697 SkylarkSignatureProcessor.configureSkylarkFunctions(SkylarkRuleClassFunctions.class);
698 }
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000699
700 /**
701 * A Skylark value that is a result of 'aspect(..)' function call.
702 */
Dmitry Lomov82e03772015-11-30 12:13:22 +0000703 public static final class SkylarkAspect implements SkylarkValue {
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000704 private final BaseFunction implementation;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000705 private final ImmutableList<String> attributeAspects;
Dmitry Lomovace678e2015-12-16 15:10:20 +0000706 private final ImmutableList<Pair<String, Descriptor>> attributes;
Michael Staibcd48cd52016-01-15 20:11:11 +0000707 private final ImmutableSet<String> fragments;
708 private final ImmutableSet<String> hostFragments;
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000709 private final Environment funcallEnv;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000710 private Exported exported;
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000711
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000712 public SkylarkAspect(
713 BaseFunction implementation,
714 ImmutableList<String> attributeAspects,
Dmitry Lomovace678e2015-12-16 15:10:20 +0000715 ImmutableList<Pair<String, Descriptor>> attributes,
Michael Staibcd48cd52016-01-15 20:11:11 +0000716 ImmutableSet<String> fragments,
717 ImmutableSet<String> hostFragments,
Dmitry Lomovace678e2015-12-16 15:10:20 +0000718 Environment funcallEnv) {
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000719 this.implementation = implementation;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000720 this.attributeAspects = attributeAspects;
Dmitry Lomovace678e2015-12-16 15:10:20 +0000721 this.attributes = attributes;
Michael Staibcd48cd52016-01-15 20:11:11 +0000722 this.fragments = fragments;
723 this.hostFragments = hostFragments;
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000724 this.funcallEnv = funcallEnv;
725 }
726
727 public BaseFunction getImplementation() {
728 return implementation;
729 }
730
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000731 public ImmutableList<String> getAttributeAspects() {
732 return attributeAspects;
733 }
734
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000735 public Environment getFuncallEnv() {
736 return funcallEnv;
737 }
738
Dmitry Lomovace678e2015-12-16 15:10:20 +0000739 public ImmutableList<Pair<String, Descriptor>> getAttributes() {
740 return attributes;
Dmitry Lomovc1b1eed2015-11-26 11:18:12 +0000741 }
742
Michael Staibcd48cd52016-01-15 20:11:11 +0000743 /**
744 * Gets the set of configuration fragment names needed in the target configuration.
745 */
746 public ImmutableSet<String> getFragments() {
747 return fragments;
748 }
749
750 /**
751 * Gets the set of configuration fragment names needed in the host configuration.
752 */
753 public ImmutableSet<String> getHostFragments() {
754 return hostFragments;
755 }
756
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000757 @Override
758 public boolean isImmutable() {
759 return implementation.isImmutable();
760 }
761
762 @Override
763 public void write(Appendable buffer, char quotationMark) {
764 Printer.append(buffer, "Aspect:");
765 implementation.write(buffer, quotationMark);
766 }
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000767
768 public String getName() {
Dmitry Lomov82e03772015-11-30 12:13:22 +0000769 return getAspectClass().getName();
770 }
771
772 public SkylarkAspectClass getAspectClass() {
773 Preconditions.checkState(isExported());
774 return new SkylarkAspectClassImpl(this);
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000775 }
776
John Fielda97e17f2015-11-13 02:19:52 +0000777 void export(Label extensionLabel, String name) {
778 this.exported = new Exported(extensionLabel, name);
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000779 }
780
781 public boolean isExported() {
782 return exported != null;
783 }
784
John Fielda97e17f2015-11-13 02:19:52 +0000785 private Label getExtensionLabel() {
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000786 Preconditions.checkArgument(isExported());
John Fielda97e17f2015-11-13 02:19:52 +0000787 return exported.extensionLabel;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000788 }
789
790 private String getExportedName() {
791 Preconditions.checkArgument(isExported());
792 return exported.name;
793 }
794
795 @Immutable
796 private static class Exported {
John Fielda97e17f2015-11-13 02:19:52 +0000797 private final Label extensionLabel;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000798 private final String name;
799
John Fielda97e17f2015-11-13 02:19:52 +0000800 public Exported(Label extensionLabel, String name) {
801 this.extensionLabel = extensionLabel;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000802 this.name = name;
803 }
804
John Fielda97e17f2015-11-13 02:19:52 +0000805 @Override
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000806 public String toString() {
John Fielda97e17f2015-11-13 02:19:52 +0000807 return extensionLabel.toString() + "%" + name;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000808 }
809 }
810 }
811
812 /**
813 * Implementation of an aspect class defined in Skylark.
814 */
815 @Immutable
Dmitry Lomov82e03772015-11-30 12:13:22 +0000816 private static final class SkylarkAspectClassImpl extends SkylarkAspectClass {
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000817 private final AspectDefinition aspectDefinition;
John Fielda97e17f2015-11-13 02:19:52 +0000818 private final Label extensionLabel;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000819 private final String exportedName;
820
Dmitry Lomov82e03772015-11-30 12:13:22 +0000821 public SkylarkAspectClassImpl(SkylarkAspect skylarkAspect) {
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000822 Preconditions.checkArgument(skylarkAspect.isExported(), "Skylark aspects must be exported");
Dmitry Lomov82e03772015-11-30 12:13:22 +0000823 this.extensionLabel = skylarkAspect.getExtensionLabel();
824 this.exportedName = skylarkAspect.getExportedName();
825
826 AspectDefinition.Builder builder = new AspectDefinition.Builder(getName());
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000827 for (String attributeAspect : skylarkAspect.getAttributeAspects()) {
828 builder.attributeAspect(attributeAspect, this);
829 }
Dmitry Lomovace678e2015-12-16 15:10:20 +0000830 ImmutableList<Pair<String, Descriptor>> attributes = skylarkAspect.getAttributes();
831 for (Pair<String, Descriptor> attribute : attributes) {
832 builder.add(attribute.second.getAttributeBuilder().build(attribute.first));
833 }
Michael Staibcd48cd52016-01-15 20:11:11 +0000834 builder.requiresConfigurationFragmentsBySkylarkModuleName(skylarkAspect.getFragments());
835 builder.requiresHostConfigurationFragmentsBySkylarkModuleName(
836 skylarkAspect.getHostFragments());
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000837 this.aspectDefinition = builder.build();
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000838 }
839
840 @Override
Dmitry Lomov6231d082015-11-02 17:17:20 +0000841 public AspectDefinition getDefinition(AspectParameters aspectParameters) {
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000842 return aspectDefinition;
843 }
844
Francois-Rene Rideauc0a8c582016-01-28 18:36:22 +0000845 @Override
John Fielda97e17f2015-11-13 02:19:52 +0000846 public Label getExtensionLabel() {
847 return extensionLabel;
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000848 }
849
Francois-Rene Rideauc0a8c582016-01-28 18:36:22 +0000850 @Override
Dmitry Lomovc15ba2e2015-10-30 15:50:01 +0000851 public String getExportedName() {
852 return exportedName;
853 }
854
Dmitry Lomov0b832ce2015-10-20 10:03:14 +0000855 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100856}