blob: 63909262a4ada01c7a903e994a2eca6bd2eb813f [file] [log] [blame]
// Copyright 2020 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.packages;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.License.DistributionType;
import com.google.devtools.build.lib.server.FailureDetails.PackageLoading.Code;
import java.util.List;
import java.util.Map;
import java.util.Set;
import net.starlark.java.eval.Dict;
import net.starlark.java.eval.EvalException;
import net.starlark.java.eval.Printer;
import net.starlark.java.eval.Starlark;
import net.starlark.java.eval.StarlarkCallable;
import net.starlark.java.eval.StarlarkThread;
import net.starlark.java.eval.Tuple;
import net.starlark.java.syntax.Location;
/**
* Utility class encapsulating the definition of the {@code package()} function of BUILD files.
*
* <p>Also includes the definitions of those arguments to {@code package()} that are available in
* all Bazel environments.
*/
public class PackageCallable {
private PackageCallable() {}
/**
* Returns a {@link StarlarkCallable} that implements the {@code package()} functionality.
*
* @param packageArgs a list of {@link PackageArgument}s to support, beyond the standard ones
* included in every Bazel environment
*/
// TODO(b/280446865): Consider eliminating the package() extensibility mechanism altogether.
// There is currently only one use case: distinguishing the set of package() arguments available
// in OSS Bazel vs internally to Google. Instead of registering these arguments and passing them
// to this factory method to obtain a package() callable, we could instead define two
// @StarlarkMethod-annotated Java functions implementing the two versions of package(), and
// register the appropriate one with the ConfiguredRuleClassProvider.Builder.
public static StarlarkCallable newPackageCallable(List<PackageArgument<?>> packageArgs) {
// Construct a map of PackageArguments, which the returned callable closes over.
ImmutableMap.Builder<String, PackageArgument<?>> argsBuilder = ImmutableMap.builder();
for (PackageArgument<?> arg : getCommonArguments()) {
argsBuilder.put(arg.getName(), arg);
}
for (PackageArgument<?> arg : packageArgs) {
argsBuilder.put(arg.getName(), arg);
}
final ImmutableMap<String, PackageArgument<?>> packageArguments = argsBuilder.buildOrThrow();
return new StarlarkCallable() {
@Override
public String getName() {
return "package";
}
@Override
public String toString() {
return "package(...)";
}
@Override
public boolean isImmutable() {
return true;
}
@Override
public void repr(Printer printer) {
printer.append("<built-in function package>");
}
@Override
public Object call(StarlarkThread thread, Tuple args, Dict<String, Object> kwargs)
throws EvalException {
if (!args.isEmpty()) {
throw new EvalException("unexpected positional arguments");
}
Package.Builder pkgBuilder = PackageFactory.getContext(thread).pkgBuilder;
// Validate parameter list
if (pkgBuilder.isPackageFunctionUsed()) {
throw new EvalException("'package' can only be used once per BUILD file");
}
pkgBuilder.setPackageFunctionUsed();
// Each supplied argument must name a PackageArgument.
if (kwargs.isEmpty()) {
throw new EvalException("at least one argument must be given to the 'package' function");
}
Location loc = thread.getCallerLocation();
for (Map.Entry<String, Object> kwarg : kwargs.entrySet()) {
String name = kwarg.getKey();
PackageArgument<?> pkgarg = packageArguments.get(name);
if (pkgarg == null) {
throw Starlark.errorf("unexpected keyword argument: %s", name);
}
pkgarg.convertAndProcess(pkgBuilder, loc, kwarg.getValue());
}
return Starlark.NONE;
}
};
}
/** Returns the basic set of {@link PackageArgument}s. */
private static ImmutableList<PackageArgument<?>> getCommonArguments() {
return ImmutableList.of(
new DefaultDeprecation(),
new DefaultDistribs(),
new DefaultApplicableLicenses(),
new DefaultPackageMetadata(),
new DefaultLicenses(),
new DefaultTestOnly(),
new DefaultVisibility(),
new Features(),
new DefaultCompatibleWith(),
new DefaultRestrictedTo());
}
private static class DefaultVisibility extends PackageArgument<List<Label>> {
private DefaultVisibility() {
super("default_visibility", BuildType.LABEL_LIST);
}
@Override
protected void process(Package.Builder pkgBuilder, Location location, List<Label> value)
throws EvalException {
pkgBuilder.setDefaultVisibility(RuleVisibility.parse(value));
}
}
private static class DefaultTestOnly extends PackageArgument<Boolean> {
private DefaultTestOnly() {
super("default_testonly", Type.BOOLEAN);
}
@Override
protected void process(Package.Builder pkgBuilder, Location location, Boolean value) {
pkgBuilder.setDefaultTestonly(value);
}
}
private static class DefaultDeprecation extends PackageArgument<String> {
private DefaultDeprecation() {
super("default_deprecation", Type.STRING);
}
@Override
protected void process(Package.Builder pkgBuilder, Location location, String value) {
pkgBuilder.setDefaultDeprecation(value);
}
}
private static class Features extends PackageArgument<List<String>> {
private Features() {
super("features", Type.STRING_LIST);
}
@Override
protected void process(Package.Builder pkgBuilder, Location location, List<String> value) {
pkgBuilder.addFeatures(value);
}
}
/**
* Declares the package() attribute specifying the default value for {@link
* com.google.devtools.build.lib.packages.RuleClass#APPLICABLE_LICENSES_ATTR} when not explicitly
* specified.
*/
private static class DefaultApplicableLicenses extends PackageArgument<List<Label>> {
private DefaultApplicableLicenses() {
super("default_applicable_licenses", BuildType.LABEL_LIST);
}
@Override
protected void process(Package.Builder pkgBuilder, Location location, List<Label> value) {
if (!pkgBuilder.getDefaultPackageMetadata().isEmpty()) {
pkgBuilder.addEvent(
Package.error(
location,
"Can not set both default_package_metadata and default_applicable_licenses."
+ " Move all declarations to default_package_metadata.",
Code.INVALID_PACKAGE_SPECIFICATION));
}
pkgBuilder.setDefaultPackageMetadata(value, "default_package_metadata", location);
}
}
/**
* Declares the package() attribute specifying the default value for {@link
* com.google.devtools.build.lib.packages.RuleClass#APPLICABLE_LICENSES_ATTR} when not explicitly
* specified.
*/
private static class DefaultPackageMetadata extends PackageArgument<List<Label>> {
private static final String DEFAULT_PACKAGE_METADATA_ATTRIBUTE = "default_package_metadata";
private DefaultPackageMetadata() {
super(DEFAULT_PACKAGE_METADATA_ATTRIBUTE, BuildType.LABEL_LIST);
}
@Override
protected void process(Package.Builder pkgBuilder, Location location, List<Label> value) {
if (!pkgBuilder.getDefaultPackageMetadata().isEmpty()) {
pkgBuilder.addEvent(
Package.error(
location,
"Can not set both default_package_metadata and default_applicable_licenses."
+ " Move all declarations to default_package_metadata.",
Code.INVALID_PACKAGE_SPECIFICATION));
}
pkgBuilder.setDefaultPackageMetadata(value, DEFAULT_PACKAGE_METADATA_ATTRIBUTE, location);
}
}
private static class DefaultLicenses extends PackageArgument<License> {
private DefaultLicenses() {
super("licenses", BuildType.LICENSE);
}
@Override
protected void process(Package.Builder pkgBuilder, Location location, License value) {
pkgBuilder.setDefaultLicense(value);
}
}
private static class DefaultDistribs extends PackageArgument<Set<DistributionType>> {
private DefaultDistribs() {
super("distribs", BuildType.DISTRIBUTIONS);
}
@Override
protected void process(
Package.Builder pkgBuilder, Location location, Set<DistributionType> value) {
pkgBuilder.setDefaultDistribs(value);
}
}
/**
* Declares the package() attribute specifying the default value for {@link
* com.google.devtools.build.lib.packages.RuleClass#COMPATIBLE_ENVIRONMENT_ATTR} when not
* explicitly specified.
*/
private static class DefaultCompatibleWith extends PackageArgument<List<Label>> {
private static final String DEFAULT_COMPATIBLE_WITH_ATTRIBUTE = "default_compatible_with";
private DefaultCompatibleWith() {
super(DEFAULT_COMPATIBLE_WITH_ATTRIBUTE, BuildType.LABEL_LIST);
}
@Override
protected void process(Package.Builder pkgBuilder, Location location, List<Label> value) {
pkgBuilder.setDefaultCompatibleWith(value, DEFAULT_COMPATIBLE_WITH_ATTRIBUTE, location);
}
}
/**
* Declares the package() attribute specifying the default value for {@link
* com.google.devtools.build.lib.packages.RuleClass#RESTRICTED_ENVIRONMENT_ATTR} when not
* explicitly specified.
*/
private static class DefaultRestrictedTo extends PackageArgument<List<Label>> {
private static final String DEFAULT_RESTRICTED_TO_ATTRIBUTE = "default_restricted_to";
private DefaultRestrictedTo() {
super(DEFAULT_RESTRICTED_TO_ATTRIBUTE, BuildType.LABEL_LIST);
}
@Override
protected void process(Package.Builder pkgBuilder, Location location, List<Label> value) {
pkgBuilder.setDefaultRestrictedTo(value, DEFAULT_RESTRICTED_TO_ATTRIBUTE, location);
}
}
}