// Copyright 2015 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.config; import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.analysis.config.BuildOptions; import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider; import com.google.devtools.build.lib.analysis.config.ConfigurationFragmentFactory; import com.google.devtools.build.lib.analysis.config.FragmentOptions; import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; import com.google.devtools.build.lib.testutil.TestConstants; import com.google.devtools.build.lib.testutil.TestRuleClassProvider; import com.google.devtools.common.options.Option; import com.google.devtools.common.options.OptionDocumentationCategory; import com.google.devtools.common.options.OptionEffectTag; import com.google.devtools.common.options.OptionMetadataTag; import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for {@link ConfigSetting}. */ @RunWith(JUnit4.class) public class ConfigSettingTest extends BuildViewTestCase { /** Test option that has its null default overridden by its fragment. */ @AutoCodec(strategy = AutoCodec.Strategy.PUBLIC_FIELDS) public static class LateBoundTestOptions extends FragmentOptions { public LateBoundTestOptions() {} @Option( name = "opt_with_default", documentationCategory = OptionDocumentationCategory.UNCATEGORIZED, effectTags = {OptionEffectTag.NO_OP}, defaultValue = "null" ) public String optwithDefault; } @AutoCodec static class LateBoundTestOptionsFragment extends BuildConfiguration.Fragment { @Override public Map lateBoundOptionDefaults() { return ImmutableMap.of("opt_with_default", "overridden"); } } private static class LateBoundTestOptionsLoader implements ConfigurationFragmentFactory { @Override public BuildConfiguration.Fragment create(BuildOptions buildOptions) { return new LateBoundTestOptionsFragment(); } @Override public Class creates() { return LateBoundTestOptionsFragment.class; } @Override public ImmutableSet> requiredOptions() { return ImmutableSet.>of(LateBoundTestOptions.class); } } /** Test option which is private. */ @AutoCodec(strategy = AutoCodec.Strategy.PUBLIC_FIELDS) public static class InternalTestOptions extends FragmentOptions { public InternalTestOptions() {} @Option( name = "internal_option", documentationCategory = OptionDocumentationCategory.UNDOCUMENTED, effectTags = {OptionEffectTag.NO_OP}, defaultValue = "super secret", metadataTags = {OptionMetadataTag.INTERNAL} ) public String optwithDefault; } @AutoCodec static class InternalTestOptionsFragment extends BuildConfiguration.Fragment { } private static class InternalTestOptionsLoader implements ConfigurationFragmentFactory { @Override public BuildConfiguration.Fragment create(BuildOptions buildOptions) { return new InternalTestOptionsFragment(); } @Override public Class creates() { return InternalTestOptionsFragment.class; } @Override public ImmutableSet> requiredOptions() { return ImmutableSet.>of(InternalTestOptions.class); } } @Override protected ConfiguredRuleClassProvider getRuleClassProvider() { ConfiguredRuleClassProvider.Builder builder = new ConfiguredRuleClassProvider.Builder(); TestRuleClassProvider.addStandardRules(builder); builder.addRuleDefinition(new FeatureFlagSetterRule()); builder.addConfigurationOptions(LateBoundTestOptions.class); builder.addConfigurationFragment(new LateBoundTestOptionsLoader()); builder.addConfigurationOptions(InternalTestOptions.class); builder.addConfigurationFragment(new InternalTestOptionsLoader()); return builder.build(); } private void writeSimpleExample() throws Exception { scratch.file("pkg/BUILD", "config_setting(", " name = 'foo',", " values = {", " 'compilation_mode': 'dbg',", " 'stamp': '1',", " })"); } private ConfigMatchingProvider getConfigMatchingProvider(String label) throws Exception { return getConfiguredTarget(label).getProvider(ConfigMatchingProvider.class); } /** * Tests that a config_setting only matches build configurations where *all* of * its flag specifications match. */ @Test public void matchingCriteria() throws Exception { writeSimpleExample(); // First flag mismatches: useConfiguration("-c", "opt", "--stamp"); assertThat(getConfigMatchingProvider("//pkg:foo").matches()).isFalse(); // Second flag mismatches: useConfiguration("-c", "dbg", "--nostamp"); assertThat(getConfigMatchingProvider("//pkg:foo").matches()).isFalse(); // Both flags mismatch: useConfiguration("-c", "opt", "--nostamp"); assertThat(getConfigMatchingProvider("//pkg:foo").matches()).isFalse(); // Both flags match: useConfiguration("-c", "dbg", "--stamp"); assertThat(getConfigMatchingProvider("//pkg:foo").matches()).isTrue(); } /** * Tests that {@link ConfigMatchingProvider#label} is correct. */ @Test public void labelGetter() throws Exception { writeSimpleExample(); assertThat(getConfigMatchingProvider("//pkg:foo").label()) .isEqualTo(Label.parseAbsolute("//pkg:foo")); } /** * Tests that rule analysis fails on unknown options. */ @Test public void unknownOption() throws Exception { checkError("foo", "badoption", "unknown option: 'not_an_option'", "config_setting(", " name = 'badoption',", " values = {'not_an_option': 'bar'})"); } /** * Tests that rule analysis fails on internal options. */ @Test public void internalOption() throws Exception { checkError("foo", "badoption", "unknown option: 'internal_option'", "config_setting(", " name = 'badoption',", " values = {'internal_option': 'bar'})"); } /** * Tests that rule analysis fails on invalid option values. */ @Test public void invalidOptionValue() throws Exception { checkError("foo", "badvalue", "Not a valid compilation mode: 'baz'", "config_setting(", " name = 'badvalue',", " values = {'compilation_mode': 'baz'})"); } /** * Tests that when the first option is valid but the config_setting doesn't match, * remaining options are still validity-checked. */ @Test public void invalidOptionFartherDown() throws Exception { checkError("foo", "badoption", "unknown option: 'not_an_option'", "config_setting(", " name = 'badoption',", " values = {", " 'compilation_mode': 'opt',", " 'not_an_option': 'bar',", " })"); } /** * Tests that *some* settings (values or flag_values) must be specified. */ @Test public void emptySettings() throws Exception { checkError( "foo", "empty", "in config_setting rule //foo:empty: " + "Either values, flag_values or constraint_values must be specified and non-empty", "config_setting(", " name = 'empty',", " values = {})"); } /** * Tests {@link BuildConfiguration.Fragment#lateBoundOptionDefaults} options (options * that take alternative defaults from what's specified in {@link * com.google.devtools.common.options.Option#defaultValue}). */ @Test public void lateBoundOptionDefaults() throws Exception { scratch.file("test/BUILD", "config_setting(", " name = 'match',", " values = { 'opt_with_default': 'overridden' }", ")"); assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue(); } /** Tests disallowing {@link BuildConfiguration.Fragment#lateBoundOptionDefaults} */ @Test public void disallowLateBoundOptionDefaults() throws Exception { useConfiguration("--experimental_use_late_bound_option_defaults=false"); scratch.file( "test/BUILD", "config_setting(", " name = 'match',", " values = { 'opt_with_default': 'overridden' }", ")"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); } /** * Tests matching on multi-value attributes with key=value entries (e.g. --define). */ @Test public void multiValueDict() throws Exception { scratch.file("test/BUILD", "config_setting(", " name = 'match',", " values = {", " 'define': 'foo=bar',", " })"); useConfiguration(""); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); useConfiguration("--define", "foo=bar"); assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue(); useConfiguration("--define", "foo=baz"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); useConfiguration("--define", "foo=bar", "--define", "bar=baz"); assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue(); useConfiguration("--define", "foo=bar", "--define", "bar=baz", "--define", "foo=nope"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); useConfiguration("--define", "foo=nope", "--define", "bar=baz", "--define", "foo=bar"); assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue(); } @Test public void multipleDefines() throws Exception { scratch.file("test/BUILD", "config_setting(", " name = 'match',", " define_values = {", " 'foo1': 'bar',", " 'foo2': 'baz',", " })"); useConfiguration(""); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); useConfiguration("--define", "foo1=bar"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); useConfiguration("--define", "foo2=baz"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); useConfiguration("--define", "foo1=bar", "--define", "foo2=baz"); assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue(); } @Test public void definesCrossAttributes() throws Exception { scratch.file("test/BUILD", "config_setting(", " name = 'match',", " values = {", " 'define': 'a=c'", " },", " define_values = {", " 'b': 'd',", " })"); useConfiguration(""); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); useConfiguration("--define", "a=c"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); useConfiguration("--define", "b=d"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); useConfiguration("--define", "a=c", "--define", "b=d"); assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue(); } /** * Tests matching on multi-value attributes with primitive values. */ @Test public void multiValueList() throws Exception { scratch.file("test/BUILD", "config_setting(", " name = 'match',", " values = {", " 'copt': '-Dfoo',", " })"); useConfiguration(""); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); useConfiguration("--copt", "-Dfoo"); assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue(); useConfiguration("--copt", "-Dbar"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); useConfiguration("--copt", "-Dfoo", "--copt", "-Dbar"); assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue(); useConfiguration("--copt", "-Dbar", "--copt", "-Dfoo"); assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue(); } @Test public void selectForDefaultCrosstoolTop() throws Exception { String crosstoolTop = TestConstants.TOOLS_REPOSITORY + "//tools/cpp:toolchain"; scratchConfiguredTarget("a", "a", "config_setting(name='cs', values={'crosstool_top': '" + crosstoolTop + "'})", "sh_library(name='a', srcs=['a.sh'], deps=select({':cs': []}))"); } @Test public void selectForDefaultGrteTop() throws Exception { scratchConfiguredTarget("a", "a", "config_setting(name='cs', values={'grte_top': 'default'})", "sh_library(name='a', srcs=['a.sh'], deps=select({':cs': []}))"); } @Test public void requiredConfigFragmentMatcher() throws Exception { scratch.file("test/BUILD", "config_setting(", " name = 'match',", " values = {", " 'copt': '-Dfoo',", " 'javacopt': '-Dbar'", " })"); Rule target = (Rule) getTarget("//test:match"); assertThat(target.getRuleClassObject().getOptionReferenceFunction().apply(target)) .containsExactly("copt", "javacopt"); } @Test public void matchesIfFlagValuesAndValuesBothMatch() throws Exception { useConfiguration("--copt=-Dright"); scratch.file( "test/BUILD", "config_setting(", " name = 'match',", " flag_values = {", " ':flag': 'right',", " },", " values = {", " 'copt': '-Dright',", " },", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right'],", " default_value = 'right',", ")"); assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue(); } @Test public void matchesIfFlagValuesMatchAndValuesAreEmpty() throws Exception { scratch.file( "test/BUILD", "config_setting(", " name = 'match',", " flag_values = {", " ':flag': 'right',", " },", " values = {},", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right'],", " default_value = 'right',", ")"); assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue(); } @Test public void matchesIfValuesMatchAndFlagValuesAreEmpty() throws Exception { useConfiguration("--copt=-Dright"); scratch.file( "test/BUILD", "config_setting(", " name = 'match',", " flag_values = {},", " values = {", " 'copt': '-Dright',", " },", ")"); assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue(); } @Test public void doesNotMatchIfNeitherFlagValuesNorValuesMatches() throws Exception { useConfiguration("--copt=-Dright"); scratch.file( "test/BUILD", "config_setting(", " name = 'match',", " flag_values = {", " ':flag': 'wrong',", " },", " values = {", " 'copt': '-Dwrong',", " },", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); } @Test public void doesNotMatchIfFlagValuesDoNotMatchAndValuesAreEmpty() throws Exception { scratch.file( "test/BUILD", "config_setting(", " name = 'match',", " flag_values = {", " ':flag': 'wrong',", " },", " values = {},", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); } @Test public void doesNotMatchIfFlagValuesDoNotMatchButValuesDo() throws Exception { useConfiguration("--copt=-Dright"); scratch.file( "test/BUILD", "config_setting(", " name = 'match',", " flag_values = {", " ':flag': 'wrong',", " },", " values = {", " 'copt': '-Dright',", " },", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); } @Test public void doesNotMatchIfValuesDoNotMatchAndFlagValuesAreEmpty() throws Exception { useConfiguration("--copt=-Dright"); scratch.file( "test/BUILD", "config_setting(", " name = 'match',", " flag_values = {},", " values = {", " 'copt': '-Dwrong',", " },", ")"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); } @Test public void doesNotMatchIfValuesDoNotMatchButFlagValuesDo() throws Exception { useConfiguration("--copt=-Dright"); scratch.file( "test/BUILD", "config_setting(", " name = 'match',", " flag_values = {", " ':flag': 'right',", " },", " values = {", " 'copt': '-Dwrong',", " },", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); } @Test public void doesNotMatchIfEvenOneFlagValueDoesNotMatch() throws Exception { scratch.file( "test/BUILD", "config_setting(", " name = 'match',", " flag_values = {", " ':flag': 'right',", " ':flag2': 'bad',", " },", " values = {},", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")", "config_feature_flag(", " name = 'flag2',", " allowed_values = ['good', 'bad'],", " default_value = 'good',", ")"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); } @Test public void matchesIfNonDefaultIsSpecifiedAndFlagValueIsThatValue() throws Exception { scratch.file( "test/BUILD", "feature_flag_setter(", " name = 'setter',", " exports_setting = ':match',", " flag_values = {':flag': 'actual'},", ")", "config_setting(", " name = 'match',", " flag_values = {", " ':flag': 'actual',", " },", " values = {},", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['default', 'actual'],", " default_value = 'default',", ")"); assertThat(getConfigMatchingProvider("//test:setter").matches()).isTrue(); } @Test public void doesNotMatchIfDefaultIsSpecifiedAndFlagValueIsNotDefault() throws Exception { scratch.file( "test/BUILD", "feature_flag_setter(", " name = 'setter',", " exports_setting = ':match',", " flag_values = {':flag': 'actual'},", ")", "config_setting(", " name = 'match',", " flag_values = {", " ':flag': 'default',", " },", " values = {},", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['default', 'actual'],", " default_value = 'default',", ")"); assertThat(getConfigMatchingProvider("//test:setter").matches()).isFalse(); } @Test public void doesNotRefineSettingWithSameValuesAndSameFlagValues() throws Exception { useConfiguration("--copt=-Dright", "--javacopt=-Dgood"); scratch.file( "test/BUILD", "config_setting(", " name = 'refined',", " flag_values = {", " ':flag': 'right',", " ':flag2': 'good',", " },", " values = {", " 'copt': '-Dright',", " 'javacopt': '-Dgood',", " },", ")", "config_setting(", " name = 'other',", " flag_values = {", " ':flag': 'right',", " ':flag2': 'good',", " },", " values = {", " 'copt': '-Dright',", " 'javacopt': '-Dgood',", " },", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")", "config_feature_flag(", " name = 'flag2',", " allowed_values = ['good', 'bad'],", " default_value = 'good',", ")"); assertThat( getConfigMatchingProvider("//test:refined") .refines(getConfigMatchingProvider("//test:other"))) .isFalse(); } @Test public void doesNotRefineSettingWithDifferentValuesAndSameFlagValues() throws Exception { useConfiguration("--copt=-Dright", "--javacopt=-Dgood"); scratch.file( "test/BUILD", "config_setting(", " name = 'refined',", " flag_values = {", " ':flag': 'right',", " ':flag2': 'good',", " },", " values = {", " 'javacopt': '-Dgood',", " },", ")", "config_setting(", " name = 'other',", " flag_values = {", " ':flag': 'right',", " ':flag2': 'good',", " },", " values = {", " 'copt': '-Dright',", " },", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")", "config_feature_flag(", " name = 'flag2',", " allowed_values = ['good', 'bad'],", " default_value = 'good',", ")"); assertThat( getConfigMatchingProvider("//test:refined") .refines(getConfigMatchingProvider("//test:other"))) .isFalse(); } @Test public void doesNotRefineSettingWithSameValuesAndDifferentFlagValues() throws Exception { useConfiguration("--copt=-Dright", "--javacopt=-Dgood"); scratch.file( "test/BUILD", "config_setting(", " name = 'refined',", " flag_values = {", " ':flag': 'right',", " },", " values = {", " 'copt': '-Dright',", " 'javacopt': '-Dgood',", " },", ")", "config_setting(", " name = 'other',", " flag_values = {", " ':flag2': 'good',", " },", " values = {", " 'copt': '-Dright',", " 'javacopt': '-Dgood',", " },", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")", "config_feature_flag(", " name = 'flag2',", " allowed_values = ['good', 'bad'],", " default_value = 'good',", ")"); assertThat( getConfigMatchingProvider("//test:refined") .refines(getConfigMatchingProvider("//test:other"))) .isFalse(); } @Test public void doesNotRefineSettingWithDifferentValuesAndDifferentFlagValues() throws Exception { useConfiguration("--copt=-Dright", "--javacopt=-Dgood"); scratch.file( "test/BUILD", "config_setting(", " name = 'refined',", " flag_values = {", " ':flag': 'right',", " },", " values = {", " 'copt': '-Dright',", " },", ")", "config_setting(", " name = 'other',", " flag_values = {", " ':flag2': 'good',", " },", " values = {", " 'javacopt': '-Dgood',", " },", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")", "config_feature_flag(", " name = 'flag2',", " allowed_values = ['good', 'bad'],", " default_value = 'good',", ")"); assertThat( getConfigMatchingProvider("//test:refined") .refines(getConfigMatchingProvider("//test:other"))) .isFalse(); } @Test public void doesNotRefineSettingWithDifferentValuesAndSubsetFlagValues() throws Exception { useConfiguration("--copt=-Dright", "--javacopt=-Dgood"); scratch.file( "test/BUILD", "config_setting(", " name = 'refined',", " flag_values = {", " ':flag': 'right',", " ':flag2': 'good',", " },", " values = {", " 'copt': '-Dright',", " },", ")", "config_setting(", " name = 'other',", " flag_values = {", " ':flag': 'right',", " },", " values = {", " 'javacopt': '-Dgood',", " },", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")", "config_feature_flag(", " name = 'flag2',", " allowed_values = ['good', 'bad'],", " default_value = 'good',", ")"); assertThat( getConfigMatchingProvider("//test:refined") .refines(getConfigMatchingProvider("//test:other"))) .isFalse(); } @Test public void doesNotRefineSettingWithSubsetValuesAndDifferentFlagValues() throws Exception { useConfiguration("--copt=-Dright", "--javacopt=-Dgood"); scratch.file( "test/BUILD", "config_setting(", " name = 'refined',", " flag_values = {", " ':flag': 'right',", " },", " values = {", " 'copt': '-Dright',", " 'javacopt': '-Dgood',", " },", ")", "config_setting(", " name = 'other',", " flag_values = {", " ':flag2': 'good',", " },", " values = {", " 'copt': '-Dright',", " },", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")", "config_feature_flag(", " name = 'flag2',", " allowed_values = ['good', 'bad'],", " default_value = 'good',", ")"); assertThat( getConfigMatchingProvider("//test:refined") .refines(getConfigMatchingProvider("//test:other"))) .isFalse(); } @Test public void refinesSettingWithSubsetValuesAndSameFlagValues() throws Exception { useConfiguration("--copt=-Dright", "--javacopt=-Dgood"); scratch.file( "test/BUILD", "config_setting(", " name = 'refined',", " flag_values = {", " ':flag': 'right',", " ':flag2': 'good',", " },", " values = {", " 'copt': '-Dright',", " 'javacopt': '-Dgood',", " },", ")", "config_setting(", " name = 'other',", " flag_values = {", " ':flag': 'right',", " ':flag2': 'good',", " },", " values = {", " 'copt': '-Dright',", " },", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")", "config_feature_flag(", " name = 'flag2',", " allowed_values = ['good', 'bad'],", " default_value = 'good',", ")"); assertThat( getConfigMatchingProvider("//test:refined") .refines(getConfigMatchingProvider("//test:other"))) .isTrue(); } @Test public void refinesSettingWithSameValuesAndSubsetFlagValues() throws Exception { useConfiguration("--copt=-Dright", "--javacopt=-Dgood"); scratch.file( "test/BUILD", "config_setting(", " name = 'refined',", " flag_values = {", " ':flag': 'right',", " ':flag2': 'good',", " },", " values = {", " 'copt': '-Dright',", " 'javacopt': '-Dgood',", " },", ")", "config_setting(", " name = 'other',", " flag_values = {", " ':flag': 'right',", " },", " values = {", " 'copt': '-Dright',", " 'javacopt': '-Dgood',", " },", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")", "config_feature_flag(", " name = 'flag2',", " allowed_values = ['good', 'bad'],", " default_value = 'good',", ")"); assertThat( getConfigMatchingProvider("//test:refined") .refines(getConfigMatchingProvider("//test:other"))) .isTrue(); } @Test public void refinesSettingWithSubsetValuesAndSubsetFlagValues() throws Exception { useConfiguration("--copt=-Dright", "--javacopt=-Dgood"); scratch.file( "test/BUILD", "config_setting(", " name = 'refined',", " flag_values = {", " ':flag': 'right',", " ':flag2': 'good',", " },", " values = {", " 'copt': '-Dright',", " 'javacopt': '-Dgood',", " },", ")", "config_setting(", " name = 'other',", " flag_values = {", " ':flag': 'right',", " },", " values = {", " 'copt': '-Dright',", " },", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")", "config_feature_flag(", " name = 'flag2',", " allowed_values = ['good', 'bad'],", " default_value = 'good',", ")"); assertThat( getConfigMatchingProvider("//test:refined") .refines(getConfigMatchingProvider("//test:other"))) .isTrue(); } @Test public void matchesAliasedFlagsInFlagValues() throws Exception { scratch.file( "test/BUILD", "config_setting(", " name = 'alias_matcher',", " flag_values = {", " ':alias': 'right',", " },", ")", "alias(", " name = 'alias',", " actual = 'flag',", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")"); assertThat(getConfigMatchingProvider("//test:alias_matcher").matches()).isTrue(); } @Test public void aliasedFlagsAreCountedInRefining() throws Exception { scratch.file( "test/BUILD", "config_setting(", " name = 'refined',", " flag_values = {", " ':alias': 'right',", " ':flag2': 'good',", " },", ")", "config_setting(", " name = 'other',", " flag_values = {", " ':flag': 'right',", " },", ")", "alias(", " name = 'alias',", " actual = 'flag',", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")", "config_feature_flag(", " name = 'flag2',", " allowed_values = ['good', 'bad'],", " default_value = 'good',", ")"); assertThat( getConfigMatchingProvider("//test:refined") .refines(getConfigMatchingProvider("//test:other"))) .isTrue(); } @Test public void referencingSameFlagViaMultipleAliasesFails() throws Exception { checkError( "test", "multialias", "in flag_values attribute of config_setting rule //test:multialias: " + "flag '//test:direct' referenced multiple times as ['//test:alias', '//test:direct']", "config_setting(", " name = 'multialias',", " flag_values = {", " ':alias': 'right',", " ':direct': 'right',", " },", ")", "alias(", " name = 'alias',", " actual = 'direct',", ")", "config_feature_flag(", " name = 'direct',", " allowed_values = ['right', 'wrong'],", " default_value = 'right',", ")"); } @Test public void forbidsNonConfigFeatureFlagRulesForFlagValues() throws Exception { checkError("test", "invalid_flag", "in flag_values attribute of config_setting rule //test:invalid_flag: " + "'//test:genrule' does not have mandatory providers: 'FeatureFlagInfo'", "config_setting(", " name = 'invalid_flag',", " flag_values = {", " ':genrule': 'lolz',", " })", "genrule(", " name = 'genrule',", " outs = ['output'],", " cmd = 'echo >$@',", " )"); } @Test public void requiresValidValueForFlagValues() throws Exception { checkError("test", "invalid_flag", "in flag_values attribute of config_setting rule //test:invalid_flag: " + "error while parsing user-defined configuration values: " + "'invalid' is not a valid value for '//test:flag'", "config_setting(", " name = 'invalid_flag',", " flag_values = {", " ':flag': 'invalid',", " })", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'valid'],", " default_value = 'valid',", ")"); } @Test public void usesAliasLabelWhenReportingErrorInFlagValues() throws Exception { checkError("test", "invalid_flag", "in flag_values attribute of config_setting rule //test:invalid_flag: " + "error while parsing user-defined configuration values: " + "'invalid' is not a valid value for '//test:alias'", "config_setting(", " name = 'invalid_flag',", " flag_values = {", " ':alias': 'invalid',", " })", "alias(", " name = 'alias',", " actual = ':flag',", ")", "config_feature_flag(", " name = 'flag',", " allowed_values = ['right', 'valid'],", " default_value = 'valid',", ")"); } @Test public void constraintValue() throws Exception { scratch.file( "test/BUILD", "constraint_setting(name = 'notable_building')", "constraint_value(name = 'empire_state', constraint_setting = 'notable_building')", "constraint_value(name = 'space_needle', constraint_setting = 'notable_building')", "platform(", " name = 'new_york_platform',", " constraint_values = [':empire_state'],", ")", "platform(", " name = 'seattle_platform',", " constraint_values = [':space_needle'],", ")", "config_setting(", " name = 'match',", " constraint_values = [':empire_state'],", ");"); useConfiguration("--experimental_platforms=//test:new_york_platform"); assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue(); useConfiguration("--experimental_platforms=//test:seattle_platform"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); useConfiguration(""); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); } @Test public void multipleConstraintValues() throws Exception { scratch.file( "test/BUILD", "constraint_setting(name = 'notable_building')", "constraint_value(name = 'empire_state', constraint_setting = 'notable_building')", "constraint_setting(name = 'museum')", "constraint_value(name = 'cloisters', constraint_setting = 'museum')", "constraint_setting(name = 'theme_park')", "constraint_value(name = 'coney_island', constraint_setting = 'theme_park')", "platform(", " name = 'manhattan_platform',", " constraint_values = [", " ':empire_state',", " ':cloisters',", " ],", ")", "platform(", " name = 'museum_platform',", " constraint_values = [':cloisters'],", ")", "platform(", " name = 'new_york_platform',", " constraint_values = [", " ':empire_state',", " ':cloisters',", " ':coney_island',", " ],", ")", "config_setting(", " name = 'match',", " constraint_values = [':empire_state', ':cloisters'],", ");"); useConfiguration("--experimental_platforms=//test:manhattan_platform"); assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue(); useConfiguration("--experimental_platforms=//test:museum_platform"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); useConfiguration("--experimental_platforms=//test:new_york_platform"); assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue(); } @Test public void definesAndConstraints() throws Exception { scratch.file( "test/BUILD", "constraint_setting(name = 'notable_building')", "constraint_value(name = 'empire_state', constraint_setting = 'notable_building')", "constraint_value(name = 'space_needle', constraint_setting = 'notable_building')", "platform(", " name = 'new_york_platform',", " constraint_values = [':empire_state'],", ")", "platform(", " name = 'seattle_platform',", " constraint_values = [':space_needle'],", ")", "config_setting(", " name = 'match',", " constraint_values = [':empire_state'],", " values = {", " 'define': 'a=c',", " },", " define_values = {", " 'b': 'd',", " },", ");"); useConfiguration( "--experimental_platforms=//test:new_york_platform", "--define", "a=c", "--define", "b=d"); assertThat(getConfigMatchingProvider("//test:match").matches()).isTrue(); useConfiguration("--experimental_platforms=//test:new_york_platform"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); useConfiguration("--define", "a=c"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); useConfiguration("--define", "a=c", "--experimental_platforms=//test:new_york_platform"); assertThat(getConfigMatchingProvider("//test:match").matches()).isFalse(); } /** * Tests that a config_setting doesn't allow a constraint_values list with more than one * constraint value per constraint setting. */ @Test public void multipleValuesPerSetting() throws Exception { checkError( "foo", "bad", "in config_setting rule //foo:bad: " + "Duplicate constraint_values detected: " + "constraint_setting //foo:notable_building has " + "[//foo:empire_state, //foo:space_needle], " + "constraint_setting //foo:museum has " + "[//foo:moma, //foo:sam]", "constraint_setting(name = 'notable_building')", "constraint_value(name = 'empire_state', constraint_setting = 'notable_building')", "constraint_value(name = 'space_needle', constraint_setting = 'notable_building')", "constraint_value(name = 'peace_arch', constraint_setting = 'notable_building')", "constraint_setting(name = 'museum')", "constraint_value(name = 'moma', constraint_setting = 'museum')", "constraint_value(name = 'sam', constraint_setting = 'museum')", "config_setting(", " name = 'bad',", " constraint_values = [", " ':empire_state',", " ':space_needle',", " ':moma',", " ':sam',", " ],", ");"); } }