// 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.skyframe; import static com.google.common.truth.Truth.assertThat; import static com.google.devtools.build.skyframe.WalkableGraphUtils.exists; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.eventbus.EventBus; import com.google.devtools.build.lib.actions.ActionKeyContext; import com.google.devtools.build.lib.analysis.BlazeDirectories; import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; import com.google.devtools.build.lib.analysis.ServerDirectories; import com.google.devtools.build.lib.analysis.util.AnalysisMock; import com.google.devtools.build.lib.analysis.util.DefaultBuildOptionsForTesting; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.PackageIdentifier; import com.google.devtools.build.lib.cmdline.RepositoryName; import com.google.devtools.build.lib.events.Reporter; import com.google.devtools.build.lib.packages.SkylarkSemanticsOptions; import com.google.devtools.build.lib.pkgcache.PackageCacheOptions; import com.google.devtools.build.lib.pkgcache.PathPackageLocator; import com.google.devtools.build.lib.rules.repository.RepositoryDelegatorFunction; import com.google.devtools.build.lib.testutil.FoundationTestCase; import com.google.devtools.build.lib.testutil.TestConstants; import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor; import com.google.devtools.build.lib.vfs.PathFragment; import com.google.devtools.build.lib.vfs.Root; import com.google.devtools.build.skyframe.EvaluationResult; import com.google.devtools.build.skyframe.SkyKey; import com.google.devtools.build.skyframe.SkyValue; import com.google.devtools.build.skyframe.WalkableGraph; import com.google.devtools.common.options.Options; import java.io.IOException; import java.util.UUID; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests for {@link PrepareDepsOfPatternsFunction}. */ @RunWith(JUnit4.class) public class PrepareDepsOfPatternsFunctionSmartNegationTest extends FoundationTestCase { private SkyframeExecutor skyframeExecutor; private static final String ADDITIONAL_BLACKLISTED_PACKAGE_PREFIXES_FILE_PATH_STRING = "config/blacklist.txt"; private static SkyKey getKeyForLabel(Label label) { // Note that these tests used to look for TargetMarker SkyKeys before TargetMarker was // inlined in TransitiveTraversalFunction. Because TargetMarker is now inlined, it doesn't // appear in the graph. Instead, these tests now look for TransitiveTraversal keys. return TransitiveTraversalValue.key(label); } @Before public void setUp() throws Exception { BlazeDirectories directories = new BlazeDirectories( new ServerDirectories( getScratch().dir("/install"), getScratch().dir("/output"), getScratch().dir("/user_root")), rootDirectory, /* defaultSystemJavabase= */ null, AnalysisMock.get().getProductName()); ConfiguredRuleClassProvider ruleClassProvider = AnalysisMock.get().createRuleClassProvider(); skyframeExecutor = SequencedSkyframeExecutor.create( AnalysisMock.get() .getPackageFactoryBuilderForTesting(directories) .build(ruleClassProvider), fileSystem, directories, new ActionKeyContext(), /*workspaceStatusActionFactory=*/ null, AnalysisMock.get().createRuleClassProvider().getBuildInfoFactories(), ImmutableList.of(), AnalysisMock.get().getSkyFunctions(directories), ImmutableList.of(), BazelSkyframeExecutorConstants.HARDCODED_BLACKLISTED_PACKAGE_PREFIXES, PathFragment.create(ADDITIONAL_BLACKLISTED_PACKAGE_PREFIXES_FILE_PATH_STRING), BazelSkyframeExecutorConstants.CROSS_REPOSITORY_LABEL_VIOLATION_STRATEGY, BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY, BazelSkyframeExecutorConstants.ACTION_ON_IO_EXCEPTION_READING_BUILD_FILE, DefaultBuildOptionsForTesting.getDefaultBuildOptionsForTest(ruleClassProvider)); TestConstants.processSkyframeExecutorForTesting(skyframeExecutor); skyframeExecutor.preparePackageLoading( new PathPackageLocator( outputBase, ImmutableList.of(Root.fromPath(rootDirectory)), BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY), Options.getDefaults(PackageCacheOptions.class), Options.getDefaults(SkylarkSemanticsOptions.class), AnalysisMock.get().getDefaultsPackageContent(), UUID.randomUUID(), ImmutableMap.of(), new TimestampGranularityMonitor(null)); skyframeExecutor.setActionEnv(ImmutableMap.of()); skyframeExecutor.injectExtraPrecomputedValues( ImmutableList.of( PrecomputedValue.injected( RepositoryDelegatorFunction.REPOSITORY_OVERRIDES, ImmutableMap.of()), PrecomputedValue.injected( RepositoryDelegatorFunction.DEPENDENCY_FOR_UNCONDITIONAL_FETCHING, RepositoryDelegatorFunction.DONT_FETCH_UNCONDITIONALLY))); scratch.file(ADDITIONAL_BLACKLISTED_PACKAGE_PREFIXES_FILE_PATH_STRING); } @Test public void testRecursiveEvaluationFailsOnBadBuildFile() throws Exception { // Given a well-formed package "@//foo" and a malformed package "@//foo/foo", createFooAndFooFoo(); // Given a target pattern sequence consisting of a recursive pattern for "//foo/...", ImmutableList patternSequence = ImmutableList.of("//foo/..."); // When PrepareDepsOfPatternsFunction completes evaluation (with no error because it was // recovered from), WalkableGraph walkableGraph = getGraphFromPatternsEvaluation( patternSequence, /*successExpected=*/ true, /*keepGoing=*/ true); // Then the graph contains package values for "@//foo" and "@//foo/foo", assertThat(exists(PackageValue.key(PackageIdentifier.parse("@//foo")), walkableGraph)).isTrue(); assertThat(exists(PackageValue.key(PackageIdentifier.parse("@//foo/foo")), walkableGraph)) .isTrue(); // But the graph does not contain a value for the target "@//foo/foo:foofoo". assertThat(exists(getKeyForLabel(Label.create("@//foo/foo", "foofoo")), walkableGraph)) .isFalse(); } @Test public void testNegativePatternBlocksPatternEvaluation() throws Exception { // Given a well-formed package "//foo" and a malformed package "//foo/foo", createFooAndFooFoo(); // Given a target pattern sequence consisting of a recursive pattern for "//foo/..." followed // by a negative pattern for the malformed package, ImmutableList patternSequence = ImmutableList.of("//foo/...", "-//foo/foo/..."); assertSkipsFoo(patternSequence); } @Test public void testBlacklistPatternBlocksPatternEvaluation() throws Exception { // Given a well-formed package "//foo" and a malformed package "//foo/foo", createFooAndFooFoo(); // Given a target pattern sequence consisting of a recursive pattern for "//foo/...", ImmutableList patternSequence = ImmutableList.of("//foo/..."); // and a blacklist for the malformed package, scratch.overwriteFile(ADDITIONAL_BLACKLISTED_PACKAGE_PREFIXES_FILE_PATH_STRING, "foo/foo"); assertSkipsFoo(patternSequence); } private void assertSkipsFoo(ImmutableList patternSequence) throws Exception { // When PrepareDepsOfPatternsFunction completes evaluation (successfully), WalkableGraph walkableGraph = getGraphFromPatternsEvaluation( patternSequence, /*successExpected=*/ true, /*keepGoing=*/ true); // Then the graph contains a package value for "@//foo", assertThat(exists(PackageValue.key(PackageIdentifier.parse("@//foo")), walkableGraph)).isTrue(); // But no package value for "@//foo/foo", assertThat(exists(PackageValue.key(PackageIdentifier.parse("@//foo/foo")), walkableGraph)) .isFalse(); // And the graph does not contain a value for the target "@//foo/foo:foofoo". Label label = Label.create("@//foo/foo", "foofoo"); assertThat(exists(getKeyForLabel(label), walkableGraph)).isFalse(); } @Test public void testNegativeNonTBDPatternsAreSkippedWithWarnings() throws Exception { // Given a target pattern sequence with a negative non-TBD pattern, ImmutableList patternSequence = ImmutableList.of("-//foo/bar"); // When PrepareDepsOfPatternsFunction completes evaluation, getGraphFromPatternsEvaluation(patternSequence, /*successExpected=*/ true, /*keepGoing=*/ true); // Then a event is published that says that negative non-TBD patterns are skipped. assertContainsEvent( "Skipping '-//foo/bar': Negative target patterns of types other than \"targets below " + "directory\" are not permitted."); } // Helpers: private WalkableGraph getGraphFromPatternsEvaluation( ImmutableList patternSequence, boolean successExpected, boolean keepGoing) throws InterruptedException { SkyKey independentTarget = PrepareDepsOfPatternsValue.key(patternSequence, ""); ImmutableList singletonTargetPattern = ImmutableList.of(independentTarget); // When PrepareDepsOfPatternsFunction completes evaluation, EvaluationResult evaluationResult = skyframeExecutor .getDriverForTesting() .evaluate( singletonTargetPattern, keepGoing, /*numThreads=*/ 100, new Reporter(new EventBus(), eventCollector)); // The evaluation has no errors if success was expected. assertThat(evaluationResult.hasError()).isNotEqualTo(successExpected); return Preconditions.checkNotNull(evaluationResult.getWalkableGraph()); } private void createFooAndFooFoo() throws IOException { scratch.file( "foo/BUILD", "genrule(name = 'foo',", " outs = ['out.txt'],", " cmd = 'touch $@')"); scratch.file( "foo/foo/BUILD", "genrule(name = 'foofoo',", " This isn't even remotely grammatical.)"); } }