Skip to content

Commit

Permalink
RuleVisibility concatenation doesn't add redundant items
Browse files Browse the repository at this point in the history
This is useful if you have several nested symbolic macros, since it avoids generating a long visibility value like `["//A:__pkg__", "//A:__pkg__", ..., "//A:__pkg__"]` when each macro's body is defined in the same package `A`.

Also clarify the purpose of the label accessors on `RuleVisibility` and flip their declaration order.

Work toward #19922.

PiperOrigin-RevId: 679962558
Change-Id: Iebb55d0256d72d7bc0a8b575446dea83c9bbf632
  • Loading branch information
brandjon authored and copybara-github committed Sep 28, 2024
1 parent 5bc9b65 commit 6f705ce
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,20 @@
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__".
* Returns the list of all labels comprising this visibility.
*
* <p>This includes labels that are not loadable, such as //visibility:public and //foo:__pkg__.
*/
List<Label> getDependencyLabels();
List<Label> getDeclaredLabels();

/**
* 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.
* Same as {@link #getDeclaredLabels}, but excludes labels that cannot be loaded.
*
* <p>I.e., this returns the labels of the top-level {@code package_group}s that must be loaded in
* order to determine the complete set of packages represented by this visibility. (Additional
* {@code package_group}s may need to be loaded due to their {@code includes} attribute.)
*/
List<Label> getDeclaredLabels();
List<Label> getDependencyLabels();

@SerializationConstant Label PUBLIC_LABEL = Label.parseCanonicalUnchecked("//visibility:public");

