blob: 4c0d9adced38dda465dd979201f606c5201d71e1 [file] [log] [blame]
// Copyright 2019 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.analysis;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import com.google.devtools.build.lib.analysis.util.AnalysisTestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Test for visibility of targets. */
@RunWith(JUnit4.class)
public class VisibilityTest extends AnalysisTestCase {
void setupArgsScenario() throws Exception {
scratch.file("tool/tool.sh", "#!/bin/sh", "echo Hello > $2", "cat $1 >> $2");
scratch.file("rule/BUILD");
scratch.file(
"rule/rule.bzl",
"""
def _impl(ctx):
output = ctx.actions.declare_file(ctx.label.name + ".out")
ctx.actions.run(
inputs = ctx.files._tool + ctx.files.data,
executable = ctx.files._tool[0].path,
arguments = [f.path for f in ctx.files.data] + [output.path],
outputs = [output],
)
greet = rule(
implementation = _impl,
attrs = {
"data": attr.label(allow_files = True),
"_tool": attr.label(
cfg = "exec",
allow_files = True,
default = Label("//tool:tool.sh"),
),
},
outputs = {"out": "%{name}.out"},
)
""");
scratch.file("data/data.txt", "World");
scratch.file(
"use/BUILD",
"""
load("//rule:rule.bzl", "greet")
greet(
name = "world",
data = "//data:data.txt",
)
""");
}
@Test
public void testToolVisibilityRuleCheckAtRule() throws Exception {
setupArgsScenario();
scratch.file("data/BUILD", "exports_files(['data.txt'], visibility=['//visibility:public'])");
scratch.file("tool/BUILD", "exports_files(['tool.sh'], visibility=['//rule:__pkg__'])");
update("//use:world");
assertThat(hasErrors(getConfiguredTarget("//use:world"))).isFalse();
}
@Test
public void testToolVisibilityUseCheckAtUse() throws Exception {
setupArgsScenario();
scratch.file("data/BUILD", "exports_files(['data.txt'], visibility=['//visibility:public'])");
scratch.file("tool/BUILD", "exports_files(['tool.sh'], visibility=['//use:__pkg__'])");
update("//use:world");
assertThat(hasErrors(getConfiguredTarget("//use:world"))).isFalse();
}
@Test
public void testToolVisibilityUseCheckAtRule_fallbackToUse() throws Exception {
setupArgsScenario();
scratch.file("data/BUILD", "exports_files(['data.txt'], visibility=['//visibility:public'])");
scratch.file("tool/BUILD", "exports_files(['tool.sh'], visibility=['//use:__pkg__'])");
update("//use:world");
assertThat(hasErrors(getConfiguredTarget("//use:world"))).isFalse();
}
@Test
public void testToolVisibilityPrivateCheckAtUse() throws Exception {
setupArgsScenario();
scratch.file("data/BUILD", "exports_files(['data.txt'], visibility=['//visibility:public'])");
scratch.file("tool/BUILD", "exports_files(['tool.sh'], visibility=['//visibility:private'])");
reporter.removeHandler(failFastHandler);
assertThrows(ViewCreationFailedException.class, () -> update("//use:world"));
}
@Test
public void testToolVisibilityPrivate() throws Exception {
setupArgsScenario();
scratch.file("data/BUILD", "exports_files(['data.txt'], visibility=['//visibility:public'])");
scratch.file("tool/BUILD", "exports_files(['tool.sh'], visibility=['//visibility:private'])");
reporter.removeHandler(failFastHandler);
assertThrows(ViewCreationFailedException.class, () -> update("//use:world"));
}
@Test
public void testDataVisibilityUseCheckPrivateAtRule() throws Exception {
setupArgsScenario();
scratch.file("data/BUILD", "exports_files(['data.txt'], visibility=['//use:__pkg__'])");
scratch.file("tool/BUILD", "exports_files(['tool.sh'], visibility=['//visibility:public'])");
update("//use:world");
assertThat(hasErrors(getConfiguredTarget("//use:world"))).isFalse();
}
@Test
public void testDataVisibilityPrivate() throws Exception {
setupArgsScenario();
scratch.file("data/BUILD", "exports_files(['data.txt'], visibility=['//visibility:private'])");
scratch.file("tool/BUILD", "exports_files(['tool.sh'], visibility=['//visibility:public'])");
reporter.removeHandler(failFastHandler);
assertThrows(ViewCreationFailedException.class, () -> update("//use:world"));
}
@Test
public void testConfigSettingVisibilityAlwaysCheckedAtUse() throws Exception {
scratch.file(
"BUILD",
"load('//build_defs:defs.bzl', 'my_rule')",
"my_rule(",
" name = 'my_target',",
" value = select({",
" '//config_setting:my_setting': 'foo',",
" '//conditions:default': 'bar',",
" }),",
")");
scratch.file("build_defs/BUILD");
scratch.file(
"build_defs/defs.bzl",
"""
def _my_rule_impl(ctx):
pass
my_rule = rule(
implementation = _my_rule_impl,
attrs = {
"value": attr.string(mandatory = True),
},
)
""");
scratch.file(
"config_setting/BUILD",
"""
config_setting(
name = "my_setting",
values = {"compilation_mode": "dbg"},
visibility = ["//:__pkg__"],
)
""");
update("//:my_target");
assertThat(hasErrors(getConfiguredTarget("//:my_target"))).isFalse();
}
@Test
public void testImplicitDependency_samePackageAsDefinition_visible() throws Exception {
scratch.file(
"aspect_def/BUILD",
"""
load('//test_defs:foo_binary.bzl', 'foo_binary')
foo_binary(
name = "aspect_tool",
srcs = ["a.sh"],
visibility = ["//visibility:private"],
)
""");
scratch.file(
"aspect_def/lib.bzl",
"""
def _impl_my_aspect(ctx, target):
return []
my_aspect = aspect(
_impl_my_aspect,
attrs = {"_aspect_tool": attr.label(default = "//aspect_def:aspect_tool")},
)
""");
scratch.file(
"rule_def/BUILD",
"""
load('//test_defs:foo_binary.bzl', 'foo_binary')
foo_binary(
name = "rule_tool",
srcs = ["a.sh"],
visibility = ["//visibility:private"],
)
""");
scratch.file(
"rule_def/lib.bzl",
"""
load("//aspect_def:lib.bzl", "my_aspect")
def _impl(ctx):
pass
my_rule = rule(
_impl,
attrs = {
"dep": attr.label(aspects = [my_aspect]),
"_rule_tool": attr.label(default = "//rule_def:rule_tool"),
},
)
simple_starlark_rule = rule(
_impl,
)
""");
scratch.file(
"foo/BUILD",
"""
load("//rule_def:lib.bzl", "my_rule", "simple_starlark_rule")
simple_starlark_rule(name = "simple_dep")
my_rule(
name = "my_target",
dep = ":simple_dep",
)
""");
update("//foo:my_target");
assertThat(hasErrors(getConfiguredTarget("//foo:my_target"))).isFalse();
}
@Test
public void testAspectImplicitDependencyCheckedAtDefinition_visible() throws Exception {
scratch.file("inner_aspect/BUILD");
scratch.file(
"inner_aspect/lib.bzl",
"""
InnerAspectInfo = provider()
def _impl_inner_aspect(ctx, target):
return [InnerAspectInfo()]
inner_aspect = aspect(
_impl_inner_aspect,
attrs = {"_inner_aspect_tool": attr.label(default = "//tool:inner_aspect_tool")},
provides = [InnerAspectInfo],
)
""");
scratch.file("outer_aspect/BUILD");
scratch.file(
"outer_aspect/lib.bzl",
"""
load("//inner_aspect:lib.bzl", "InnerAspectInfo")
def _impl_outer_aspect(ctx, target):
return []
outer_aspect = aspect(
_impl_outer_aspect,
attrs = {"_outer_aspect_tool": attr.label(default = "//tool:outer_aspect_tool")},
required_aspect_providers = [InnerAspectInfo],
)
""");
scratch.file("rule/BUILD");
scratch.file(
"rule/lib.bzl",
"""
load("//inner_aspect:lib.bzl", "inner_aspect")
load("//outer_aspect:lib.bzl", "outer_aspect")
def _impl(ctx):
pass
my_rule = rule(
_impl,
attrs = {
"dep": attr.label(aspects = [inner_aspect, outer_aspect]),
"_rule_tool": attr.label(default = "//tool:rule_tool"),
},
)
simple_starlark_rule = rule(
_impl,
)
""");
scratch.file(
"foo/BUILD",
"""
load("//rule:lib.bzl", "my_rule", "simple_starlark_rule")
simple_starlark_rule(name = "simple_dep")
my_rule(
name = "target_with_aspects",
dep = ":simple_dep",
)
""");
scratch.file(
"tool/BUILD",
"""
load('//test_defs:foo_binary.bzl', 'foo_binary')
foo_binary(
name = "outer_aspect_tool",
srcs = ["a.sh"],
visibility = ["//outer_aspect:__pkg__"],
)
foo_binary(
name = "inner_aspect_tool",
srcs = ["a.sh"],
visibility = ["//inner_aspect:__pkg__"],
)
foo_binary(
name = "rule_tool",
srcs = ["a.sh"],
visibility = ["//rule:__pkg__"],
)
""");
update("//foo:target_with_aspects");
assertThat(hasErrors(getConfiguredTarget("//foo:target_with_aspects"))).isFalse();
}
@Test
public void testAspectImplicitDependencyCheckedAtDefinition_visibleWithNameCollision()
throws Exception {
scratch.file("inner_aspect/BUILD");
scratch.file(
"inner_aspect/lib.bzl",
"""
InnerAspectInfo = provider()
def _impl_inner_aspect(ctx, target):
return [InnerAspectInfo()]
inner_aspect = aspect(
_impl_inner_aspect,
attrs = {"_tool": attr.label(default = "//tool:inner_aspect_tool")},
provides = [InnerAspectInfo],
)
""");
scratch.file("outer_aspect/BUILD");
scratch.file(
"outer_aspect/lib.bzl",
"""
load("//inner_aspect:lib.bzl", "InnerAspectInfo")
def _impl_outer_aspect(ctx, target):
return []
outer_aspect = aspect(
_impl_outer_aspect,
attrs = {"_tool": attr.label(default = "//tool:outer_aspect_tool")},
required_aspect_providers = [InnerAspectInfo],
)
""");
scratch.file("rule/BUILD");
scratch.file(
"rule/lib.bzl",
"""
load("//inner_aspect:lib.bzl", "inner_aspect")
load("//outer_aspect:lib.bzl", "outer_aspect")
def _impl(ctx):
pass
my_rule = rule(
_impl,
attrs = {
"dep": attr.label(aspects = [inner_aspect, outer_aspect]),
"_tool": attr.label(default = "//tool:rule_tool"),
},
)
simple_starlark_rule = rule(
_impl,
)
""");
scratch.file(
"foo/BUILD",
"""
load("//rule:lib.bzl", "my_rule", "simple_starlark_rule")
simple_starlark_rule(name = "simple_dep")
my_rule(
name = "target_with_aspects",
dep = ":simple_dep",
)
""");
scratch.file(
"tool/BUILD",
"""
load('//test_defs:foo_binary.bzl', 'foo_binary')
foo_binary(
name = "outer_aspect_tool",
srcs = ["a.sh"],
visibility = ["//outer_aspect:__pkg__"],
)
foo_binary(
name = "inner_aspect_tool",
srcs = ["a.sh"],
visibility = ["//inner_aspect:__pkg__"],
)
foo_binary(
name = "rule_tool",
srcs = ["a.sh"],
visibility = ["//rule:__pkg__"],
)
""");
update("//foo:target_with_aspects");
assertThat(hasErrors(getConfiguredTarget("//foo:target_with_aspects"))).isFalse();
}
@Test
public void testAspectImplicitDependencyCheckedAtDefinition_outerAspectToolNotVisible()
throws Exception {
scratch.file("inner_aspect/BUILD");
scratch.file(
"inner_aspect/lib.bzl",
"""
InnerAspectInfo = provider()
def _impl_inner_aspect(ctx, target):
return [InnerAspectInfo()]
inner_aspect = aspect(
_impl_inner_aspect,
attrs = {"_inner_aspect_tool": attr.label(default = "//tool:inner_aspect_tool")},
provides = [InnerAspectInfo],
)
""");
scratch.file("outer_aspect/BUILD");
scratch.file(
"outer_aspect/lib.bzl",
"""
load("//inner_aspect:lib.bzl", "InnerAspectInfo")
def _impl_outer_aspect(ctx, target):
return []
outer_aspect = aspect(
_impl_outer_aspect,
attrs = {"_outer_aspect_tool": attr.label(default = "//tool:outer_aspect_tool")},
required_aspect_providers = [InnerAspectInfo],
)
""");
scratch.file("rule/BUILD");
scratch.file(
"rule/lib.bzl",
"""
load("//inner_aspect:lib.bzl", "inner_aspect")
load("//outer_aspect:lib.bzl", "outer_aspect")
def _impl(ctx):
pass
my_rule = rule(
_impl,
attrs = {
"dep": attr.label(aspects = [inner_aspect, outer_aspect]),
"_rule_tool": attr.label(default = "//tool:rule_tool"),
},
)
simple_starlark_rule = rule(
_impl,
)
""");
scratch.file(
"foo/BUILD",
"""
load("//rule:lib.bzl", "my_rule", "simple_starlark_rule")
simple_starlark_rule(name = "simple_dep")
my_rule(
name = "target_with_aspects",
dep = ":simple_dep",
)
""");
scratch.file(
"tool/BUILD",
"""
load('//test_defs:foo_binary.bzl', 'foo_binary')
foo_binary(
name = "outer_aspect_tool",
srcs = ["a.sh"],
visibility = [
"//inner_aspect:__pkg__",
"//rule:__pkg__",
],
)
foo_binary(
name = "inner_aspect_tool",
srcs = ["a.sh"],
visibility = ["//inner_aspect:__pkg__"],
)
foo_binary(
name = "rule_tool",
srcs = ["a.sh"],
visibility = ["//rule:__pkg__"],
)
""");
reporter.removeHandler(failFastHandler);
assertThrows(ViewCreationFailedException.class, () -> update("//foo:target_with_aspects"));
assertContainsEvent(
"in //inner_aspect:lib.bzl%inner_aspect,//outer_aspect:lib.bzl%outer_aspect "
+ "aspect on simple_starlark_rule rule //foo:simple_dep: Visibility error:\n"
+ "target '//tool:outer_aspect_tool' is not visible from\n"
+ "target '//outer_aspect:lib.bzl'");
}
@Test
public void testAspectImplicitDependencyCheckedAtDefinition_innerAspectToolNotVisible()
throws Exception {
scratch.file("inner_aspect/BUILD");
scratch.file(
"inner_aspect/lib.bzl",
"""
InnerAspectInfo = provider()
def _impl_inner_aspect(ctx, target):
return [InnerAspectInfo()]
inner_aspect = aspect(
_impl_inner_aspect,
attrs = {"_inner_aspect_tool": attr.label(default = "//tool:inner_aspect_tool")},
provides = [InnerAspectInfo],
)
""");
scratch.file("outer_aspect/BUILD");
scratch.file(
"outer_aspect/lib.bzl",
"""
load("//inner_aspect:lib.bzl", "InnerAspectInfo")
def _impl_outer_aspect(ctx, target):
return []
outer_aspect = aspect(
_impl_outer_aspect,
attrs = {"_outer_aspect_tool": attr.label(default = "//tool:outer_aspect_tool")},
required_aspect_providers = [InnerAspectInfo],
)
""");
scratch.file("rule/BUILD");
scratch.file(
"rule/lib.bzl",
"""
load("//inner_aspect:lib.bzl", "inner_aspect")
load("//outer_aspect:lib.bzl", "outer_aspect")
def _impl(ctx):
pass
my_rule = rule(
_impl,
attrs = {
"dep": attr.label(aspects = [inner_aspect, outer_aspect]),
"_rule_tool": attr.label(default = "//tool:rule_tool"),
},
)
simple_starlark_rule = rule(
_impl,
)
""");
scratch.file(
"foo/BUILD",
"""
load("//rule:lib.bzl", "my_rule", "simple_starlark_rule")
simple_starlark_rule(name = "simple_dep")
my_rule(
name = "target_with_aspects",
dep = ":simple_dep",
)
""");
scratch.file(
"tool/BUILD",
"""
load('//test_defs:foo_binary.bzl', 'foo_binary')
foo_binary(
name = "outer_aspect_tool",
srcs = ["a.sh"],
visibility = ["//outer_aspect:__pkg__"],
)
foo_binary(
name = "inner_aspect_tool",
srcs = ["a.sh"],
visibility = [
"//outer_aspect:__pkg__",
"//rule:__pkg__",
],
)
foo_binary(
name = "rule_tool",
srcs = ["a.sh"],
visibility = ["//rule:__pkg__"],
)
""");
reporter.removeHandler(failFastHandler);
assertThrows(ViewCreationFailedException.class, () -> update("//foo:target_with_aspects"));
assertContainsEvent(
"in //inner_aspect:lib.bzl%inner_aspect aspect on simple_starlark_rule "
+ "rule //foo:simple_dep: Visibility error:\n"
+ "target '//tool:inner_aspect_tool' is not visible from\n"
+ "target '//inner_aspect:lib.bzl'");
}
@Test
public void testAspectImplicitDependencyCheckedAtDefinition_ruleToolNotVisible()
throws Exception {
scratch.file("inner_aspect/BUILD");
scratch.file(
"inner_aspect/lib.bzl",
"""
InnerAspectInfo = provider()
def _impl_inner_aspect(ctx, target):
return [InnerAspectInfo()]
inner_aspect = aspect(
_impl_inner_aspect,
attrs = {"_inner_aspect_tool": attr.label(default = "//tool:inner_aspect_tool")},
provides = [InnerAspectInfo],
)
""");
scratch.file("outer_aspect/BUILD");
scratch.file(
"outer_aspect/lib.bzl",
"""
load("//inner_aspect:lib.bzl", "InnerAspectInfo")
def _impl_outer_aspect(ctx, target):
return []
outer_aspect = aspect(
_impl_outer_aspect,
attrs = {"_outer_aspect_tool": attr.label(default = "//tool:outer_aspect_tool")},
required_aspect_providers = [InnerAspectInfo],
)
""");
scratch.file("rule/BUILD");
scratch.file(
"rule/lib.bzl",
"""
load("//inner_aspect:lib.bzl", "inner_aspect")
load("//outer_aspect:lib.bzl", "outer_aspect")
def _impl(ctx):
pass
my_rule = rule(
_impl,
attrs = {
"dep": attr.label(aspects = [inner_aspect, outer_aspect]),
"_rule_tool": attr.label(default = "//tool:rule_tool"),
},
)
simple_starlark_rule = rule(
_impl,
)
""");
scratch.file(
"foo/BUILD",
"""
load("//rule:lib.bzl", "my_rule", "simple_starlark_rule")
simple_starlark_rule(name = "simple_dep")
my_rule(
name = "target_with_aspects",
dep = ":simple_dep",
)
""");
scratch.file(
"tool/BUILD",
"""
load('//test_defs:foo_binary.bzl', 'foo_binary')
foo_binary(
name = "outer_aspect_tool",
srcs = ["a.sh"],
visibility = ["//outer_aspect:__pkg__"],
)
foo_binary(
name = "inner_aspect_tool",
srcs = ["a.sh"],
visibility = ["//inner_aspect:__pkg__"],
)
foo_binary(
name = "rule_tool",
srcs = ["a.sh"],
visibility = [
"//inner_aspect:__pkg__",
"//outer_aspect:__pkg__",
],
)
""");
reporter.removeHandler(failFastHandler);
assertThrows(ViewCreationFailedException.class, () -> update("//foo:target_with_aspects"));
assertContainsEvent(
"in my_rule rule //foo:target_with_aspects: Visibility error:\n"
+ "target '//tool:rule_tool' is not visible from\n"
+ "target '//rule:lib.bzl'");
}
void setupFilesScenario(String wantRead) throws Exception {
scratch.file("src/source.txt", "source");
scratch.file("src/BUILD", "exports_files(['source.txt'], visibility=['//pkg:__pkg__'])");
scratch.file("pkg/foo.txt", "foo");
scratch.file("pkg/bar.txt", "bar");
scratch.file("pkg/groupfile.txt", "groupfile");
scratch.file("pkg/unused.txt", "unused");
scratch.file("pkg/exported.txt", "exported");
scratch.file(
"pkg/BUILD",
"""
package(default_visibility = ["//visibility:public"])
exports_files(["exported.txt"])
genrule(
name = "foobar",
srcs = [
"foo.txt",
"bar.txt",
],
outs = ["foobar.txt"],
cmd = "cat $(SRCS) > $@",
)
filegroup(
name = "remotegroup",
srcs = ["//src:source.txt"],
)
filegroup(
name = "localgroup",
srcs = [":groupfile.txt"],
)
""");
scratch.file(
"otherpkg/BUILD",
"genrule(",
" name = 'it',",
" srcs = ['//pkg:" + wantRead + "'],",
" outs = ['it.xt'],",
" cmd = 'cp $< $@',",
")");
}
@Test
public void testTargetImplicitExport() throws Exception {
setupFilesScenario("foobar");
useConfiguration("--noincompatible_no_implicit_file_export");
update("//otherpkg:it");
assertThat(hasErrors(getConfiguredTarget("//otherpkg:it"))).isFalse();
}
@Test
public void testTargetNoImplicitExport() throws Exception {
setupFilesScenario("foobar");
useConfiguration("--incompatible_no_implicit_file_export");
update("//otherpkg:it");
assertThat(hasErrors(getConfiguredTarget("//otherpkg:it"))).isFalse();
}
@Test
public void testLocalFilegroupImplicitExport() throws Exception {
setupFilesScenario("localgroup");
useConfiguration("--noincompatible_no_implicit_file_export");
update("//otherpkg:it");
assertThat(hasErrors(getConfiguredTarget("//otherpkg:it"))).isFalse();
}
@Test
public void testLocalFilegroupNoImplicitExport() throws Exception {
setupFilesScenario("localgroup");
useConfiguration("--incompatible_no_implicit_file_export");
update("//otherpkg:it");
assertThat(hasErrors(getConfiguredTarget("//otherpkg:it"))).isFalse();
}
@Test
public void testRemoteFilegroupImplicitExport() throws Exception {
setupFilesScenario("remotegroup");
useConfiguration("--noincompatible_no_implicit_file_export");
update("//otherpkg:it");
assertThat(hasErrors(getConfiguredTarget("//otherpkg:it"))).isFalse();
}
@Test
public void testRemoteFilegroupNoImplicitExport() throws Exception {
setupFilesScenario("remotegroup");
useConfiguration("--incompatible_no_implicit_file_export");
update("//otherpkg:it");
assertThat(hasErrors(getConfiguredTarget("//otherpkg:it"))).isFalse();
}
@Test
public void testExportedImplicitExport() throws Exception {
setupFilesScenario("exported.txt");
useConfiguration("--noincompatible_no_implicit_file_export");
update("//otherpkg:it");
assertThat(hasErrors(getConfiguredTarget("//otherpkg:it"))).isFalse();
}
@Test
public void testExportedNoImplicitExport() throws Exception {
setupFilesScenario("exported.txt");
useConfiguration("--incompatible_no_implicit_file_export");
update("//otherpkg:it");
assertThat(hasErrors(getConfiguredTarget("//otherpkg:it"))).isFalse();
}
@Test
public void testUnusedImplicitExport() throws Exception {
setupFilesScenario("unused.txt");
useConfiguration("--noincompatible_no_implicit_file_export");
reporter.removeHandler(failFastHandler);
assertThrows(ViewCreationFailedException.class, () -> update("//otherpkg:it"));
}
@Test
public void testUnusedNoImplicitExport() throws Exception {
setupFilesScenario("unused.txt");
useConfiguration("--incompatible_no_implicit_file_export");
reporter.removeHandler(failFastHandler);
assertThrows(ViewCreationFailedException.class, () -> update("//otherpkg:it"));
}
@Test
public void testSourcefileImplicitExport() throws Exception {
setupFilesScenario("foo.txt");
useConfiguration("--noincompatible_no_implicit_file_export");
update("//otherpkg:it");
assertThat(hasErrors(getConfiguredTarget("//otherpkg:it"))).isFalse();
}
@Test
public void testSourcefileNoImplicitExport() throws Exception {
setupFilesScenario("foo.txt");
useConfiguration("--incompatible_no_implicit_file_export");
reporter.removeHandler(failFastHandler);
assertThrows(ViewCreationFailedException.class, () -> update("//otherpkg:it"));
}
@Test
public void testVerboseDiagnostics_ruleImplicitDep() throws Exception {
useConfiguration("--verbose_visibility_errors");
reporter.removeHandler(failFastHandler);
scratch.file(
"tool/BUILD",
"""
cc_library(
name = "tool",
visibility = ["//visibility:private"],
)
""");
scratch.file("rule/BUILD");
scratch.file(
"rule/defs.bzl",
"""
def _impl(ctx):
pass
my_rule = rule(
implementation = _impl,
attrs = {"_implicit_dep": attr.label(default="//tool:tool")},
)
""");
scratch.file(
"pkg/BUILD",
"""
load("//rule:defs.bzl", "my_rule")
my_rule(name = "foo")
""");
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo"));
assertContainsEvent(
"""
* The dependency is an implicit dependency of the consuming target's rule, my_rule, which \
is defined in //rule:defs.bzl. Since that file's package, //rule, does not match\
""");
}
@Test
public void testVerboseDiagnostics_aspectImplicitDep() throws Exception {
useConfiguration("--verbose_visibility_errors");
reporter.removeHandler(failFastHandler);
scratch.file(
"tool/BUILD",
"""
cc_library(
name = "tool",
visibility = ["//visibility:private"],
)
""");
scratch.file("aspect/BUILD");
scratch.file(
"aspect/defs.bzl",
"""
def _impl(ctx):
pass
my_aspect = aspect(
implementation = _impl,
attrs = {"_implicit_dep": attr.label(default="//tool:tool")},
)
""");
scratch.file("rule/BUILD");
scratch.file(
"rule/defs.bzl",
"""
load("//aspect:defs.bzl", "my_aspect")
def _impl(ctx):
pass
my_rule = rule(
implementation = _impl,
attrs = {"deps": attr.label_list(aspects=[my_aspect])},
)
""");
scratch.file(
"pkg/BUILD",
"""
load("//rule:defs.bzl", "my_rule")
cc_library(name = "dep")
my_rule(
name = "foo",
deps = [":dep"],
)
""");
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo"));
assertContainsEvent(
"""
* The dependency is an implicit dependency of the consuming target's aspect, my_aspect, \
which is defined in //aspect:defs.bzl. Since that file's package, //aspect, does not\
""");
}
@Test
public void testVerboseDiagnostics_consumingLocation_isNotInMacro() throws Exception {
useConfiguration("--verbose_visibility_errors");
reporter.removeHandler(failFastHandler);
scratch.file(
"tool/BUILD",
"""
cc_library(
name = "tool",
visibility = ["//visibility:private"],
)
""");
scratch.file(
"pkg/BUILD",
"""
cc_library(
name = "foo",
deps = ["//tool:tool"],
)
""");
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo"));
assertContainsEvent(
"""
* The location being checked is the package where the consuming target lives, //pkg.\
""");
}
@Test
public void testVerboseDiagnostics_consumingLocation_isInMacro() throws Exception {
useConfiguration("--verbose_visibility_errors");
reporter.removeHandler(failFastHandler);
scratch.file(
"tool/BUILD",
"""
cc_library(
name = "tool",
visibility = ["//visibility:private"],
)
""");
scratch.file("macro/BUILD");
scratch.file(
"macro/defs.bzl",
"""
def _impl(name, visibility):
native.cc_library(
name = name + "_bar",
deps = ["//tool:tool"],
)
my_macro = macro(implementation = _impl)
""");
scratch.file(
"pkg/BUILD",
"""
load("//macro:defs.bzl", "my_macro")
my_macro(name = "foo")
""");
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo_bar"));
assertContainsEvent(
"""
* Because the consuming target was declared in the body of the symbolic macro my_macro \
defined in //macro:defs.bzl, the location being checked is this file's package, //macro.\
""");
}
@Test
public void testVerboseDiagnostics_consumingLocation_isDelegatedFromPackage() throws Exception {
useConfiguration("--verbose_visibility_errors");
reporter.removeHandler(failFastHandler);
scratch.file(
"tool/BUILD",
"""
cc_library(
name = "tool",
visibility = ["//visibility:private"],
)
""");
scratch.file("submacro/BUILD");
scratch.file(
"submacro/defs.bzl",
"""
def _impl(name, visibility, deps):
native.cc_library(
name = name + "_baz",
deps = deps,
)
my_submacro = macro(
implementation = _impl,
attrs = {"deps": attr.label_list()},
)
""");
scratch.file("macro/BUILD");
scratch.file(
"macro/defs.bzl",
"""
load("//submacro:defs.bzl", "my_submacro")
def _impl(name, visibility, deps, use_submacro):
callable = my_submacro if use_submacro else native.cc_library
callable(
name = name + "_bar",
deps = deps,
)
my_macro = macro(
implementation = _impl,
attrs = {
"deps": attr.label_list(),
"use_submacro": attr.bool(configurable=False),
},
)
""");
scratch.file(
"pkg/BUILD",
"""
load("//macro:defs.bzl", "my_macro")
my_macro(
name = "foo",
deps = ["//tool:tool"],
use_submacro = False,
)
my_macro(
name = "foo2",
deps = ["//tool:tool"],
use_submacro = True,
)
""");
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo_bar"));
assertContainsEvent(
"""
* Because the dependency was passed to the consuming target from an attribute of the \
symbolic macro //pkg:foo, the location being checked is the place where this macro is \
declared: package //pkg.\
""");
eventCollector.clear();
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo2_bar_baz"));
// Should say "transitive", and should still identify outer macro (foo2), not inner macro
// (foo2_bar).
assertContainsEvent(
"""
* Because the dependency was transitively passed to the consuming target from an attribute \
of the symbolic macro //pkg:foo2, the location being checked is the place where this macro \
is declared: package //pkg.\
""");
}
@Test
public void testVerboseDiagnostics_consumingLocation_isDelegatedFromMacro() throws Exception {
useConfiguration("--verbose_visibility_errors");
reporter.removeHandler(failFastHandler);
scratch.file(
"tool/BUILD",
"""
cc_library(
name = "tool",
visibility = ["//visibility:private"],
)
""");
scratch.file("subsubmacro/BUILD");
scratch.file(
"subsubmacro/defs.bzl",
"""
def _impl(name, visibility, deps):
native.cc_library(
name = name + "_qux",
deps = deps,
)
my_subsubmacro = macro(
implementation = _impl,
attrs = {"deps": attr.label_list()},
)
""");
scratch.file("submacro/BUILD");
scratch.file(
"submacro/defs.bzl",
"""
load("//subsubmacro:defs.bzl", "my_subsubmacro")
def _impl(name, visibility, deps):
my_subsubmacro(
name = name + "_baz",
deps = deps,
)
my_submacro = macro(
implementation = _impl,
attrs = {"deps": attr.label_list()},
)
""");
scratch.file("macro/BUILD");
scratch.file(
"macro/defs.bzl",
"""
load("//submacro:defs.bzl", "my_submacro")
load("//subsubmacro:defs.bzl", "my_subsubmacro")
def _impl(name, visibility, extra_level_deep):
callable = my_submacro if extra_level_deep else my_subsubmacro
callable(
name = name + "_bar",
deps = ["//tool:tool"],
)
my_macro = macro(
implementation = _impl,
attrs = {"extra_level_deep": attr.bool(configurable=False)},
)
""");
scratch.file(
"pkg/BUILD",
"""
load("//macro:defs.bzl", "my_macro")
my_macro(
name = "foo",
extra_level_deep = False,
)
my_macro(
name = "foo2",
extra_level_deep = True,
)
""");
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo_bar_qux"));
assertContainsEvent(
"""
* Because the dependency was passed to the consuming target from an attribute of the \
symbolic macro //pkg:foo_bar, the location being checked is the place where this macro is \
declared: the body of the calling macro my_macro, defined in //macro:defs.bzl of package \
//macro.\
""");
eventCollector.clear();
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo2_bar_baz_qux"));
// Should say "transitive", and should still identify outer macro (foo2_bar), not inner macro
// (foo2_bar_baz).
assertContainsEvent(
"""
* Because the dependency was transitively passed to the consuming target from an attribute \
of the symbolic macro //pkg:foo2_bar, the location being checked is the place where this \
macro is declared: the body of the calling macro my_macro, defined in //macro:defs.bzl of \
package //macro.\
""");
}
@Test
public void testVerboseDiagnostics_aliasDisclaimer_shownForAlias() throws Exception {
useConfiguration("--verbose_visibility_errors");
reporter.removeHandler(failFastHandler);
scratch.file(
"tool/BUILD",
"""
cc_library(
name = "tool",
visibility = ["//pkg:__pkg__"],
)
alias(
name = "indirect",
actual = ":tool",
visibility = ["//visibility:private"],
)
""");
scratch.file(
"pkg/BUILD",
"""
cc_library(
name = "foo",
deps = ["//tool:indirect"],
)
""");
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo"));
assertContainsEvent(
"""
* The dependency is an alias target. Note that it is the visibility of the alias we care \
about, not the visibility of the underlying target it refers to.
""");
}
@Test
public void testVerboseDiagnostics_aliasDisclaimer_notShownForNonAlias() throws Exception {
useConfiguration("--verbose_visibility_errors");
reporter.removeHandler(failFastHandler);
scratch.file(
"tool/BUILD",
"""
cc_library(
name = "tool",
visibility = ["//visibility:private"],
)
""");
scratch.file(
"pkg/BUILD",
"""
cc_library(
name = "foo",
deps = ["//tool:tool"],
)
""");
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo"));
assertDoesNotContainEvent("The dependency is an alias");
}
@Test
public void testVerboseDiagnostics_samePackageDisclaimer() throws Exception {
useConfiguration("--verbose_visibility_errors");
reporter.removeHandler(failFastHandler);
scratch.file("macro/BUILD");
scratch.file(
"macro/defs.bzl",
"""
def _impl(name, visibility):
native.cc_library(
name = name,
deps = ["//pkg:tool"],
)
my_macro = macro(implementation = _impl)
""");
scratch.file(
"pkg/BUILD",
"""
load("//macro:defs.bzl", "my_macro")
cc_library(
name = "tool",
visibility = ["//visibility:private"],
)
my_macro(
name = "foo",
)
""");
scratch.file(
"pkg2/BUILD",
"""
load("//macro:defs.bzl", "my_macro")
my_macro(
name = "foo",
)
""");
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo"));
assertContainsEvent(
"""
* Although both targets live in the same package, they cannot automatically see each other \
because they are declared by different symbolic macros.\
""");
eventCollector.clear();
assertThrows(ViewCreationFailedException.class, () -> update("//pkg2:foo"));
assertDoesNotContainEvent("both targets live in the same package");
}
@Test
public void testVerboseDiagnostics_samePackageDisclaimer_shownForImplicitDepOfMacro()
throws Exception {
useConfiguration("--verbose_visibility_errors");
reporter.removeHandler(failFastHandler);
scratch.file("macro/BUILD");
scratch.file(
"macro/defs.bzl",
"""
def _impl(name, visibility, _implicit_dep):
native.cc_library(
name = name,
deps = [_implicit_dep],
)
my_macro = macro(
implementation = _impl,
attrs = {"_implicit_dep": attr.label(default="//pkg:tool", configurable=False)},
)
""");
scratch.file(
"pkg/BUILD",
"""
load("//macro:defs.bzl", "my_macro")
cc_library(
name = "tool",
visibility = ["//visibility:private"],
)
my_macro(
name = "foo",
)
""");
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo"));
assertContainsEvent(
"""
* Although both targets live in the same package, they cannot automatically see each other \
because they are declared by different symbolic macros.\
""");
}
@Test
public void testVerboseDiagnostics_samePackageDisclaimer_notShownForImplicitDepOfRule()
throws Exception {
useConfiguration("--verbose_visibility_errors");
reporter.removeHandler(failFastHandler);
scratch.file("rule/BUILD");
scratch.file(
"rule/defs.bzl",
"""
def _impl(ctx):
pass
my_rule = rule(
implementation = _impl,
attrs = {"_implicit_dep": attr.label(default="//pkg:tool")},
)
""");
scratch.file("macro/BUILD");
scratch.file(
"macro/defs.bzl",
"""
load("//rule:defs.bzl", "my_rule")
def _impl(name, visibility):
my_rule(name = name)
my_macro = macro(implementation = _impl)
""");
scratch.file(
"pkg/BUILD",
"""
load("//macro:defs.bzl", "my_macro")
cc_library(
name = "tool",
visibility = ["//visibility:private"],
)
my_macro(name = "foo")
""");
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo"));
assertDoesNotContainEvent("both targets live in the same package");
}
@Test
public void testVerboseDiagnostics_moreDelegationNeeded_fromAncestorMacro() throws Exception {
useConfiguration("--verbose_visibility_errors");
reporter.removeHandler(failFastHandler);
scratch.file(
"tool/BUILD",
"""
cc_library(
name = "tool",
visibility = ["//macro:__pkg__"],
)
""");
scratch.file("subsubmacro/BUILD");
scratch.file(
"subsubmacro/defs.bzl",
"""
def _impl(name, visibility):
native.cc_library(
name = name + "_qux",
deps = ["//tool:tool"],
)
my_subsubmacro = macro(implementation = _impl)
""");
scratch.file("submacro/BUILD");
scratch.file(
"submacro/defs.bzl",
"""
load("//subsubmacro:defs.bzl", "my_subsubmacro")
def _impl(name, visibility):
my_subsubmacro(name = name + "_baz")
my_submacro = macro(implementation = _impl)
""");
scratch.file("macro/BUILD");
scratch.file(
"macro/defs.bzl",
"""
load("//submacro:defs.bzl", "my_submacro")
load("//subsubmacro:defs.bzl", "my_subsubmacro")
def _impl(name, visibility, extra_level_deep):
callable = my_submacro if extra_level_deep else my_subsubmacro
callable(name = name + "_bar")
my_macro = macro(
implementation = _impl,
attrs = {"extra_level_deep": attr.bool(configurable=False)},
)
""");
scratch.file(
"pkg/BUILD",
"""
load("//macro:defs.bzl", "my_macro")
my_macro(
name = "foo",
extra_level_deep = False,
)
my_macro(
name = "foo2",
extra_level_deep = True,
)
""");
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo_bar_qux"));
assertContainsEvent(
"""
* Although the dependency is not visible to the location being checked, it is visible to \
this location's caller, //pkg:foo, a my_macro macro defined in //macro. (Perhaps the \
caller needs to pass in the dependency as an argument?)\
""");
eventCollector.clear();
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo2_bar_baz_qux"));
// Should say "transitive", and should still identify outer macro (foo2_bar), not inner macro
// (foo2_bar_baz).
assertContainsEvent(
"""
* Although the dependency is not visible to the location being checked, it is visible to \
this location's transitive caller, //pkg:foo2, a my_macro macro defined in //macro. \
(Perhaps this caller, or an intermediate caller, needs to pass in the dependency as an \
argument?)\
""");
}
@Test
public void testVerboseDiagnostics_moreDelegationNeeded_fromBuildFile() throws Exception {
useConfiguration("--verbose_visibility_errors");
reporter.removeHandler(failFastHandler);
scratch.file(
"tool/BUILD",
"""
cc_library(
name = "tool",
visibility = ["//pkg:__pkg__"],
)
""");
scratch.file("submacro/BUILD");
scratch.file(
"submacro/defs.bzl",
"""
def _impl(name, visibility):
native.cc_library(
name = name + "_baz",
deps = ["//tool:tool"],
)
my_submacro = macro(implementation = _impl)
""");
scratch.file("macro/BUILD");
scratch.file(
"macro/defs.bzl",
"""
load("//submacro:defs.bzl", "my_submacro")
def _impl(name, visibility, extra_level_deep):
if extra_level_deep:
my_submacro(name = name + "_bar")
else:
native.cc_library(
name = name + "_bar",
deps = ["//tool:tool"],
)
my_macro = macro(
implementation = _impl,
attrs = {"extra_level_deep": attr.bool(configurable=False)},
)
""");
scratch.file(
"pkg/BUILD",
"""
load("//macro:defs.bzl", "my_macro")
my_macro(
name = "foo",
extra_level_deep = False,
)
my_macro(
name = "foo2",
extra_level_deep = True,
)
""");
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo_bar"));
assertContainsEvent(
"""
* Although the dependency is not visible to the location being checked, it is visible to \
this location's caller, the BUILD file of package //pkg. (Perhaps the caller needs to pass \
in the dependency as an argument?)\
""");
eventCollector.clear();
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo2_bar_baz"));
// Should say "transitive".
assertContainsEvent(
"""
* Although the dependency is not visible to the location being checked, it is visible to \
this location's transitive caller, the BUILD file of package //pkg. (Perhaps this caller, \
or an intermediate caller, needs to pass in the dependency as an argument?)\
""");
}
@Test
public void testVerboseDiagnostics_moreDelegationNeeded_incompleteDelegation() throws Exception {
useConfiguration("--verbose_visibility_errors");
reporter.removeHandler(failFastHandler);
scratch.file(
"tool/BUILD",
"""
cc_library(
name = "tool",
visibility = ["//pkg:__pkg__"],
)
""");
scratch.file("submacro/BUILD");
scratch.file(
"submacro/defs.bzl",
"""
def _impl(name, visibility, deps):
if not deps:
deps = ["//tool:tool"]
native.cc_library(
name = name + "_baz",
deps = deps,
)
my_submacro = macro(
implementation = _impl,
attrs = {"deps": attr.label_list(configurable=False)},
)
""");
scratch.file("macro/BUILD");
scratch.file(
"macro/defs.bzl",
"""
load("//submacro:defs.bzl", "my_submacro")
def _impl(name, visibility, deps, pass_in_tool):
my_submacro(
name = name + "_bar",
deps = ["//tool:tool"] if pass_in_tool else [],
)
my_macro = macro(
implementation = _impl,
attrs = {
"deps": attr.label_list(),
"pass_in_tool": attr.bool(configurable=False),
},
)
""");
scratch.file(
"pkg/BUILD",
"""
load("//macro:defs.bzl", "my_macro")
my_macro(
name = "foo",
pass_in_tool = True,
)
my_macro(
name = "foo2",
deps = ["//tool:tool"],
pass_in_tool = False,
)
""");
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo_bar_baz"));
// my_macro passed it to my_submacro, but BUILD didn't pass it to my_macro.
assertContainsEvent(
"""
* Although the dependency is not visible to the location being checked, it is visible to \
this location's caller, the BUILD file of package //pkg. (Perhaps the caller needs to pass \
in the dependency as an argument?)\
""");
eventCollector.clear();
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo2_bar_baz"));
// BUILD passed it to my_macro, but my_macro didn't pass it to my_submacro.
assertContainsEvent(
"""
* Although the dependency is not visible to the location being checked, it is visible to \
this location's transitive caller, the BUILD file of package //pkg. (Perhaps this caller, \
or an intermediate caller, needs to pass in the dependency as an argument?)\
""");
}
@Test
public void testVerboseDiagnostics_editVisibilitySuggestion_forRuleTarget() throws Exception {
useConfiguration("--verbose_visibility_errors");
reporter.removeHandler(failFastHandler);
scratch.file(
"dep/BUILD",
"""
cc_library(
name = "dep",
visibility = ["//visibility:private"],
)
""");
scratch.file(
"pkg/BUILD",
"""
cc_library(
name = "foo",
deps = ["//dep:dep"],
)
""");
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo"));
assertContainsEvent(
"""
* If you think the dependency is legitimate, consider updating its visibility declaration.\
""");
}
@Test
public void testVerboseDiagnostics_editVisibilitySuggestion_forFileTarget() throws Exception {
useConfiguration("--verbose_visibility_errors");
reporter.removeHandler(failFastHandler);
scratch.file(
"dep/BUILD",
"""
exports_files(
["dep"],
visibility = ["//visibility:private"],
)
""");
scratch.file(
"pkg/BUILD",
"""
cc_library(
name = "foo",
deps = ["//dep:dep"],
)
""");
assertThrows(ViewCreationFailedException.class, () -> update("//pkg:foo"));
assertContainsEvent(
"""
* If you think the dependency on this source file is legitimate, consider updating its \
visibility declaration using exports_files().\
""");
}
}