blob: c146a7c7b3a290e0b2639cb0a054894a1ddf57e7 [file] [log] [blame]
// Copyright 2014 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.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.SerializationConstant;
import java.util.List;
import javax.annotation.Nullable;
import net.starlark.java.eval.EvalException;
import net.starlark.java.eval.Starlark;
/**
* A RuleVisibility specifies which other rules can depend on a specified rule. Note that the actual
* method that performs this check is declared in RuleConfiguredTargetVisibility.
*
* <p>The conversion to ConfiguredTargetVisibility is handled in an ugly if-ladder, because I want
* to avoid this package depending on build.lib.view.
*
* <p>All implementations of this interface are immutable.
*/
public interface RuleVisibility {
/**
* Returns the list of labels that need to be loaded so that the visibility decision can be made
* during analysis time. E.g. for package group visibility, this is the list of package groups
* referenced. Does not include labels that have special meanings in the visibility declaration,
* e.g. "//visibility:*" or "//*:__pkg__".
*/
List<Label> getDependencyLabels();
/**
* Returns the list of labels used during the declaration of this visibility. These do not
* necessarily represent loadable labels: for example, for public or private visibilities, the
* special labels "//visibility:*" will be returned, and so will be the special "//*:__pkg__"
* labels indicating a single package.
*/
List<Label> getDeclaredLabels();
@SerializationConstant Label PUBLIC_LABEL = Label.parseCanonicalUnchecked("//visibility:public");
@SerializationConstant
Label PRIVATE_LABEL = Label.parseCanonicalUnchecked("//visibility:private");
@SerializationConstant
RuleVisibility PUBLIC =
new RuleVisibility() {
@Override
public ImmutableList<Label> getDependencyLabels() {
return ImmutableList.of();
}
@Override
public ImmutableList<Label> getDeclaredLabels() {
return ImmutableList.of(PUBLIC_LABEL);
}
@Override
public String toString() {
return PUBLIC_LABEL.toString();
}
};
@SerializationConstant
RuleVisibility PRIVATE =
new RuleVisibility() {
@Override
public ImmutableList<Label> getDependencyLabels() {
return ImmutableList.of();
}
@Override
public ImmutableList<Label> getDeclaredLabels() {
return ImmutableList.of(PRIVATE_LABEL);
}
@Override
public String toString() {
return PRIVATE_LABEL.toString();
}
};
/** Validates and parses the given labels into a {@link RuleVisibility}. */
static RuleVisibility parse(List<Label> labels) throws EvalException {
validate(labels);
return parseUnchecked(labels);
}
/**
* Same as {@link #parse} except does not perform validation checks.
*
* <p>Use only after the given labels have been {@linkplain #validate validated}.
*/
static RuleVisibility parseUnchecked(List<Label> labels) {
RuleVisibility result = parseIfConstant(labels);
if (result != null) {
return result;
}
return PackageGroupsRuleVisibility.create(labels);
}
/**
* If the given list of labels represents a constant {@link RuleVisibility} ({@link #PUBLIC} or
* {@link #PRIVATE}), returns that visibility instance, otherwise returns {@code null}.
*
* <p>Use only after the given labels have been {@linkplain #validate validated}.
*/
@Nullable
static RuleVisibility parseIfConstant(List<Label> labels) {
if (labels.size() != 1) {
return null;
}
Label label = labels.get(0);
if (label.equals(PUBLIC_LABEL)) {
return PUBLIC;
}
if (label.equals(PRIVATE_LABEL)) {
return PRIVATE;
}
return null;
}
static void validate(List<Label> labels) throws EvalException {
for (Label label : labels) {
if (label.equals(PUBLIC_LABEL) || label.equals(PRIVATE_LABEL)) {
if (labels.size() > 1) {
throw Starlark.errorf(
"//visibility:public and //visibility:private cannot be used in combination with"
+ " other labels");
}
} else if (label.getPackageIdentifier().equals(PUBLIC_LABEL.getPackageIdentifier())
&& PackageSpecification.fromLabel(label) == null) {
// In other words, if the label is in //visibility and is not //visibility:public,
// //visibility:private, or (for the unusual case where //visibility
// exists as a package) //visibility:__pkg__ or //visibility:__subpackages__
throw Starlark.errorf(
"Invalid visibility label '%s'; did you mean //visibility:public or"
+ " //visibility:private?",
label);
}
}
}
}