// 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.analysis.config; import com.google.devtools.build.lib.Constants; import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.common.options.OptionsBase; import com.google.devtools.common.options.OptionsParser; /** * Tests for {@link ConfigSetting}. */ public class ConfigSettingTest extends BuildViewTestCase { 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); } /** * Returns the default value of the given flag. */ private Object flagDefault(String option) { Class optionsClass = getTargetConfiguration().getOptionClass(option); return OptionsParser.newOptionsParser(optionsClass) .getOptions(optionsClass) .asMap() .get(option); } /** * Tests that a config_setting only matches build configurations where *all* of * its flag specifications match. */ public void testMatchingCriteria() throws Exception { writeSimpleExample(); // First flag mismatches: useConfiguration("-c", "opt", "--stamp"); assertFalse(getConfigMatchingProvider("//pkg:foo").matches()); // Second flag mismatches: useConfiguration("-c", "dbg", "--nostamp"); assertFalse(getConfigMatchingProvider("//pkg:foo").matches()); // Both flags mismatch: useConfiguration("-c", "opt", "--nostamp"); assertFalse(getConfigMatchingProvider("//pkg:foo").matches()); // Both flags match: useConfiguration("-c", "dbg", "--stamp"); assertTrue(getConfigMatchingProvider("//pkg:foo").matches()); } /** * Tests that {@link ConfigMatchingProvider#label} is correct. */ public void testLabel() throws Exception { writeSimpleExample(); assertEquals( Label.parseAbsolute("//pkg:foo"), getConfigMatchingProvider("//pkg:foo").label()); } /** * Tests that rule analysis fails on unknown options. */ public void testUnknownOption() 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 invalid option values. */ public void testInvalidOptionValue() 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. */ public void testInvalidOptionFartherDown() 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 must be specified. */ public void testEmptySettings() throws Exception { checkError("foo", "empty", "//foo:empty: no settings specified", "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}). */ public void testLateBoundOptionDefaults() throws Exception { String crosstoolCpuDefault = (String) getTargetConfiguration().getOptionValue("cpu"); String crosstoolCompilerDefault = (String) getTargetConfiguration().getOptionValue("compiler"); scratch.file("test/BUILD", "config_setting(", " name = 'match',", " values = {", " 'cpu': '" + crosstoolCpuDefault + "',", " 'compiler': '" + crosstoolCompilerDefault + "',", //'gcc-4.4.0',", " })"); assertTrue(getConfigMatchingProvider("//test:match").matches()); assertNull(flagDefault("cpu")); assertNotNull(crosstoolCpuDefault); assertNull(flagDefault("compiler")); assertNotNull(crosstoolCompilerDefault); } /** * Tests matching on multi-value attributes with key=value entries (e.g. --define). */ public void testMultiValueDict() throws Exception { scratch.file("test/BUILD", "config_setting(", " name = 'match',", " values = {", " 'define': 'foo=bar',", " })"); useConfiguration(""); assertFalse(getConfigMatchingProvider("//test:match").matches()); useConfiguration("--define", "foo=bar"); assertTrue(getConfigMatchingProvider("//test:match").matches()); useConfiguration("--define", "foo=baz"); assertFalse(getConfigMatchingProvider("//test:match").matches()); useConfiguration("--define", "foo=bar", "--define", "bar=baz"); assertTrue(getConfigMatchingProvider("//test:match").matches()); useConfiguration("--define", "foo=bar", "--define", "bar=baz", "--define", "foo=nope"); assertFalse(getConfigMatchingProvider("//test:match").matches()); useConfiguration("--define", "foo=nope", "--define", "bar=baz", "--define", "foo=bar"); assertTrue(getConfigMatchingProvider("//test:match").matches()); } /** * Tests matching on multi-value attributes with primitive values. */ public void testMultiValueList() throws Exception { scratch.file("test/BUILD", "config_setting(", " name = 'match',", " values = {", " 'copt': '-Dfoo',", " })"); useConfiguration(""); assertFalse(getConfigMatchingProvider("//test:match").matches()); useConfiguration("--copt", "-Dfoo"); assertTrue(getConfigMatchingProvider("//test:match").matches()); useConfiguration("--copt", "-Dbar"); assertFalse(getConfigMatchingProvider("//test:match").matches()); useConfiguration("--copt", "-Dfoo", "--copt", "-Dbar"); assertTrue(getConfigMatchingProvider("//test:match").matches()); useConfiguration("--copt", "-Dbar", "--copt", "-Dfoo"); assertTrue(getConfigMatchingProvider("//test:match").matches()); } public void testSelectForDefaultCrosstoolTop() throws Exception { String crosstoolTop = Constants.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': []}))"); } }