blob: 93726a163bf5abc13f39f7ecc4ab532103cc5198 [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.buildtool;
import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.lib.testutil.MoreAsserts.assertDoesNotContainEvent;
import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.testing.GcFinalization;
import com.google.devtools.build.lib.actions.BuildFailedException;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.ViewCreationFailedException;
import com.google.devtools.build.lib.analysis.util.AnalysisMock;
import com.google.devtools.build.lib.buildtool.util.BuildIntegrationTestCase;
import com.google.devtools.build.lib.events.EventCollector;
import com.google.devtools.build.lib.events.EventKind;
import com.google.devtools.build.lib.testutil.MoreAsserts;
import java.lang.ref.WeakReference;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Miscellaneous tests of the analysis phase. (Sometimes it's easier to express these in terms of
* the BuildTool than of the BuildView because the latter's class interface is quite complex.)
*/
@RunWith(JUnit4.class)
public class MiscAnalysisTest extends BuildIntegrationTestCase {
@Test
public void testWarningsNotReplayed() throws Exception {
AnalysisMock.get().pySupport().setup(mockToolsConfig);
write(
"y/BUILD",
"genrule(name='y', outs=['y.out'], cmd='touch $@', deprecation='generate a warning')");
addOptions("--nobuild");
buildTarget("//y");
events.assertContainsWarning("target '//y:y' is deprecated");
events.clear();
buildTarget("//y");
events.assertDoesNotContainEvent("target '//y:y' is deprecated");
}
@Test
public void testDeprecatedTargetOnCommandLine() throws Exception {
write(
"raspberry/BUILD",
"sh_library(name='raspberry', srcs=['raspberry.sh'], deprecation='rotten')");
addOptions("--nobuild");
buildTarget("//raspberry:raspberry");
events.assertContainsWarning("target '//raspberry:raspberry' is deprecated: rotten");
}
@Test
public void targetAnalyzedInTwoConfigurations_deprecationWarningDisplayedOncePerBuild()
throws Exception {
// :a depends on :dep in the target configuration. :b depends on :dep in the exec configuration.
write(
"foo/BUILD",
"genrule(name = 'a', outs = ['a.out'], srcs = [':dep'], cmd = 'touch $@')",
"genrule(name = 'b', outs = ['b.out'], tools = [':dep'], cmd = 'touch $@')",
"genrule(name = 'dep', outs = ['dep.out'], srcs = ['//deprecated'], cmd = 'touch $@')");
write("deprecated/BUILD", "sh_library(name = 'deprecated', deprecation = 'old')");
addOptions("--nobuild");
buildTarget("//foo:a", "//foo:b");
events.assertContainsEventWithFrequency(
"'//foo:dep' depends on deprecated target '//deprecated:deprecated'", 1);
events.clear();
// Edit to force re-analysis.
write("deprecated/BUILD", "sh_library(name = 'deprecated', deprecation = 'very old')");
buildTarget("//foo:a", "//foo:b");
events.assertContainsEventWithFrequency(
"'//foo:dep' depends on deprecated target '//deprecated:deprecated'", 1);
}
// Regression test for http://b/12465751: "IllegalStateException in ParallelEvaluator".
@Test
public void testShBinaryTwoSrcs() throws Exception {
write("sh/BUILD", "sh_test(name = 'double', srcs = ['a','b'])");
addOptions("--nobuild");
assertThrows(Exception.class, () -> buildTarget("//sh:double"));
events.assertContainsError("you must specify exactly one file in 'srcs'");
}
// Note that the cache_analysis flag has been deleted, as it is now standard app behavior.
@Test
public void testAnalysisCachingAndKeepGoing() throws Exception {
write(
"fruit/BUILD",
"cc_library(name='apple', deps=[':banana'])",
"cc_library(name='banana', deps=[':cherry'])",
"cc_library(name='cherry', deps=[':durian__hdrs__'])",
"genrule(name='durian', outs=['durian.out'], cmd=':')");
addOptions("--nobuild", "--keep_going");
BuildFailedException e =
assertThrows(BuildFailedException.class, () -> buildTarget("//fruit:apple"));
assertThat(e).hasMessageThat().contains("command succeeded");
events.assertContainsError(
"in deps attribute of cc_library rule //fruit:cherry: "
+ "target '//fruit:durian__hdrs__' does not exist");
events.clear();
e = assertThrows(BuildFailedException.class, () -> buildTarget("//fruit:apple"));
assertThat(e).hasMessageThat().contains("command succeeded");
events.assertContainsError(
"in deps attribute of cc_library rule //fruit:cherry: "
+ "target '//fruit:durian__hdrs__' does not exist");
}
// Note that the cache_analysis flag has been deleted, as it is now standard app behavior.
@Test
public void testErrorsAreReplayedEvenWithAnalysisCaching() throws Exception {
write(
"fruit/BUILD",
"cc_library(name='apple', deps=[':banana__hdrs__'])",
"genrule(name='banana', outs=['banana.out'], cmd=':')");
addOptions("--nobuild");
assertThrows(ViewCreationFailedException.class, () -> buildTarget("//fruit:apple"));
events.assertContainsError(
"in deps attribute of cc_library rule //fruit:apple: "
+ "target '//fruit:banana__hdrs__' does not exist");
events.clear();
assertThrows(ViewCreationFailedException.class, () -> buildTarget("//fruit:apple"));
events.assertContainsError(
"in deps attribute of cc_library rule //fruit:apple: "
+ "target '//fruit:banana__hdrs__' does not exist");
}
@Test
public void testBuildAllAndParsingError() throws Exception {
write("pkg/BUILD",
"java_binary(",
"name = \"foo\",",
" syntax error here",
")");
addOptions("--nobuild");
Exception e = assertThrows(Exception.class, () -> buildTarget("//pkg:all"));
events.assertContainsError("syntax error at 'error'");
assertPkgErrorMsg(e);
}
@Test
public void testDiscardAnalysisCache() throws Exception {
write("sh/BUILD",
"sh_library(name = 'sh', srcs = [], deps = [':dep'])",
"sh_library(name = 'dep', srcs = [])"
);
buildTarget("//sh:sh");
// We test with dep because target completion middleman actions keep references to the
// top-level configured targets.
ConfiguredTarget ct = getConfiguredTarget("//sh:dep");
addOptions("--discard_analysis_cache");
buildTarget("//sh:sh");
addOptions("--nodiscard_analysis_cache");
buildTarget("//sh:sh");
// Configured target was replaced.
ConfiguredTarget newCt = getConfiguredTarget("//sh:dep");
assertThat(newCt).isNotSameInstanceAs(ct);
WeakReference<ConfiguredTarget> ref = new WeakReference<>(newCt);
newCt = null;
addOptions("--discard_analysis_cache");
buildTarget("//sh:sh");
GcFinalization.awaitClear(ref);
}
@Test
public void testDiscardAnalysisCacheWithError() throws Exception {
write(
"x/BUILD",
"cc_library(name='x', deps=[':z__hdrs__'])",
"genrule(name='z', outs=['z.out'], cmd=':')");
write("y/BUILD", "sh_library(name='y')");
addOptions("--discard_analysis_cache", "--keep_going");
EventCollector collector = new EventCollector(EventKind.STDERR);
events.addHandler(collector);
assertThrows(BuildFailedException.class, () -> buildTarget("//x:x", "//y:y"));
events.assertContainsError(
"in deps attribute of cc_library rule //x:x: target '//x:z__hdrs__' does not exist");
MoreAsserts.assertContainsEvent(collector, "Target //y:y up-to-date", EventKind.STDERR);
}
@Test
public void testBuildAllAndEvaluationError() throws Exception {
write("pkg/BUILD",
"java_binary(",
" name = \"foo\",",
" srcs = unknown_value,",
")");
addOptions("--nobuild");
Exception e = assertThrows(Exception.class, () -> buildTarget("//pkg:all"));
events.assertContainsError("name 'unknown_value' is not defined");
assertPkgErrorMsg(e);
}
private static void assertPkgErrorMsg(Exception e) {
assertThat(e).hasMessageThat().containsMatch("[pP]ackage.*contains errors");
}
@Test
public void testNoTestTargetsFoundMessageForBuildCommand() throws Exception {
write("pkg/BUILD");
for (String option : ImmutableList.of("", "--nobuild", "--noanalyze")) {
resetOptions();
addOptions(option);
buildTarget("//pkg:all");
assertDoesNotContainEvent(events.infos(), "test target");
}
}
}