Expand All @@ -58,13 +58,13 @@ public interface RuleVisibility {
RuleVisibility PUBLIC =
new RuleVisibility() {
@Override
public ImmutableList<Label> getDependencyLabels() {
return ImmutableList.of();
public ImmutableList<Label> getDeclaredLabels() {
return ImmutableList.of(PUBLIC_LABEL);
}

@Override
public ImmutableList<Label> getDeclaredLabels() {
return ImmutableList.of(PUBLIC_LABEL);
public ImmutableList<Label> getDependencyLabels() {
return ImmutableList.of();
}

@Override
Expand All @@ -77,13 +77,13 @@ public String toString() {
RuleVisibility PRIVATE =
new RuleVisibility() {
@Override
public ImmutableList<Label> getDependencyLabels() {
return ImmutableList.of();
public ImmutableList<Label> getDeclaredLabels() {
return ImmutableList.of(PRIVATE_LABEL);
}

@Override
public ImmutableList<Label> getDeclaredLabels() {
return ImmutableList.of(PRIVATE_LABEL);
public ImmutableList<Label> getDependencyLabels() {
return ImmutableList.of();
}

@Override
Expand Down Expand Up @@ -161,6 +161,9 @@ static void validate(List<Label> labels) throws EvalException {
* or {@code visibility} is private, the result is {@code visibility} or a visibility consisting
* solely of {@code element}, respectively.
*
* <p>If {@code element} is already present in {@code visibility}, the result is just {@code
* visibility}.
*
* @throws EvalException if there's a problem parsing {@code element} into a visibility
*/
static RuleVisibility concatWithElement(RuleVisibility visibility, Label element)
Expand All @@ -175,10 +178,15 @@ static RuleVisibility concatWithElement(RuleVisibility visibility, Label element
// Public is idempotent.
return RuleVisibility.PUBLIC;
} else {
ImmutableList.Builder<Label> items = new ImmutableList.Builder<>();
items.addAll(visibility.getDeclaredLabels());
items.add(element);
return parse(items.build());
List<Label> items = visibility.getDeclaredLabels();
if (items.contains(element)) {
return visibility;
} else {
ImmutableList.Builder<Label> newItems = new ImmutableList.Builder<>();
newItems.addAll(items);
newItems.add(element);
return parse(newItems.build());
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1693,9 +1693,10 @@ def _impl(name, visibility):
Package pkg = loadPackageAndAssertSuccess("pkg");
assertVisibilityIs(pkg.getTarget("foo_public"), "//visibility:public");
assertVisibilityIs(pkg.getTarget("foo_private"), "//lib:__pkg__");
// Visibility is a label_list. Label lists don't do duplicate elimination. (Nor can we eliminate
// all logical redundancies anyway, since visibilities may refer to redundant package groups.)
assertVisibilityIs(pkg.getTarget("foo_selfvisible"), "//lib:__pkg__", "//lib:__pkg__");
// The visibility concatenation operation does not add any label that would duplicate an
// existing one. (Note that we can't eliminate *all* possible redundancy, since the visibility
// list's semantics depend on expanding package_groups.)
assertVisibilityIs(pkg.getTarget("foo_selfvisible"), "//lib:__pkg__");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package com.google.devtools.build.lib.packages;

import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.lib.packages.RuleVisibility.concatWithElement;

import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.cmdline.Label;
Expand Down Expand Up @@ -44,43 +45,42 @@ private static void assertEqual(RuleVisibility vis1, RuleVisibility vis2) {
assertThat(vis1.getDeclaredLabels()).isEqualTo(vis2.getDeclaredLabels());
}

private static final String A = "//a:__pkg__";
// Package group labels are represented differently than __pkg__ labels, so cover both cases.
private static final String B = "//b:pkggroup";
private static final String C = "//c:__pkg__";
private static final String PUBLIC = "//visibility:public";
private static final String PRIVATE = "//visibility:private";
private static final RuleVisibility PUBLIC_VIS = RuleVisibility.PUBLIC;
private static final RuleVisibility PRIVATE_VIS = RuleVisibility.PRIVATE;

@Test
public void concatenation() throws Exception {
RuleVisibility normalVis = ruleVisibility("//a:__pkg__", "//b:__pkg__");
RuleVisibility publicVis = RuleVisibility.PUBLIC;
RuleVisibility privateVis = RuleVisibility.PRIVATE;
// Technically the empty visibility is a distinct object from private visibility, even though
// it has the same semantics.
RuleVisibility emptyVis = ruleVisibility();

assertEqual(
RuleVisibility.concatWithElement(normalVis, label("//c:__pkg__")),
ruleVisibility("//a:__pkg__", "//b:__pkg__", "//c:__pkg__"));
assertEqual(
RuleVisibility.concatWithElement(normalVis, label("//visibility:public")), publicVis);
assertEqual(
RuleVisibility.concatWithElement(normalVis, label("//visibility:private")), normalVis);
assertEqual(concatWithElement(ruleVisibility(A, B), label(C)), ruleVisibility(A, B, C));
assertEqual(concatWithElement(ruleVisibility(A, B), label(PUBLIC)), PUBLIC_VIS);
assertEqual(concatWithElement(ruleVisibility(A, B), label(PRIVATE)), ruleVisibility(A, B));

assertEqual(RuleVisibility.concatWithElement(publicVis, label("//c:__pkg__")), publicVis);
assertEqual(
RuleVisibility.concatWithElement(publicVis, label("//visibility:public")), publicVis);
assertEqual(
RuleVisibility.concatWithElement(publicVis, label("//visibility:private")), publicVis);
assertEqual(concatWithElement(PUBLIC_VIS, label(C)), PUBLIC_VIS);
assertEqual(concatWithElement(PUBLIC_VIS, label(PUBLIC)), PUBLIC_VIS);
assertEqual(concatWithElement(PUBLIC_VIS, label(PRIVATE)), PUBLIC_VIS);

assertEqual(
RuleVisibility.concatWithElement(privateVis, label("//c:__pkg__")),
ruleVisibility("//c:__pkg__"));
assertEqual(
RuleVisibility.concatWithElement(privateVis, label("//visibility:public")), publicVis);
assertEqual(
RuleVisibility.concatWithElement(privateVis, label("//visibility:private")), privateVis);
assertEqual(concatWithElement(PRIVATE_VIS, label(C)), ruleVisibility(C));
assertEqual(concatWithElement(PRIVATE_VIS, label(PUBLIC)), PUBLIC_VIS);
assertEqual(concatWithElement(PRIVATE_VIS, label(PRIVATE)), PRIVATE_VIS);

assertEqual(concatWithElement(emptyVis, label(C)), ruleVisibility(C));
assertEqual(concatWithElement(emptyVis, label(PUBLIC)), PUBLIC_VIS);
assertEqual(concatWithElement(emptyVis, label(PRIVATE)), emptyVis);

// Duplicates are not added, though they are preserved.
assertEqual(concatWithElement(ruleVisibility(A, B), label(A)), ruleVisibility(A, B));
assertEqual(
RuleVisibility.concatWithElement(emptyVis, label("//c:__pkg__")),
ruleVisibility("//c:__pkg__"));
assertEqual(
RuleVisibility.concatWithElement(emptyVis, label("//visibility:public")), publicVis);
concatWithElement(ruleVisibility(A, B, B, A), label(A)), ruleVisibility(A, B, B, A));
assertEqual(
RuleVisibility.concatWithElement(emptyVis, label("//visibility:private")), emptyVis);
concatWithElement(ruleVisibility(A, B, B, A), label(C)), ruleVisibility(A, B, B, A, C));
}
}

0 comments on commit 6f705ce

Please sign in to comment.