blob: d120bb8207e3e5eb97dec45611c4ebd80ec32b0a [file] [log] [blame]
// Copyright 2018 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.rules;
import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.lib.bazel.bzlmod.BzlmodTestUtil.createModuleKey;
import static com.google.devtools.build.lib.skyframe.BzlLoadValue.keyForBuild;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.StarlarkInfo;
import com.google.devtools.build.lib.packages.StarlarkProvider;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for {@link LabelBuildSettings} rules. */
@RunWith(JUnit4.class)
public class LabelBuildSettingTest extends BuildViewTestCase {
private void writeRulesBzl(String type) throws Exception {
scratch.file(
"test/rules.bzl",
"MyRuleInfo = provider()",
"def _my_rule_impl(ctx):",
" return MyRuleInfo(value = ctx.attr._label_setting[SimpleRuleInfo].value)",
"",
"my_rule = rule(",
" implementation = _my_rule_impl,",
" attrs = {",
" '_label_setting': attr.label(default = Label('//test:my_label_" + type + "')),",
" },",
")",
"",
"SimpleRuleInfo = provider(fields = ['value'])",
"",
"def _simple_rule_impl(ctx):",
" return [SimpleRuleInfo(value = ctx.attr.value)]",
"",
"simple_rule = rule(",
" implementation = _simple_rule_impl,",
" attrs = {",
" 'value':attr.string(),",
" },",
")");
}
@Test
public void testLabelSetting() throws Exception {
String testing = "setting";
writeRulesBzl(testing);
scratch.file(
"test/BUILD",
"load('//test:rules.bzl', 'my_rule', 'simple_rule')",
"",
"my_rule(name = 'my_rule')",
"simple_rule(name = 'default', value = 'default_value')",
"simple_rule(name = 'command_line', value = 'command_line_value')",
"label_setting(name = 'my_label_" + testing + "', build_setting_default = ':default')");
scratch.file(
"a/BUILD",
"""
cc_library(
name = "a",
srcs = ["a.cc"],
)
alias(
name = "b",
actual = "a",
)
""");
ConfiguredTarget b = getConfiguredTarget("//test:my_rule");
StarlarkInfo myRuleInfo = getStarlarkProvider(b, "MyRuleInfo");
assertThat(myRuleInfo.getValue("value")).isEqualTo("default_value");
}
@Test
public void testLabelFlag_default() throws Exception {
String testing = "flag";
writeRulesBzl(testing);
scratch.file(
"test/BUILD",
"load('//test:rules.bzl', 'my_rule', 'simple_rule')",
"",
"my_rule(name = 'my_rule')",
"simple_rule(name = 'default', value = 'default_value')",
"simple_rule(name = 'command_line', value = 'command_line_value')",
"label_flag(name = 'my_label_" + testing + "', build_setting_default = ':default')");
scratch.file(
"a/BUILD",
"""
cc_library(
name = "a",
srcs = ["a.cc"],
)
alias(
name = "b",
actual = "a",
)
""");
ConfiguredTarget b = getConfiguredTarget("//test:my_rule");
StarlarkProvider.Key myRuleInfo =
new StarlarkProvider.Key(
keyForBuild(Label.parseCanonical("//test:rules.bzl")), "MyRuleInfo");
assertThat(((StarlarkInfo) b.get(myRuleInfo)).getValue("value")).isEqualTo("default_value");
}
@Test
public void testLabelFlag_set() throws Exception {
String testing = "flag";
writeRulesBzl(testing);
scratch.file(
"test/BUILD",
"load('//test:rules.bzl', 'my_rule', 'simple_rule')",
"",
"my_rule(name = 'my_rule')",
"simple_rule(name = 'default', value = 'default_value')",
"simple_rule(name = 'command_line', value = 'command_line_value')",
"label_flag(name = 'my_label_" + testing + "', build_setting_default = ':default')");
scratch.file(
"a/BUILD",
"""
cc_library(
name = "a",
srcs = ["a.cc"],
)
alias(
name = "b",
actual = "a",
)
""");
useConfiguration("--//test:my_label_flag=//test:command_line");
ConfiguredTarget b = getConfiguredTarget("//test:my_rule");
StarlarkProvider.Key myRuleInfo =
new StarlarkProvider.Key(
keyForBuild(Label.parseCanonical("//test:rules.bzl")), "MyRuleInfo");
assertThat(((StarlarkInfo) b.get(myRuleInfo)).getValue("value"))
.isEqualTo("command_line_value");
}
@Test
public void withSelectThroughAlias() throws Exception {
writeRulesBzl("flag");
scratch.file(
"test/BUILD",
"""
load("//test:rules.bzl", "my_rule", "simple_rule")
simple_rule(
name = "default",
value = "default_value",
)
simple_rule(
name = "command_line",
value = "command_line_value",
)
label_flag(
name = "my_label_flag",
build_setting_default = ":default",
)
alias(
name = "my_label_flag_alias",
actual = ":my_label_flag",
)
config_setting(
name = "is_default_label",
flag_values = {":my_label_flag_alias": "//test:default"},
)
simple_rule(
name = "selector",
value = select({":is_default_label": "valid"}),
)
""");
useConfiguration();
getConfiguredTarget("//test:selector");
assertNoEvents();
reporter.removeHandler(failFastHandler);
useConfiguration("--//test:my_label_flag=//test:command_line");
getConfiguredTarget("//test:selector");
assertContainsEvent(
"configurable attribute \"value\" in //test:selector doesn't match this configuration");
}
@Test
public void withSelect() throws Exception {
writeRulesBzl("flag");
scratch.file(
"test/BUILD",
"""
load("//test:rules.bzl", "my_rule", "simple_rule")
simple_rule(
name = "default",
value = "default_value",
)
simple_rule(
name = "command_line",
value = "command_line_value",
)
label_flag(
name = "my_label_flag",
build_setting_default = ":default",
)
config_setting(
name = "is_default_label",
flag_values = {":my_label_flag": "//test:default"},
)
simple_rule(
name = "selector",
value = select({":is_default_label": "valid"}),
)
""");
useConfiguration();
getConfiguredTarget("//test:selector");
assertNoEvents();
reporter.removeHandler(failFastHandler);
useConfiguration("--//test:my_label_flag=//test:command_line");
getConfiguredTarget("//test:selector");
assertContainsEvent(
"configurable attribute \"value\" in //test:selector doesn't match this configuration");
}
@Test
public void selectWithRelativeLabel() throws Exception {
writeRulesBzl("flag");
scratch.file(
"test/BUILD",
"""
load("//test:rules.bzl", "my_rule", "simple_rule")
simple_rule(
name = "default",
value = "default_value",
)
simple_rule(
name = "command_line",
value = "command_line_value",
)
label_flag(
name = "my_label_flag",
build_setting_default = ":default",
)
config_setting(
name = "is_default_label",
flag_values = {":my_label_flag": ":default"},
)
simple_rule(
name = "selector",
value = select({":is_default_label": "valid"}),
)
""");
useConfiguration();
getConfiguredTarget("//test:selector");
assertNoEvents();
reporter.removeHandler(failFastHandler);
useConfiguration("--//test:my_label_flag=//test:command_line");
getConfiguredTarget("//test:selector");
assertContainsEvent(
"configurable attribute \"value\" in //test:selector doesn't match this configuration");
}
@Test
public void selectOnInvalidLabel() throws Exception {
writeRulesBzl("flag");
scratch.file(
"test/BUILD",
"""
load("//test:rules.bzl", "my_rule", "simple_rule")
simple_rule(
name = "default",
value = "default_value",
)
simple_rule(
name = "command_line",
value = "command_line_value",
)
label_flag(
name = "my_label_flag",
build_setting_default = ":default",
)
config_setting(
name = "is_default_label",
flag_values = {":my_label_flag": ":@not_a_valid_label/"},
)
simple_rule(
name = "selector",
value = select({":is_default_label": "valid"}),
)
""");
reporter.removeHandler(failFastHandler);
useConfiguration();
getConfiguredTarget("//test:selector");
assertContainsEvent(
"':@not_a_valid_label/' cannot be converted to //test:my_label_flag type label");
}
@Test
public void transitionOutput_samePackage() throws Exception {
scratch.overwriteFile(
"tools/allowlists/function_transition_allowlist/BUILD",
"""
package_group(
name = "function_transition_allowlist",
packages = [
"//test/...",
],
)
""");
scratch.file(
"test/rules.bzl",
"""
def _transition_impl(settings, attr):
return {
"//test:my_flag1": Label("//test:other_rule"),
"//test:my_flag2": "//test:other_rule",
"//test:my_flag3": ":other_rule",
}
_my_transition = transition(
implementation = _transition_impl,
inputs = [],
outputs = ["//test:my_flag1", "//test:my_flag2", "//test:my_flag3"],
)
def _rule_impl(ctx):
target = Label("//test:other_rule")
if target != ctx.attr._flag1.label:
fail("flag1 is " + str(ctx.attr._flag1.label))
if target != ctx.attr._flag2.label:
fail("flag2 is " + str(ctx.attr._flag2.label))
if target != ctx.attr._flag3.label:
fail("flag3 is " + str(ctx.attr._flag3.label))
rule_with_transition = rule(
implementation = _rule_impl,
cfg = _my_transition,
attrs = {
"_flag1": attr.label(default = ":my_flag1"),
"_flag2": attr.label(default = ":my_flag2"),
"_flag3": attr.label(default = ":my_flag3"),
},
)
""");
scratch.file(
"test/BUILD",
"""
load("//test:rules.bzl", "rule_with_transition")
label_flag(
name = "my_flag1",
build_setting_default = ":first_rule",
)
label_flag(
name = "my_flag2",
build_setting_default = ":first_rule",
)
label_flag(
name = "my_flag3",
build_setting_default = ":first_rule",
)
filegroup(name = "first_rule")
filegroup(name = "other_rule")
rule_with_transition(name = "buildme")
""");
assertThat(getConfiguredTarget("//test:buildme")).isNotNull();
assertNoEvents();
}
@Test
public void transitionOutput_otherRepo() throws Exception {
scratch.overwriteFile("MODULE.bazel", "bazel_dep(name='foo',version='1.0')");
registry.addModule(createModuleKey("foo", "1.0"), "module(name='foo', version='1.0')");
scratch.file("modules/foo~1.0/WORKSPACE");
scratch.file("modules/foo~1.0/BUILD", "filegroup(name='other_rule')");
scratch.overwriteFile(
"tools/allowlists/function_transition_allowlist/BUILD",
"""
package_group(
name = "function_transition_allowlist",
packages = [
"//test/...",
],
)
""");
scratch.file(
"test/rules.bzl",
"""
def _transition_impl(settings, attr):
return {
"//test:my_flag1": Label("@foo//:other_rule"),
"//test:my_flag2": "@foo//:other_rule",
}
_my_transition = transition(
implementation = _transition_impl,
inputs = [],
outputs = ["//test:my_flag1", "//test:my_flag2"],
)
def _rule_impl(ctx):
target = Label("@foo//:other_rule")
if target != ctx.attr._flag1.label:
fail("flag1 is " + str(ctx.attr._flag1.label))
if target != ctx.attr._flag2.label:
fail("flag2 is " + str(ctx.attr._flag2.label))
rule_with_transition = rule(
implementation = _rule_impl,
cfg = _my_transition,
attrs = {
"_flag1": attr.label(default = ":my_flag1"),
"_flag2": attr.label(default = ":my_flag2"),
},
)
""");
scratch.file(
"test/BUILD",
"""
load("//test:rules.bzl", "rule_with_transition")
label_flag(
name = "my_flag1",
build_setting_default = ":first_rule",
)
label_flag(
name = "my_flag2",
build_setting_default = ":first_rule",
)
filegroup(name = "first_rule")
rule_with_transition(name = "buildme")
""");
invalidatePackages();
assertThat(getConfiguredTarget("//test:buildme")).isNotNull();
assertNoEvents();
}
@Test
public void testInvisibleRepoInLabelResultsInEarlyError() throws Exception {
scratch.file(
"test/defs.bzl",
"""
def _setting_impl(ctx):
return []
string_flag = rule(
implementation = _setting_impl,
build_setting = config.string(flag = True),
)
def _transition_impl(settings, attr):
return {"//test:formation": "mesa"}
formation_transition = transition(
implementation = _transition_impl,
inputs = ["@foobar//test:formation"], # invalid repo name
outputs = ["//test:formation"],
)
def _impl(ctx):
return []
state = rule(
implementation = _impl,
cfg = formation_transition,
)
""");
scratch.file(
"test/BUILD",
"""
load("//test:defs.bzl", "state", "string_flag")
state(name = "arizona")
string_flag(
name = "formation",
build_setting_default = "canyon",
)
""");
reporter.removeHandler(failFastHandler);
getConfiguredTarget("//test:arizona");
assertContainsEvent(
"Error in transition: invalid transition input '@@[unknown repo 'foobar' requested from @@]"
+ "//test:formation': no repo visible as @foobar from main repository");
}
}