blob: 8c725372e18767a5028012499dfc856855b1b572 [file] [log] [blame]
// Copyright 2020 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 com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.config.ConfigMatchingProvider;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.events.Event;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* A test for rule ConfiguredTargets.
*/
@RunWith(JUnit4.class)
public final class RuleConfiguredTargetTest extends BuildViewTestCase {
private ConfiguredTarget configure(String ruleLabel) throws Exception {
return getConfiguredTarget(ruleLabel);
}
@Test
public void smokeNonexistentFailure() throws Exception {
scratch.file("a/BUILD", "");
reporter.removeHandler(failFastHandler);
getConfiguredTarget("//a:a");
assertContainsEvent("target 'a' not declared in package 'a'");
}
@Test
public void testFeatureEnabledOnCommandLine() throws Exception {
useConfiguration("--features=feature");
scratch.file("a/BUILD",
"cc_library(name = 'a')");
ImmutableSet<String> features = getRuleContext(configure("//a")).getFeatures();
assertThat(features).contains("feature");
assertThat(features).doesNotContain("other");
}
@Test
public void testFeatureDisabledOnCommandLine() throws Exception {
useConfiguration("--features=-feature");
scratch.file("a/BUILD", "cc_library(name = 'a')");
ImmutableSet<String> disabledFeatures = getRuleContext(configure("//a")).getDisabledFeatures();
assertThat(disabledFeatures).contains("feature");
assertThat(disabledFeatures).doesNotContain("other");
}
@Test
public void testFeatureEnabledInPackage() throws Exception {
scratch.file("a/BUILD", "package(features = ['feature'])", "cc_library(name = 'a')");
ImmutableSet<String> features = getRuleContext(configure("//a")).getFeatures();
assertThat(features).contains("feature");
assertThat(features).doesNotContain("other");
}
@Test
public void testFeatureDisableddInPackage() throws Exception {
scratch.file("a/BUILD", "package(features = ['-feature'])", "cc_library(name = 'a')");
ImmutableSet<String> disabledFeatures = getRuleContext(configure("//a")).getDisabledFeatures();
assertThat(disabledFeatures).contains("feature");
assertThat(disabledFeatures).doesNotContain("other");
}
@Test
public void testFeatureEnabledInRule() throws Exception {
scratch.file("a/BUILD",
"cc_library(name = 'a', features = ['feature'])");
ImmutableSet<String> features = getRuleContext(configure("//a")).getFeatures();
assertThat(features).contains("feature");
assertThat(features).doesNotContain("other");
}
@Test
public void testFeatureDisabledInRule() throws Exception {
scratch.file("a/BUILD", "cc_library(name = 'a', features = ['-feature'])");
ImmutableSet<String> disabledFeatures = getRuleContext(configure("//a")).getDisabledFeatures();
assertThat(disabledFeatures).contains("feature");
assertThat(disabledFeatures).doesNotContain("other");
}
@Test
public void testFeaturesInPackageOverrideFeaturesFromCommandLine() throws Exception {
useConfiguration("--features=feature");
scratch.file("a/BUILD", "package(features = ['-feature'])", "cc_library(name = 'a')");
RuleContext ruleContext = getRuleContext(configure("//a"));
ImmutableSet<String> features = ruleContext.getFeatures();
ImmutableSet<String> disabledFeatures = ruleContext.getDisabledFeatures();
assertThat(features).doesNotContain("feature");
assertThat(disabledFeatures).contains("feature");
}
@Test
public void testFeaturesInRuleOverrideFeaturesFromCommandLine() throws Exception {
useConfiguration("--features=feature");
scratch.file("a/BUILD", "cc_library(name = 'a', features = ['-feature'])");
RuleContext ruleContext = getRuleContext(configure("//a"));
ImmutableSet<String> features = ruleContext.getFeatures();
ImmutableSet<String> disabledFeatures = ruleContext.getDisabledFeatures();
assertThat(features).doesNotContain("feature");
assertThat(disabledFeatures).contains("feature");
}
@Test
public void testFeaturesInRuleOverrideFeaturesFromPackage() throws Exception {
scratch.file("a/BUILD",
"package(features = ['a', '-b', 'c'])",
"cc_library(name = 'a', features = ['b', '-c', 'd'])");
RuleContext ruleContext = getRuleContext(configure("//a"));
ImmutableSet<String> features = ruleContext.getFeatures();
ImmutableSet<String> disabledFeatures = ruleContext.getDisabledFeatures();
assertThat(features).containsAtLeast("a", "b", "d");
assertThat(disabledFeatures).contains("c");
}
@Test
public void testFeaturesDisabledFromCommandLineOverrideAll() throws Exception {
useConfiguration("--features=-package_feature", "--features=-rule_feature");
scratch.file(
"a/BUILD",
"package(features = ['package_feature'])",
"cc_library(name = 'a', features = ['rule_feature'])");
RuleContext ruleContext = getRuleContext(configure("//a"));
ImmutableSet<String> features = ruleContext.getFeatures();
ImmutableSet<String> disabledFeatures = ruleContext.getDisabledFeatures();
assertThat(features).doesNotContain("package_feature");
assertThat(features).doesNotContain("rule_feature");
assertThat(disabledFeatures).contains("package_feature");
assertThat(disabledFeatures).contains("rule_feature");
}
@Test
public void testExperimentalDependenciesOnThirdPartyExperimentalAllowed() throws Exception {
scratch.file(
"third_party/experimental/p1/BUILD",
"licenses(['unencumbered'])",
"exports_files(['p1.cc'])",
"cc_library(name = 'p1')");
scratch.file(
"experimental/p2/BUILD",
"exports_files(['p2.cc'])",
"cc_library(name = 'p2', deps=['//third_party/experimental/p1:p1'])");
getConfiguredTarget("//experimental/p2:p2"); // No errors.
}
@Test
public void testThirdPartyExperimentalDependenciesOnExperimentalAllowed() throws Exception {
scratch.file("experimental/p1/BUILD", "exports_files(['p1.cc'])", "cc_library(name = 'p1')");
scratch.file(
"third_party/experimental/p2/BUILD",
"licenses(['unencumbered'])",
"exports_files(['p2.cc'])",
"cc_library(name = 'p2', deps=['//experimental/p1:p1'])");
getConfiguredTarget("//third_party/experimental/p2:p2"); // No errors.
}
@Test
public void testDependencyOnTestOnlyAllowed() throws Exception {
scratch.file("testonly/BUILD",
"cc_library(name = 'testutil',",
" srcs = ['testutil.cc'],",
" testonly = 1)");
scratch.file("util/BUILD",
"cc_library(name = 'util',",
" srcs = ['util.cc'])");
scratch.file("cc/common/BUILD",
// testonly=1 -> testonly=1
"cc_library(name = 'lib1',",
" srcs = ['foo1.cc'],",
" deps = ['//testonly:testutil'],",
" testonly = 1)",
// testonly=0 -> testonly=0
"cc_library(name = 'lib2',",
" srcs = ['foo2.cc'],",
" deps = ['//util'],",
" testonly = 0)",
// testonly=1 -> testonly=0
"cc_library(name = 'lib3',",
" srcs = ['foo3.cc'],",
" deps = [':lib2'],",
" testonly = 1)");
getConfiguredTarget("//cc/common:lib1"); // No errors.
getConfiguredTarget("//cc/common:lib2"); // No errors.
getConfiguredTarget("//cc/common:lib3"); // No errors.
}
@Test
public void testDependsOnTestOnlyDisallowed() throws Exception {
scratch.file("testonly/BUILD",
"cc_library(name = 'testutil',",
" srcs = ['testutil.cc'],",
" testonly = 1)");
checkError("cc/error", "cclib",
// error:
"non-test target '//cc/error:cclib' depends on testonly target '//testonly:testutil' and "
+ "doesn't have testonly attribute set",
// build file: testonly=0 -> testonly=1
"cc_library(name = 'cclib',",
" srcs = ['foo.cc'],",
" deps = ['//testonly:testutil'],",
" testonly = 0)");
}
@Test
public void testDependenceOnDeprecatedRule() throws Exception {
scratch.file("p/BUILD",
"cc_library(name='p', deps=['//q'])");
scratch.file("q/BUILD",
"cc_library(name='q', deprecation='Obsolete!')");
reporter.removeHandler(failFastHandler); // expect errors
ConfiguredTarget p = getConfiguredTarget("//p");
assertThat(view.hasErrors(p)).isFalse();
assertContainsEvent("target '//p:p' depends on deprecated target '//q:q':"
+ " Obsolete!");
assertThat(eventCollector.count()).isEqualTo(1);
}
@Test
public void testDependenceOnDeprecatedRuleEmptyExplanation() throws Exception {
scratch.file("p/BUILD",
"cc_library(name='p', deps=['//q'])");
scratch.file("q/BUILD",
"cc_library(name='q', deprecation='')"); // explicitly specified; still counts!
reporter.removeHandler(failFastHandler); // expect errors
ConfiguredTarget p = getConfiguredTarget("//p");
assertThat(view.hasErrors(p)).isFalse();
assertContainsEvent("target '//p:p' depends on deprecated target '//q:q'");
assertThat(eventCollector.count()).isEqualTo(1);
}
@Test
public void testNoWarningWhenDeprecatedDependsOnDeprecatedRule() throws Exception {
scratch.file("foo/BUILD",
"java_library(name='foo', srcs=['foo.java'], deps=['//bar:bar'])");
scratch.file("bar/BUILD",
"java_library(name='bar', srcs=['bar.java'], deps=['//baz:baz'], deprecation='BAR')");
scratch.file("baz/BUILD",
"java_library(name='baz', srcs=['baz.java'], deprecation='BAZ')");
reporter.removeHandler(failFastHandler); // expect errors
getConfiguredTarget("//foo");
assertContainsEvent("target '//foo:foo' depends on deprecated "
+ "target '//bar:bar': BAR");
assertDoesNotContainEvent("target '//bar:bar' depends on deprecated "
+ "target '//baz:baz': BAZ");
assertThat(eventCollector.count()).isEqualTo(1);
}
@Test
public void testAttributeErrorContainsLocationOfRule() throws Exception {
Event e =
checkError(
"x",
"x",
// error:
getErrorNonExistingTarget("srcs", "cc_library", "//x:x", "//x:a.java"),
// build file:
"# blank line",
"cc_library(name = 'x',",
" srcs = ['a.java'])");
assertThat(e.getLocation().toString()).isEqualTo("/workspace/x/BUILD:2:1");
}
@Test
public void testJavatestsIsTestonly() throws Exception {
scratch.file("java/x/BUILD",
"java_library(name='x', exports=['//javatests/y'])");
scratch.file("javatests/y/BUILD",
"java_library(name='y')");
reporter.removeHandler(failFastHandler); // expect warning
ConfiguredTarget target = getConfiguredTarget("//java/x");
assertContainsEvent("non-test target '//java/x:x' depends on testonly target"
+ " '//javatests/y:y' and doesn't have testonly attribute set");
assertThat(view.hasErrors(target)).isTrue();
}
@Test
public void testDependenceOfJavaProductionCodeOnTestPackageGroups() throws Exception {
scratch.file("java/banana/BUILD",
"java_library(name='banana',",
" visibility=['//javatests/plantain:chips'])");
scratch.file("javatests/plantain/BUILD",
"package_group(name='chips',",
" packages=['//javatests/plantain'])");
getConfiguredTarget("//java/banana");
assertNoEvents();
}
@Test
public void testUnexpectedSourceFileInDeps() throws Exception {
scratch.file("x/y.java", "foo");
checkError("x", "x", getErrorMsgMisplacedFiles(
"deps", "java_library", "//x:x", "//x:y.java"),
"java_library(name='x', srcs=['x.java'], deps=['y.java'])");
}
@Test
public void testUnexpectedButExistingSourceFileDependency() throws Exception {
scratch.file("x/y.java");
checkError("x", "x", getErrorMsgMisplacedFiles(
"deps", "java_library", "//x:x", "//x:y.java"),
"java_library(name='x', srcs=['x.java'], deps=['y.java'])");
}
@Test
public void testGetArtifactForImplicitOutput() throws Exception {
scratch.file("java/x/BUILD",
"java_binary(name='x', srcs=['x.java'])");
ConfiguredTarget javaBinary = getConfiguredTarget("//java/x:x");
Artifact classJarArtifact = getFileConfiguredTarget("//java/x:x.jar").getArtifact();
// Checks if the deploy jar is generated
getFileConfiguredTarget("//java/x:x_deploy.jar").getArtifact();
assertThat(getOutputGroup(javaBinary, OutputGroupInfo.FILES_TO_COMPILE).toList())
.containsExactly(classJarArtifact);
}
@Test
public void testSelfEdgeInRule() throws Exception {
scratch.file("x/BUILD",
"genrule(name='x', srcs=['x'], outs=['out'], cmd=':')");
reporter.removeHandler(failFastHandler); // expect errors
getConfiguredTarget("//x");
assertContainsSelfEdgeEvent("//x:x");
}
@Test
public void testNegativeShardCount() throws Exception {
checkError("foo", "bar", "Must not be negative.",
"sh_test(name='bar', srcs=['mockingbird.sh'], shard_count=-1)");
}
@Test
public void testExcessiveShardCount() throws Exception {
checkError("foo", "bar", "indicative of poor test organization",
"sh_test(name='bar', srcs=['mockingbird.sh'], shard_count=51)");
}
@Test
public void testNonexistingTargetErrorMsg() throws Exception {
checkError("foo", "foo", getErrorNonExistingTarget(
"deps", "cc_binary", "//foo:foo", "//foo:nonesuch"),
"cc_binary(name = 'foo',",
"srcs = ['foo.cc'],",
"deps = [':nonesuch'])");
}
@Test
public void testRulesDontProvideRequiredFragmentsByDefault() throws Exception {
scratch.file(
"a/BUILD",
"config_setting(name = 'config', values = {'start_end_lib': '1'})",
"py_library(name = 'pylib', srcs = ['pylib.py'])",
"cc_library(name = 'a', srcs = ['A.cc'], data = [':pylib'])");
assertThat(getConfiguredTarget("//a:a").getProvider(RequiredConfigFragmentsProvider.class))
.isNull();
assertThat(
getConfiguredTarget("//a:config")
.getProvider(ConfigMatchingProvider.class)
.getRequiredFragmentOptions())
.isEmpty();
}
@Test
public void testProvideTransitiveRequiredFragmentsMode() throws Exception {
useConfiguration("--include_config_fragments_provider=transitive");
scratch.file(
"a/BUILD",
"config_setting(name = 'config', values = {'start_end_lib': '1'})",
"py_library(name = 'pylib', srcs = ['pylib.py'])",
"cc_library(name = 'a', srcs = ['A.cc'], data = [':pylib'])");
ImmutableSet<String> ccLibTransitiveFragments =
getConfiguredTarget("//a:a")
.getProvider(RequiredConfigFragmentsProvider.class)
.getRequiredConfigFragments();
assertThat(ccLibTransitiveFragments).containsAtLeast("CppConfiguration", "PythonConfiguration");
ImmutableSet<String> configSettingTransitiveFragments =
getConfiguredTarget("//a:config")
.getProvider(RequiredConfigFragmentsProvider.class)
.getRequiredConfigFragments();
assertThat(configSettingTransitiveFragments).contains("CppOptions");
}
@Test
public void testProvideDirectRequiredFragmentsMode() throws Exception {
useConfiguration("--include_config_fragments_provider=direct");
scratch.file(
"a/BUILD",
"config_setting(name = 'config', values = {'start_end_lib': '1'})",
"py_library(name = 'pylib', srcs = ['pylib.py'])",
"cc_library(name = 'a', srcs = ['A.cc'], data = [':pylib'])");
ImmutableSet<String> ccLibTransitiveFragments =
getConfiguredTarget("//a:a")
.getProvider(RequiredConfigFragmentsProvider.class)
.getRequiredConfigFragments();
assertThat(ccLibTransitiveFragments).contains("CppConfiguration");
assertThat(ccLibTransitiveFragments).doesNotContain("PythonConfiguration");
ImmutableSet<String> configSettingTransitiveFragments =
getConfiguredTarget("//a:config")
.getProvider(RequiredConfigFragmentsProvider.class)
.getRequiredConfigFragments();
assertThat(configSettingTransitiveFragments).contains("CppOptions");
}
}