blob: 73e037540b60fb8698f2510d32ad3bf08d2c0c6e [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Googlerd1c80612015-03-23 13:08:09 +00002//
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.packages;
16
cparsonsd6cc76d2019-10-31 15:04:48 -070017import static com.google.devtools.build.lib.packages.PackageFactory.getContext;
18
19import com.google.common.base.Joiner;
20import com.google.common.base.Preconditions;
21import com.google.common.collect.ImmutableList;
22import com.google.common.collect.ImmutableMap;
23import com.google.devtools.build.lib.cmdline.Label;
24import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
25import com.google.devtools.build.lib.cmdline.LabelValidator;
laurentlbd6443d22018-02-26 05:55:21 -080026import com.google.devtools.build.lib.cmdline.PackageIdentifier;
cparsonsd6cc76d2019-10-31 15:04:48 -070027import com.google.devtools.build.lib.events.Event;
cparsonsd6cc76d2019-10-31 15:04:48 -070028import com.google.devtools.build.lib.packages.Globber.BadGlobException;
cparsonsd6cc76d2019-10-31 15:04:48 -070029import com.google.devtools.build.lib.packages.PackageFactory.PackageContext;
30import com.google.devtools.build.lib.packages.RuleClass.Builder.ThirdPartyLicenseExistencePolicy;
Googlerc5fcc862019-09-06 16:17:47 -070031import com.google.devtools.build.lib.packages.Type.ConversionException;
gregced281df72020-05-11 12:27:06 -070032import com.google.devtools.build.lib.skylarkbuildapi.StarlarkNativeModuleApi;
Googlera9c93632019-11-13 10:48:07 -080033import com.google.devtools.build.lib.syntax.Dict;
Googlerd1c80612015-03-23 13:08:09 +000034import com.google.devtools.build.lib.syntax.EvalException;
cparsonsd6cc76d2019-10-31 15:04:48 -070035import com.google.devtools.build.lib.syntax.EvalUtils;
adonovanf5262c52020-04-02 09:25:14 -070036import com.google.devtools.build.lib.syntax.Location;
Googler48b446e2019-11-07 11:31:22 -080037import com.google.devtools.build.lib.syntax.Mutability;
Googler641bdf72019-11-12 10:32:26 -080038import com.google.devtools.build.lib.syntax.NoneType;
Googler4871cb02019-11-12 17:16:42 -080039import com.google.devtools.build.lib.syntax.Sequence;
Googler2e63b2a2019-11-11 08:59:12 -080040import com.google.devtools.build.lib.syntax.Starlark;
Googler942e1c42019-11-12 13:11:44 -080041import com.google.devtools.build.lib.syntax.StarlarkList;
Googlera3421e22019-09-26 06:48:32 -070042import com.google.devtools.build.lib.syntax.StarlarkThread;
Googler34f70582019-11-25 12:27:34 -080043import com.google.devtools.build.lib.syntax.StarlarkValue;
Googlercfd681f2019-11-11 07:24:02 -080044import com.google.devtools.build.lib.syntax.Tuple;
cparsonsd6cc76d2019-10-31 15:04:48 -070045import java.io.IOException;
46import java.util.ArrayList;
47import java.util.Collection;
Benjamin Peterson3b39e1e2020-01-31 10:21:38 -080048import java.util.Comparator;
cparsonsd6cc76d2019-10-31 15:04:48 -070049import java.util.List;
50import java.util.Map;
51import java.util.TreeMap;
52import javax.annotation.Nullable;
Googlerd1c80612015-03-23 13:08:09 +000053
gregcead752cc2020-04-10 10:32:59 -070054/** The Starlark native module. */
cparsonsd6cc76d2019-10-31 15:04:48 -070055// TODO(cparsons): Move the definition of native.package() to this class.
gregced281df72020-05-11 12:27:06 -070056public class StarlarkNativeModule implements StarlarkNativeModuleApi {
Googlerd1c80612015-03-23 13:08:09 +000057
cparsonsd6cc76d2019-10-31 15:04:48 -070058 /**
59 * This map contains all the (non-rule) functions of the native module (keyed by their symbol
60 * name). These native module bindings should be added (without the 'native' module namespace) to
61 * the global Starlark environment for BUILD files.
62 *
63 * <p>For example, the function "glob" is available under both a global symbol name {@code glob()}
64 * as well as under the native module namepsace {@code native.glob()}. An entry of this map is
65 * thus ("glob" : glob function).
66 */
67 public static final ImmutableMap<String, Object> BINDINGS_FOR_BUILD_FILES = initializeBindings();
68
69 private static ImmutableMap<String, Object> initializeBindings() {
cparsonsd6cc76d2019-10-31 15:04:48 -070070 ImmutableMap.Builder<String, Object> bindings = ImmutableMap.builder();
gregced281df72020-05-11 12:27:06 -070071 Starlark.addMethods(bindings, new StarlarkNativeModule());
cparsonsd6cc76d2019-10-31 15:04:48 -070072 return bindings.build();
73 }
74
cparsons30d877a2018-05-07 05:58:01 -070075 @Override
Googler4871cb02019-11-12 17:16:42 -080076 public Sequence<?> glob(
77 Sequence<?> include,
78 Sequence<?> exclude,
cparsonsd6cc76d2019-10-31 15:04:48 -070079 Integer excludeDirs,
80 Object allowEmptyArgument,
Googlera3421e22019-09-26 06:48:32 -070081 StarlarkThread thread)
cparsons7520dcc2018-04-04 13:59:27 -070082 throws EvalException, ConversionException, InterruptedException {
adonovanb23b8c72019-12-12 11:42:14 -080083 BazelStarlarkContext.from(thread).checkLoadingPhase("native.glob");
adonovan7891d3b2020-01-22 12:40:50 -080084 PackageContext context = getContext(thread);
cparsonsd6cc76d2019-10-31 15:04:48 -070085
86 List<String> includes = Type.STRING_LIST.convert(include, "'glob' argument");
87 List<String> excludes = Type.STRING_LIST.convert(exclude, "'glob' argument");
88
89 List<String> matches;
90 boolean allowEmpty;
Googlerf7e471b2019-11-11 10:10:07 -080091 if (allowEmptyArgument == Starlark.UNBOUND) {
cparsonsd6cc76d2019-10-31 15:04:48 -070092 allowEmpty = !thread.getSemantics().incompatibleDisallowEmptyGlob();
93 } else if (allowEmptyArgument instanceof Boolean) {
94 allowEmpty = (Boolean) allowEmptyArgument;
95 } else {
adonovan7891d3b2020-01-22 12:40:50 -080096 throw Starlark.errorf(
97 "expected boolean for argument `allow_empty`, got `%s`", allowEmptyArgument);
cparsonsd6cc76d2019-10-31 15:04:48 -070098 }
99
nharmata5e025a12019-06-28 09:58:13 -0700100 try {
cparsonsd6cc76d2019-10-31 15:04:48 -0700101 Globber.Token globToken =
102 context.globber.runAsync(includes, excludes, excludeDirs != 0, allowEmpty);
Benjamin Peterson3b39e1e2020-01-31 10:21:38 -0800103 matches = context.globber.fetchUnsorted(globToken);
cparsonsd6cc76d2019-10-31 15:04:48 -0700104 } catch (IOException e) {
105 String errorMessage =
106 String.format(
107 "error globbing [%s]%s: %s",
108 Joiner.on(", ").join(includes),
109 excludes.isEmpty() ? "" : " - [" + Joiner.on(", ").join(excludes) + "]",
110 e.getMessage());
adonovan7891d3b2020-01-22 12:40:50 -0800111 Location loc = thread.getCallerLocation();
cparsonsd6cc76d2019-10-31 15:04:48 -0700112 context.eventHandler.handle(Event.error(loc, errorMessage));
113 context.pkgBuilder.setIOExceptionAndMessage(e, errorMessage);
114 matches = ImmutableList.of();
115 } catch (BadGlobException e) {
adonovan7891d3b2020-01-22 12:40:50 -0800116 throw new EvalException(null, e.getMessage());
nharmata5e025a12019-06-28 09:58:13 -0700117 }
cparsonsd6cc76d2019-10-31 15:04:48 -0700118
Benjamin Peterson3b39e1e2020-01-31 10:21:38 -0800119 ArrayList<String> result = new ArrayList<>(matches.size());
120 for (String match : matches) {
121 if (match.charAt(0) == '@') {
122 // Add explicit colon to disambiguate from external repository.
123 match = ":" + match;
124 }
125 result.add(match);
126 }
127 result.sort(Comparator.naturalOrder());
128
129 return StarlarkList.copyOf(thread.mutability(), result);
cparsons7520dcc2018-04-04 13:59:27 -0700130 }
Han-Wen Nienhuys67e6f982016-01-15 15:54:12 +0000131
cparsons30d877a2018-05-07 05:58:01 -0700132 @Override
adonovan7891d3b2020-01-22 12:40:50 -0800133 public Object existingRule(String name, StarlarkThread thread)
cparsons7520dcc2018-04-04 13:59:27 -0700134 throws EvalException, InterruptedException {
adonovanb23b8c72019-12-12 11:42:14 -0800135 BazelStarlarkContext.from(thread).checkLoadingOrWorkspacePhase("native.existing_rule");
adonovan7891d3b2020-01-22 12:40:50 -0800136 PackageContext context = getContext(thread);
cparsonsd6cc76d2019-10-31 15:04:48 -0700137 Target target = context.pkgBuilder.getTarget(name);
adonovan7891d3b2020-01-22 12:40:50 -0800138 Dict<String, Object> rule = targetDict(target, thread.mutability());
Googler641bdf72019-11-12 10:32:26 -0800139 return rule != null ? rule : Starlark.NONE;
cparsons7520dcc2018-04-04 13:59:27 -0700140 }
Han-Wen Nienhuysde435f42016-01-28 12:35:13 +0000141
Han-Wen Nienhuys67e6f982016-01-15 15:54:12 +0000142 /*
143 If necessary, we could allow filtering by tag (anytag, alltags), name (regexp?), kind ?
gregceca48e9a2020-04-14 08:54:38 -0700144 For now, we ignore this, since users can implement it in Starlark.
Han-Wen Nienhuys67e6f982016-01-15 15:54:12 +0000145 */
cparsons30d877a2018-05-07 05:58:01 -0700146 @Override
adonovan7891d3b2020-01-22 12:40:50 -0800147 public Dict<String, Dict<String, Object>> existingRules(StarlarkThread thread)
Googlera9c93632019-11-13 10:48:07 -0800148 throws EvalException, InterruptedException {
adonovanb23b8c72019-12-12 11:42:14 -0800149 BazelStarlarkContext.from(thread).checkLoadingOrWorkspacePhase("native.existing_rules");
adonovan7891d3b2020-01-22 12:40:50 -0800150 PackageContext context = getContext(thread);
cparsonsd6cc76d2019-10-31 15:04:48 -0700151 Collection<Target> targets = context.pkgBuilder.getTargets();
Googlerbd5bc962019-11-08 14:21:14 -0800152 Mutability mu = thread.mutability();
Googler6456fcb2019-11-22 13:05:42 -0800153 Dict<String, Dict<String, Object>> rules = Dict.of(mu);
cparsonsd6cc76d2019-10-31 15:04:48 -0700154 for (Target t : targets) {
155 if (t instanceof Rule) {
adonovan7891d3b2020-01-22 12:40:50 -0800156 Dict<String, Object> rule = targetDict(t, mu);
cparsonsd6cc76d2019-10-31 15:04:48 -0700157 Preconditions.checkNotNull(rule);
adonovan7891d3b2020-01-22 12:40:50 -0800158 rules.put(t.getName(), rule, (Location) null);
cparsonsd6cc76d2019-10-31 15:04:48 -0700159 }
160 }
161
162 return rules;
cparsons7520dcc2018-04-04 13:59:27 -0700163 }
Googlerd1c80612015-03-23 13:08:09 +0000164
cparsons30d877a2018-05-07 05:58:01 -0700165 @Override
Googler641bdf72019-11-12 10:32:26 -0800166 public NoneType packageGroup(
Googlera3421e22019-09-26 06:48:32 -0700167 String name,
Googler4871cb02019-11-12 17:16:42 -0800168 Sequence<?> packagesO,
169 Sequence<?> includesO,
Googlera3421e22019-09-26 06:48:32 -0700170 StarlarkThread thread)
171 throws EvalException {
adonovanb23b8c72019-12-12 11:42:14 -0800172 BazelStarlarkContext.from(thread).checkLoadingPhase("native.package_group");
adonovan7891d3b2020-01-22 12:40:50 -0800173 PackageContext context = getContext(thread);
cparsonsd6cc76d2019-10-31 15:04:48 -0700174
175 List<String> packages =
176 Type.STRING_LIST.convert(packagesO, "'package_group.packages argument'");
177 List<Label> includes =
178 BuildType.LABEL_LIST.convert(
179 includesO, "'package_group.includes argument'", context.pkgBuilder.getBuildFileLabel());
180
adonovan7891d3b2020-01-22 12:40:50 -0800181 Location loc = thread.getCallerLocation();
cparsonsd6cc76d2019-10-31 15:04:48 -0700182 try {
183 context.pkgBuilder.addPackageGroup(name, packages, includes, context.eventHandler, loc);
Googler641bdf72019-11-12 10:32:26 -0800184 return Starlark.NONE;
cparsonsd6cc76d2019-10-31 15:04:48 -0700185 } catch (LabelSyntaxException e) {
adonovan7891d3b2020-01-22 12:40:50 -0800186 throw Starlark.errorf("package group has invalid name: %s: %s", name, e.getMessage());
cparsonsd6cc76d2019-10-31 15:04:48 -0700187 } catch (Package.NameConflictException e) {
adonovan7891d3b2020-01-22 12:40:50 -0800188 throw new EvalException(null, e.getMessage());
cparsonsd6cc76d2019-10-31 15:04:48 -0700189 }
cparsons7520dcc2018-04-04 13:59:27 -0700190 }
Googler10480572015-03-24 11:08:13 +0000191
cparsons30d877a2018-05-07 05:58:01 -0700192 @Override
Googler641bdf72019-11-12 10:32:26 -0800193 public NoneType exportsFiles(
adonovan7891d3b2020-01-22 12:40:50 -0800194 Sequence<?> srcs, Object visibilityO, Object licensesO, StarlarkThread thread)
cparsons7520dcc2018-04-04 13:59:27 -0700195 throws EvalException {
adonovanb23b8c72019-12-12 11:42:14 -0800196 BazelStarlarkContext.from(thread).checkLoadingPhase("native.exports_files");
adonovan7891d3b2020-01-22 12:40:50 -0800197 Package.Builder pkgBuilder = getContext(thread).pkgBuilder;
cparsonsd6cc76d2019-10-31 15:04:48 -0700198 List<String> files = Type.STRING_LIST.convert(srcs, "'exports_files' operand");
199
adonovan7891d3b2020-01-22 12:40:50 -0800200 RuleVisibility visibility =
201 EvalUtils.isNullOrNone(visibilityO)
202 ? ConstantRuleVisibility.PUBLIC
michajlocab889d2020-02-20 17:48:36 -0800203 : PackageUtils.getVisibility(
adonovan7891d3b2020-01-22 12:40:50 -0800204 pkgBuilder.getBuildFileLabel(),
205 BuildType.LABEL_LIST.convert(
206 visibilityO, "'exports_files' operand", pkgBuilder.getBuildFileLabel()));
207
cparsonsd6cc76d2019-10-31 15:04:48 -0700208 // TODO(bazel-team): is licenses plural or singular?
209 License license = BuildType.LICENSE.convertOptional(licensesO, "'exports_files' operand");
210
adonovan7891d3b2020-01-22 12:40:50 -0800211 Location loc = thread.getCallerLocation();
cparsonsd6cc76d2019-10-31 15:04:48 -0700212 for (String file : files) {
213 String errorMessage = LabelValidator.validateTargetName(file);
214 if (errorMessage != null) {
adonovan7891d3b2020-01-22 12:40:50 -0800215 throw Starlark.errorf("%s", errorMessage);
cparsonsd6cc76d2019-10-31 15:04:48 -0700216 }
217 try {
218 InputFile inputFile = pkgBuilder.createInputFile(file, loc);
219 if (inputFile.isVisibilitySpecified() && inputFile.getVisibility() != visibility) {
adonovan7891d3b2020-01-22 12:40:50 -0800220 throw Starlark.errorf(
221 "visibility for exported file '%s' declared twice", inputFile.getName());
cparsonsd6cc76d2019-10-31 15:04:48 -0700222 }
223 if (license != null && inputFile.isLicenseSpecified()) {
adonovan7891d3b2020-01-22 12:40:50 -0800224 throw Starlark.errorf(
225 "licenses for exported file '%s' declared twice", inputFile.getName());
cparsonsd6cc76d2019-10-31 15:04:48 -0700226 }
227
228 // See if we should check third-party licenses: first checking for any hard-coded policy,
229 // then falling back to user-settable flags.
230 boolean checkLicenses;
231 if (pkgBuilder.getThirdPartyLicenseExistencePolicy()
232 == ThirdPartyLicenseExistencePolicy.ALWAYS_CHECK) {
233 checkLicenses = true;
234 } else if (pkgBuilder.getThirdPartyLicenseExistencePolicy()
235 == ThirdPartyLicenseExistencePolicy.NEVER_CHECK) {
236 checkLicenses = false;
237 } else {
238 checkLicenses = !thread.getSemantics().incompatibleDisableThirdPartyLicenseChecking();
239 }
240
241 if (checkLicenses
242 && license == null
243 && !pkgBuilder.getDefaultLicense().isSpecified()
244 && RuleClass.isThirdPartyPackage(pkgBuilder.getPackageIdentifier())) {
adonovan7891d3b2020-01-22 12:40:50 -0800245 throw Starlark.errorf(
246 "third-party file '%s' lacks a license declaration with one of the following types:"
247 + " notice, reciprocal, permissive, restricted, unencumbered, by_exception_only",
248 inputFile.getName());
cparsonsd6cc76d2019-10-31 15:04:48 -0700249 }
250
251 pkgBuilder.setVisibilityAndLicense(inputFile, visibility, license);
252 } catch (Package.Builder.GeneratedLabelConflict e) {
adonovan7891d3b2020-01-22 12:40:50 -0800253 throw Starlark.errorf("%s", e.getMessage());
cparsonsd6cc76d2019-10-31 15:04:48 -0700254 }
255 }
Googler641bdf72019-11-12 10:32:26 -0800256 return Starlark.NONE;
cparsons7520dcc2018-04-04 13:59:27 -0700257 }
Googler10480572015-03-24 11:08:13 +0000258
cparsons30d877a2018-05-07 05:58:01 -0700259 @Override
adonovan7891d3b2020-01-22 12:40:50 -0800260 public String packageName(StarlarkThread thread) throws EvalException {
adonovanb23b8c72019-12-12 11:42:14 -0800261 BazelStarlarkContext.from(thread).checkLoadingPhase("native.package_name");
cparsons7520dcc2018-04-04 13:59:27 -0700262 PackageIdentifier packageId =
adonovan7891d3b2020-01-22 12:40:50 -0800263 PackageFactory.getContext(thread).getBuilder().getPackageIdentifier();
cparsons7520dcc2018-04-04 13:59:27 -0700264 return packageId.getPackageFragment().getPathString();
265 }
laurentlbba51cda2017-05-04 20:19:16 +0200266
cparsons30d877a2018-05-07 05:58:01 -0700267 @Override
adonovan7891d3b2020-01-22 12:40:50 -0800268 public String repositoryName(StarlarkThread thread) throws EvalException {
adonovanb23b8c72019-12-12 11:42:14 -0800269 BazelStarlarkContext.from(thread).checkLoadingPhase("native.repository_name");
cparsons7520dcc2018-04-04 13:59:27 -0700270 PackageIdentifier packageId =
adonovan7891d3b2020-01-22 12:40:50 -0800271 PackageFactory.getContext(thread).getBuilder().getPackageIdentifier();
cparsons7520dcc2018-04-04 13:59:27 -0700272 return packageId.getRepository().toString();
Francois-Rene Rideau537a90b2015-04-22 06:47:31 +0000273 }
cparsonsd6cc76d2019-10-31 15:04:48 -0700274
275 @Nullable
adonovan7891d3b2020-01-22 12:40:50 -0800276 private static Dict<String, Object> targetDict(Target target, Mutability mu)
Googlerbd5bc962019-11-08 14:21:14 -0800277 throws EvalException {
cparsonsd6cc76d2019-10-31 15:04:48 -0700278 if (!(target instanceof Rule)) {
279 return null;
280 }
Googler6456fcb2019-11-22 13:05:42 -0800281 Dict<String, Object> values = Dict.of(mu);
cparsonsd6cc76d2019-10-31 15:04:48 -0700282
283 Rule rule = (Rule) target;
284 AttributeContainer cont = rule.getAttributeContainer();
285 for (Attribute attr : rule.getAttributes()) {
286 if (!Character.isAlphabetic(attr.getName().charAt(0))) {
287 continue;
288 }
289
290 if (attr.getName().equals("distribs")) {
291 // attribute distribs: cannot represent type class java.util.Collections$SingletonSet
gregceca48e9a2020-04-14 08:54:38 -0700292 // in Starlark: [INTERNAL].
cparsonsd6cc76d2019-10-31 15:04:48 -0700293 continue;
294 }
295
296 try {
gregced281df72020-05-11 12:27:06 -0700297 Object val = starlarkifyValue(mu, cont.getAttr(attr.getName()), target.getPackage());
cparsonsd6cc76d2019-10-31 15:04:48 -0700298 if (val == null) {
299 continue;
300 }
adonovan7891d3b2020-01-22 12:40:50 -0800301 values.put(attr.getName(), val, (Location) null);
cparsonsd6cc76d2019-10-31 15:04:48 -0700302 } catch (NotRepresentableException e) {
303 throw new NotRepresentableException(
304 String.format(
305 "target %s, attribute %s: %s", target.getName(), attr.getName(), e.getMessage()));
306 }
307 }
308
adonovan7891d3b2020-01-22 12:40:50 -0800309 values.put("name", rule.getName(), (Location) null);
310 values.put("kind", rule.getRuleClass(), (Location) null);
cparsonsd6cc76d2019-10-31 15:04:48 -0700311 return values;
312 }
313
314 /**
Googler48b446e2019-11-07 11:31:22 -0800315 * Converts a target attribute value to a Starlark value for return in {@code
316 * native.existing_rule()} or {@code native.existing_rules()}.
cparsonsd6cc76d2019-10-31 15:04:48 -0700317 *
Googlerbd5bc962019-11-08 14:21:14 -0800318 * <p>Any dict values in the result have mutability {@code mu}.
Googler48b446e2019-11-07 11:31:22 -0800319 *
320 * @return the value, or null if we don't want to export it to the user.
321 * @throws NotRepresentableException if an unknown type is encountered.
cparsonsd6cc76d2019-10-31 15:04:48 -0700322 */
323 @Nullable
gregced281df72020-05-11 12:27:06 -0700324 private static Object starlarkifyValue(Mutability mu, Object val, Package pkg)
Googlerbd5bc962019-11-08 14:21:14 -0800325 throws NotRepresentableException {
cparsonsd6cc76d2019-10-31 15:04:48 -0700326 if (val == null) {
327 return null;
328 }
329 if (val instanceof Boolean) {
330 return val;
331 }
332 if (val instanceof Integer) {
333 return val;
334 }
335 if (val instanceof String) {
336 return val;
337 }
338
339 if (val instanceof TriState) {
340 switch ((TriState) val) {
341 case AUTO:
342 return -1;
343 case YES:
344 return 1;
345 case NO:
346 return 0;
347 }
348 }
349
350 if (val instanceof Label) {
351 Label l = (Label) val;
352 if (l.getPackageName().equals(pkg.getName())) {
353 return ":" + l.getName();
354 }
355 return l.getCanonicalForm();
356 }
357
358 if (val instanceof List) {
359 List<Object> l = new ArrayList<>();
360 for (Object o : (List) val) {
gregced281df72020-05-11 12:27:06 -0700361 Object elt = starlarkifyValue(mu, o, pkg);
cparsonsd6cc76d2019-10-31 15:04:48 -0700362 if (elt == null) {
363 continue;
364 }
365
366 l.add(elt);
367 }
368
Googlercfd681f2019-11-11 07:24:02 -0800369 return Tuple.copyOf(l);
cparsonsd6cc76d2019-10-31 15:04:48 -0700370 }
371 if (val instanceof Map) {
372 Map<Object, Object> m = new TreeMap<>();
373 for (Map.Entry<?, ?> e : ((Map<?, ?>) val).entrySet()) {
gregced281df72020-05-11 12:27:06 -0700374 Object key = starlarkifyValue(mu, e.getKey(), pkg);
375 Object mapVal = starlarkifyValue(mu, e.getValue(), pkg);
cparsonsd6cc76d2019-10-31 15:04:48 -0700376
377 if (key == null || mapVal == null) {
378 continue;
379 }
380
381 m.put(key, mapVal);
382 }
Googler084b64b2019-11-19 14:41:30 -0800383 return Starlark.fromJava(m, mu);
cparsonsd6cc76d2019-10-31 15:04:48 -0700384 }
385 if (val.getClass().isAnonymousClass()) {
386 // Computed defaults. They will be represented as
387 // "deprecation": com.google.devtools.build.lib.analysis.BaseRuleClasses$2@6960884a,
388 // Filter them until we invent something more clever.
389 return null;
390 }
391
392 if (val instanceof License) {
393 // License is deprecated as a Starlark type, so omit this type from Starlark values
Googler34f70582019-11-25 12:27:34 -0800394 // to avoid exposing these objects, even though they are technically StarlarkValue.
cparsonsd6cc76d2019-10-31 15:04:48 -0700395 return null;
396 }
397
Googler34f70582019-11-25 12:27:34 -0800398 if (val instanceof StarlarkValue) {
cparsonsd6cc76d2019-10-31 15:04:48 -0700399 return val;
400 }
401
402 if (val instanceof BuildType.SelectorList) {
403 // This is terrible:
404 // 1) this value is opaque, and not a BUILD value, so it cannot be used in rule arguments
405 // 2) its representation has a pointer address, so it breaks hermeticity.
406 //
407 // Even though this is clearly imperfect, we return this value because otherwise
408 // native.rules() fails if there is any rule using a select() in the BUILD file.
409 //
adonovand8ee69a2020-01-28 11:47:54 -0800410 // To remedy this, we should return a SelectorList. To do so, we have to
gregceca48e9a2020-04-14 08:54:38 -0700411 // 1) recurse into the Selector contents of SelectorList, so those values are Starlarkified
412 // too
cparsonsd6cc76d2019-10-31 15:04:48 -0700413 // 2) get the right Class<?> value. We could probably get at that by looking at
414 // ((SelectorList)val).getSelectors().first().getEntries().first().getClass().
415
416 return val;
417 }
418
419 // We are explicit about types we don't understand so we minimize changes to existing callers
420 // if we add more types that we can represent.
421 throw new NotRepresentableException(
422 String.format("cannot represent %s (%s) in Starlark", val, val.getClass()));
423 }
michajloe2fce442020-02-20 16:59:00 -0800424
425 private static class NotRepresentableException extends EvalException {
426 NotRepresentableException(String msg) {
427 super(null, msg);
428 }
429 }
Googlerd1c80612015-03-23 13:08:09 +0000430}