blob: e57cb26feae82c1c29f52cb1ff7fc0c2c03ec271 [file] [log] [blame]
// Copyright 2019 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 com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
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.clock.BlazeClock;
import com.google.devtools.build.lib.cmdline.RepositoryName;
import com.google.devtools.build.lib.events.EventCollector;
import com.google.devtools.build.lib.events.Reporter;
import com.google.devtools.build.lib.packages.BuildFileName;
import com.google.devtools.build.lib.packages.StarlarkSemanticsOptions;
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.MoreAsserts;
import com.google.devtools.build.lib.testutil.Scratch;
import com.google.devtools.build.lib.testutil.TestConstants;
import com.google.devtools.build.lib.util.AbruptExitException;
import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
import com.google.devtools.build.lib.vfs.FileSystem;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.vfs.Root;
import com.google.devtools.build.lib.vfs.RootedPath;
import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
import com.google.devtools.build.skyframe.BuildDriver;
import com.google.devtools.build.skyframe.EvaluationContext;
import com.google.devtools.build.skyframe.EvaluationResult;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.common.options.Options;
import com.google.devtools.common.options.OptionsProvider;
import java.io.IOException;
import java.util.List;
import java.util.UUID;
import org.junit.Before;
import org.junit.Test;
/**
* Abstract base class for testing an implementation of a {@link SkyFunction} for {@link
* SkyFunctions#COLLECT_PACKAGES_UNDER_DIRECTORY}.
*/
public abstract class AbstractCollectPackagesUnderDirectoryTest {
protected FileSystem fileSystem;
protected Root root;
protected Path workingDir;
private Scratch scratch;
protected BlazeDirectories directories;
private EventCollector eventCollector;
private Reporter reporter;
protected ConfiguredRuleClassProvider ruleClassProvider;
private BuildDriver buildDriver;
@Before
public void setUp() throws IOException {
fileSystem = new InMemoryFileSystem();
workingDir = fileSystem.getPath(getWorkspacePathString());
workingDir.createDirectoryAndParents();
root = Root.fromPath(workingDir);
scratch = new Scratch(workingDir);
directories =
new BlazeDirectories(
new ServerDirectories(
fileSystem.getPath("/install"),
fileSystem.getPath("/output"),
fileSystem.getPath("/user_root")),
workingDir,
/*defaultSystemJavabase=*/ null,
/*productName=*/ "DummyProductNameForUnitTests");
eventCollector = new EventCollector();
reporter = new Reporter(new EventBus());
reporter.addHandler(eventCollector);
}
protected abstract String getWorkspacePathString();
protected abstract List<BuildFileName> getBuildFileNamesByPriority();
protected abstract ImmutableMap<SkyFunctionName, SkyFunction> getExtraSkyFunctions();
protected abstract SkyframeExecutorFactory makeSkyframeExecutorFactory();
@Test
public void noPackageErrors() throws Exception {
initBuildDriver();
scratch.file("BUILD");
scratch.dir("a1/b1/c1");
scratch.file("a1/b1/c1/BUILD");
scratch.dir("a1/b1/c2");
scratch.dir("a1/b2/c1");
scratch.dir("a1/b2/c2");
scratch.dir("a2/b1/c1");
scratch.file("a2/b1/c1/BUILD");
scratch.dir("a2/b1/c2");
scratch.dir("a2/b2/c1");
scratch.dir("a2/b2/c2");
scratch.file("a2/b2/c2/BUILD");
{
CollectPackagesUnderDirectoryValue collectPackagesUnderDirectoryValue =
getCollectPackagesUnderDirectoryValue("");
assertThat(collectPackagesUnderDirectoryValue.isDirectoryPackage()).isTrue();
assertThat(
collectPackagesUnderDirectoryValue
.getSubdirectoryTransitivelyContainsPackagesOrErrors())
.containsExactly(
rootedPath("tools"), Boolean.TRUE,
rootedPath("a1"), Boolean.TRUE,
rootedPath("a2"), Boolean.TRUE);
}
{
CollectPackagesUnderDirectoryValue collectPackagesUnderDirectoryValue =
getCollectPackagesUnderDirectoryValue("a1");
assertThat(collectPackagesUnderDirectoryValue.isDirectoryPackage()).isFalse();
assertThat(
collectPackagesUnderDirectoryValue
.getSubdirectoryTransitivelyContainsPackagesOrErrors())
.containsExactly(rootedPath("a1/b1"), Boolean.TRUE, rootedPath("a1/b2"), Boolean.FALSE);
}
{
CollectPackagesUnderDirectoryValue collectPackagesUnderDirectoryValue =
getCollectPackagesUnderDirectoryValue("a2/b1");
assertThat(collectPackagesUnderDirectoryValue.isDirectoryPackage()).isFalse();
assertThat(
collectPackagesUnderDirectoryValue
.getSubdirectoryTransitivelyContainsPackagesOrErrors())
.containsExactly(
rootedPath("a2/b1/c1"), Boolean.TRUE, rootedPath("a2/b1/c2"), Boolean.FALSE);
}
}
@Test
public void packageErrors() throws Exception {
initBuildDriver();
scratch.dir("a1/b1");
scratch.file("a1/b1/BUILD", "xxx");
scratch.dir("a1/b2");
scratch.dir("a2/b1");
scratch.dir("a2/b2");
scratch.file("a2/b2/BUILD", "yyy");
CollectPackagesUnderDirectoryValue collectPackagesUnderDirectoryValue =
getCollectPackagesUnderDirectoryValue("");
assertThat(collectPackagesUnderDirectoryValue.isDirectoryPackage()).isFalse();
assertThat(
collectPackagesUnderDirectoryValue
.getSubdirectoryTransitivelyContainsPackagesOrErrors())
.containsExactly(
rootedPath("tools"), Boolean.TRUE,
rootedPath("a1"), Boolean.TRUE,
rootedPath("a2"), Boolean.TRUE);
MoreAsserts.assertContainsEvent(eventCollector, "Loading package: a1/b1");
MoreAsserts.assertContainsEvent(eventCollector, "a1/b1/BUILD:1:1: name 'xxx' is not defined");
MoreAsserts.assertContainsEvent(eventCollector, "Loading package: a2/b2");
MoreAsserts.assertContainsEvent(eventCollector, "a2/b2/BUILD:1:1: name 'yyy' is not defined");
}
@Test
public void symlinks() throws Exception {
initBuildDriver();
Path a1DirPath = scratch.dir("a1");
scratch.dir("a1/b1/c1");
Path a1CircularPath = scratch.resolve("a1/circular");
FileSystemUtils.ensureSymbolicLink(a1CircularPath, a1CircularPath);
scratch.file("a1/b1/c1/BUILD");
FileSystemUtils.ensureSymbolicLink(scratch.resolve("a2"), a1DirPath);
scratch.dir("a3");
scratch.file(
"a3/DONT_FOLLOW_SYMLINKS_WHEN_TRAVERSING_THIS_DIRECTORY_VIA_A_RECURSIVE_TARGET_PATTERN");
FileSystemUtils.ensureSymbolicLink(scratch.resolve("a3/dirlink"), a1DirPath);
CollectPackagesUnderDirectoryValue collectPackagesUnderDirectoryValue =
getCollectPackagesUnderDirectoryValue("");
assertThat(collectPackagesUnderDirectoryValue.isDirectoryPackage()).isFalse();
assertThat(
collectPackagesUnderDirectoryValue
.getSubdirectoryTransitivelyContainsPackagesOrErrors())
.containsExactly(
rootedPath("tools"), Boolean.TRUE,
rootedPath("a1"), Boolean.TRUE,
rootedPath("a2"), Boolean.TRUE,
rootedPath("a3"), Boolean.FALSE);
MoreAsserts.assertContainsEvent(eventCollector, "Loading package: a1/b1/c1");
MoreAsserts.assertContainsEvent(eventCollector, "Loading package: a2/b1/c1");
MoreAsserts.assertDoesNotContainEvent(eventCollector, "Loading package: a3/b1/c1");
MoreAsserts.assertContainsEvent(
eventCollector,
"Failed to get information about path, for a1/circular, skipping: Symlink cycle");
MoreAsserts.assertContainsEvent(
eventCollector,
"Failed to get information about path, for a2/circular, skipping: Symlink cycle");
}
@Test
public void excludedPaths() throws Exception {
initBuildDriver();
scratch.dir("a1/b1/c1");
scratch.file("a1/b1/c1/BUILD");
scratch.dir("a1/b1/c2");
scratch.file("a1/b1/c2/BUILD");
scratch.dir("a1/b2/c1");
scratch.file("a1/b2/c1/BUILD");
scratch.dir("a1/b2/c2");
scratch.file("a1/b2/c2/BUILD");
scratch.dir("a2/b1/c1");
scratch.file("a2/b1/c1/BUILD");
scratch.dir("a2/b1/c2");
scratch.file("a2/b1/c2/BUILD");
scratch.dir("a2/b2/c1");
scratch.file("a2/b2/c1/BUILD");
scratch.dir("a2/b2/c2");
scratch.file("a2/b2/c2/BUILD");
CollectPackagesUnderDirectoryValue collectPackagesUnderDirectoryValue =
getCollectPackagesUnderDirectoryValue(
"",
/*excludedPaths=*/ ImmutableSet.of(
PathFragment.create("a1"),
PathFragment.create("a2/b1"),
PathFragment.create("a2/b2/c2")));
assertThat(collectPackagesUnderDirectoryValue.isDirectoryPackage()).isFalse();
// There is not supposed to be a map entry for excluded subdirectories.
assertThat(
collectPackagesUnderDirectoryValue
.getSubdirectoryTransitivelyContainsPackagesOrErrors())
.containsExactly(
rootedPath("tools"), Boolean.TRUE,
rootedPath("a2"), Boolean.TRUE);
MoreAsserts.assertDoesNotContainEvent(eventCollector, "Loading package: a1/b1/c1");
MoreAsserts.assertDoesNotContainEvent(eventCollector, "Loading package: a1/b1/c2");
MoreAsserts.assertDoesNotContainEvent(eventCollector, "Loading package: a1/b2/c1");
MoreAsserts.assertDoesNotContainEvent(eventCollector, "Loading package: a1/b1/c2");
MoreAsserts.assertDoesNotContainEvent(eventCollector, "Loading package: a2/b1/c1");
MoreAsserts.assertDoesNotContainEvent(eventCollector, "Loading package: a2/b1/c2");
MoreAsserts.assertContainsEvent(eventCollector, "Loading package: a2/b2/c1");
MoreAsserts.assertDoesNotContainEvent(eventCollector, "Loading package: a2/b2/c2");
}
private void initBuildDriver() throws AbruptExitException, InterruptedException, IOException {
PathPackageLocator pathPackageLocator =
PathPackageLocator.createWithoutExistenceCheck(
directories.getOutputBase(), ImmutableList.of(root), getBuildFileNamesByPriority());
PackageCacheOptions packageCacheOptions = Options.getDefaults(PackageCacheOptions.class);
packageCacheOptions.packagePath = ImmutableList.of(getWorkspacePathString());
scratch.file("tools/BUILD");
scratch.file("tools/empty_prelude.bzl");
ruleClassProvider =
new ConfiguredRuleClassProvider.Builder()
.setRunfilesPrefix("workspace")
.setPrelude("//tools:empty_prelude.bzl")
.build();
SkyframeExecutor skyframeExecutor =
makeSkyframeExecutorFactory()
.create(
TestConstants.PACKAGE_FACTORY_BUILDER_FACTORY_FOR_TESTING
.builder(directories)
.build(ruleClassProvider, fileSystem),
fileSystem,
directories,
new ActionKeyContext(),
/*workspaceStatusActionFactory=*/ null,
/*diffAwarenessFactories=*/ ImmutableList.of(),
getExtraSkyFunctions(),
/*customDirtinessCheckers=*/ ImmutableList.of(),
/*managedDirectoriesKnowledge=*/ null);
skyframeExecutor.injectExtraPrecomputedValues(
ImmutableList.of(
PrecomputedValue.injected(
RepositoryDelegatorFunction.RESOLVED_FILE_INSTEAD_OF_WORKSPACE, Optional.absent()),
PrecomputedValue.injected(
RepositoryDelegatorFunction.REPOSITORY_OVERRIDES, ImmutableMap.of()),
PrecomputedValue.injected(
RepositoryDelegatorFunction.DEPENDENCY_FOR_UNCONDITIONAL_FETCHING,
RepositoryDelegatorFunction.DONT_FETCH_UNCONDITIONALLY)));
skyframeExecutor.sync(
reporter,
packageCacheOptions,
pathPackageLocator,
Options.getDefaults(StarlarkSemanticsOptions.class),
UUID.randomUUID(),
/*clientEnv=*/ ImmutableMap.of(),
new TimestampGranularityMonitor(BlazeClock.instance()),
OptionsProvider.EMPTY);
buildDriver = skyframeExecutor.getDriver();
}
private CollectPackagesUnderDirectoryValue getCollectPackagesUnderDirectoryValue(String directory)
throws InterruptedException {
return getCollectPackagesUnderDirectoryValue(directory, /*excludedPaths=*/ ImmutableSet.of());
}
private CollectPackagesUnderDirectoryValue getCollectPackagesUnderDirectoryValue(
String directory, ImmutableSet<PathFragment> excludedPaths) throws InterruptedException {
SkyKey key =
CollectPackagesUnderDirectoryValue.key(
RepositoryName.MAIN, rootedPath(directory), excludedPaths);
return evaluate(key).get(key);
}
private RootedPath rootedPath(String relativePath) {
return RootedPath.toRootedPath(root, PathFragment.create(relativePath));
}
private EvaluationResult<CollectPackagesUnderDirectoryValue> evaluate(SkyKey key)
throws InterruptedException {
EvaluationContext evaluationContext =
EvaluationContext.newBuilder()
.setKeepGoing(true)
.setNumThreads(1)
.setEventHander(new Reporter(new EventBus(), reporter))
.build();
return buildDriver.evaluate(ImmutableList.of(key), evaluationContext);
}
}