blob: 568fd31620ddb665b15ae990641c757bfb2c742f [file] [log] [blame]
// Copyright 2017 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.repository;
import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.skyframe.EvaluationResultSubjectFactory.assertThatEvaluationResult;
import com.google.auto.value.AutoValue;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.FileStateValue;
import com.google.devtools.build.lib.actions.FileValue;
import com.google.devtools.build.lib.analysis.BlazeDirectories;
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.BuildViewTestCase;
import com.google.devtools.build.lib.cmdline.PackageIdentifier;
import com.google.devtools.build.lib.events.NullEventHandler;
import com.google.devtools.build.lib.packages.PackageFactory;
import com.google.devtools.build.lib.packages.PackageFactory.EnvironmentExtension;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.packages.RuleClassProvider;
import com.google.devtools.build.lib.packages.WorkspaceFileValue;
import com.google.devtools.build.lib.pkgcache.PathPackageLocator;
import com.google.devtools.build.lib.rules.repository.RepositoryDelegatorFunction;
import com.google.devtools.build.lib.skyframe.BazelSkyframeExecutorConstants;
import com.google.devtools.build.lib.skyframe.ExternalFilesHelper;
import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction;
import com.google.devtools.build.lib.skyframe.ExternalPackageFunction;
import com.google.devtools.build.lib.skyframe.FileFunction;
import com.google.devtools.build.lib.skyframe.FileStateFunction;
import com.google.devtools.build.lib.skyframe.LocalRepositoryLookupFunction;
import com.google.devtools.build.lib.skyframe.PackageFunction;
import com.google.devtools.build.lib.skyframe.PackageLookupFunction;
import com.google.devtools.build.lib.skyframe.PackageLookupFunction.CrossRepositoryLabelViolationStrategy;
import com.google.devtools.build.lib.skyframe.PrecomputedValue;
import com.google.devtools.build.lib.skyframe.RegisteredExecutionPlatformsFunction;
import com.google.devtools.build.lib.skyframe.RegisteredToolchainsFunction;
import com.google.devtools.build.lib.skyframe.SkyFunctions;
import com.google.devtools.build.lib.skyframe.SkyframeExecutor;
import com.google.devtools.build.lib.skyframe.WorkspaceASTFunction;
import com.google.devtools.build.lib.skyframe.WorkspaceFileFunction;
import com.google.devtools.build.lib.syntax.StarlarkSemantics;
import com.google.devtools.build.lib.util.io.TimestampGranularityMonitor;
import com.google.devtools.build.lib.vfs.Root;
import com.google.devtools.build.lib.vfs.RootedPath;
import com.google.devtools.build.skyframe.AbstractSkyKey;
import com.google.devtools.build.skyframe.EvaluationContext;
import com.google.devtools.build.skyframe.EvaluationResult;
import com.google.devtools.build.skyframe.InMemoryMemoizingEvaluator;
import com.google.devtools.build.skyframe.MemoizingEvaluator;
import com.google.devtools.build.skyframe.RecordingDifferencer;
import com.google.devtools.build.skyframe.SequencedRecordingDifferencer;
import com.google.devtools.build.skyframe.SequentialBuildDriver;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunctionException;
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link ExternalPackageUtil}. */
@RunWith(JUnit4.class)
public class ExternalPackageUtilTest extends BuildViewTestCase {
private static final EvaluationContext EVALUATION_OPTIONS =
EvaluationContext.newBuilder()
.setKeepGoing(false)
.setNumThreads(SkyframeExecutor.DEFAULT_THREAD_COUNT)
.setEventHander(NullEventHandler.INSTANCE)
.build();
private SequentialBuildDriver driver;
@Before
public void createEnvironment() {
AnalysisMock analysisMock = AnalysisMock.get();
AtomicReference<PathPackageLocator> pkgLocator =
new AtomicReference<>(
new PathPackageLocator(
outputBase,
ImmutableList.of(Root.fromPath(rootDirectory)),
BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
AtomicReference<ImmutableSet<PackageIdentifier>> deletedPackages =
new AtomicReference<>(ImmutableSet.<PackageIdentifier>of());
BlazeDirectories directories =
new BlazeDirectories(
new ServerDirectories(rootDirectory, outputBase, rootDirectory),
rootDirectory,
/* defaultSystemJavabase= */ null,
analysisMock.getProductName());
ExternalFilesHelper externalFilesHelper =
ExternalFilesHelper.createForTesting(
pkgLocator,
ExternalFileAction.DEPEND_ON_EXTERNAL_PKG_FOR_EXTERNAL_REPO_PATHS,
directories);
Map<SkyFunctionName, SkyFunction> skyFunctions = new HashMap<>();
skyFunctions.put(
SkyFunctions.PACKAGE_LOOKUP,
new PackageLookupFunction(
deletedPackages,
CrossRepositoryLabelViolationStrategy.ERROR,
BazelSkyframeExecutorConstants.BUILD_FILES_BY_PRIORITY));
skyFunctions.put(
FileStateValue.FILE_STATE,
new FileStateFunction(
new AtomicReference<TimestampGranularityMonitor>(), externalFilesHelper));
skyFunctions.put(FileValue.FILE, new FileFunction(pkgLocator));
RuleClassProvider ruleClassProvider = analysisMock.createRuleClassProvider();
skyFunctions.put(SkyFunctions.WORKSPACE_AST, new WorkspaceASTFunction(ruleClassProvider));
skyFunctions.put(
WorkspaceFileValue.WORKSPACE_FILE,
new WorkspaceFileFunction(
ruleClassProvider,
analysisMock
.getPackageFactoryBuilderForTesting(directories)
.setEnvironmentExtensions(
ImmutableList.<EnvironmentExtension>of(
new PackageFactory.EmptyEnvironmentExtension()))
.build(ruleClassProvider, fileSystem),
directories,
/*skylarkImportLookupFunctionForInlining=*/ null));
skyFunctions.put(
SkyFunctions.PACKAGE, new PackageFunction(null, null, null, null, null, null, null));
skyFunctions.put(SkyFunctions.EXTERNAL_PACKAGE, new ExternalPackageFunction());
skyFunctions.put(SkyFunctions.LOCAL_REPOSITORY_LOOKUP, new LocalRepositoryLookupFunction());
// Helper Skyfunctions to call ExternalPackageUtil.
skyFunctions.put(GET_RULE_BY_NAME_FUNCTION, new GetRuleByNameFunction());
skyFunctions.put(
GET_REGISTERED_EXECUTION_PLATFORMS_FUNCTION, new GetRegisteredExecutionPlatformsFunction());
skyFunctions.put(GET_REGISTERED_TOOLCHAINS_FUNCTION, new GetRegisteredToolchainsFunction());
RecordingDifferencer differencer = new SequencedRecordingDifferencer();
MemoizingEvaluator evaluator = new InMemoryMemoizingEvaluator(skyFunctions, differencer);
driver = new SequentialBuildDriver(evaluator);
PrecomputedValue.PATH_PACKAGE_LOCATOR.set(differencer, pkgLocator.get());
PrecomputedValue.STARLARK_SEMANTICS.set(differencer, StarlarkSemantics.DEFAULT_SEMANTICS);
RepositoryDelegatorFunction.RESOLVED_FILE_INSTEAD_OF_WORKSPACE.set(
differencer, Optional.<RootedPath>absent());
}
@Test
public void getRuleByName() throws Exception {
if (!analysisMock.isThisBazel()) {
return;
}
scratch.overwriteFile("WORKSPACE", "local_repository(name = 'foo', path = 'path/to/repo')");
SkyKey key = getRuleByNameKey("foo");
EvaluationResult<GetRuleByNameValue> result = getRuleByName(key);
assertThatEvaluationResult(result).hasNoError();
Rule rule = result.get(key).rule();
assertThat(rule).isNotNull();
assertThat(rule.getName()).isEqualTo("foo");
}
EvaluationResult<GetRuleByNameValue> getRuleByName(SkyKey key) throws InterruptedException {
return driver.<GetRuleByNameValue>evaluate(ImmutableList.of(key), EVALUATION_OPTIONS);
}
@Test
public void getRuleByName_missing() throws Exception {
if (!analysisMock.isThisBazel()) {
return;
}
scratch.overwriteFile("WORKSPACE", "local_repository(name = 'foo', path = 'path/to/repo')");
SkyKey key = getRuleByNameKey("bar");
EvaluationResult<GetRuleByNameValue> result = getRuleByName(key);
assertThatEvaluationResult(result)
.hasErrorEntryForKeyThat(key)
.hasExceptionThat()
.hasMessageThat()
.contains("The rule named 'bar' could not be resolved");
}
@Test
public void getRegisteredToolchains() throws Exception {
scratch.overwriteFile(
"WORKSPACE", "register_toolchains(", " '//toolchain:tc1',", " '//toolchain:tc2')");
SkyKey key = getRegisteredToolchainsKey();
EvaluationResult<GetRegisteredToolchainsValue> result = getRegisteredToolchains(key);
assertThatEvaluationResult(result).hasNoError();
assertThat(result.get(key).registeredToolchains())
// There are default toolchains that are always registered, so just check for the ones added
.containsAllOf("//toolchain:tc1", "//toolchain:tc2")
.inOrder();
}
EvaluationResult<GetRegisteredToolchainsValue> getRegisteredToolchains(SkyKey key)
throws InterruptedException {
return driver.<GetRegisteredToolchainsValue>evaluate(ImmutableList.of(key), EVALUATION_OPTIONS);
}
@Test
public void getRegisteredExecutionPlatforms() throws Exception {
scratch.overwriteFile(
"WORKSPACE", "register_execution_platforms(", " '//platform:ep1',", " '//platform:ep2')");
SkyKey key = getRegisteredExecutionPlatformsKey();
EvaluationResult<GetRegisteredExecutionPlatformsValue> result =
getRegisteredExecutionPlatforms(key);
assertThatEvaluationResult(result).hasNoError();
assertThat(result.get(key).registeredExecutionPlatforms())
.containsExactly("//platform:ep1", "//platform:ep2")
.inOrder();
}
EvaluationResult<GetRegisteredExecutionPlatformsValue> getRegisteredExecutionPlatforms(SkyKey key)
throws InterruptedException {
return driver.<GetRegisteredExecutionPlatformsValue>evaluate(
ImmutableList.of(key), EVALUATION_OPTIONS);
}
// HELPER SKYFUNCTIONS
// GetRuleByName.
private static SkyKey getRuleByNameKey(String ruleName) {
return new Key(ruleName);
}
private static final SkyFunctionName GET_RULE_BY_NAME_FUNCTION =
SkyFunctionName.createHermetic("GET_RULE_BY_NAME");
@AutoValue
abstract static class GetRuleByNameValue implements SkyValue {
abstract Rule rule();
static GetRuleByNameValue create(Rule rule) {
return new AutoValue_ExternalPackageUtilTest_GetRuleByNameValue(rule);
}
}
private static final class GetRuleByNameFunction implements SkyFunction {
@Nullable
@Override
public SkyValue compute(SkyKey skyKey, Environment env)
throws SkyFunctionException, InterruptedException {
String ruleName = (String) skyKey.argument();
Rule rule = ExternalPackageUtil.getRuleByName(ruleName, env);
if (rule == null) {
return null;
}
return GetRuleByNameValue.create(rule);
}
@Nullable
@Override
public String extractTag(SkyKey skyKey) {
return null;
}
}
// GetRegisteredToolchains.
private static SkyKey getRegisteredToolchainsKey() {
return () -> GET_REGISTERED_TOOLCHAINS_FUNCTION;
}
private static final SkyFunctionName GET_REGISTERED_TOOLCHAINS_FUNCTION =
SkyFunctionName.createHermetic("GET_REGISTERED_TOOLCHAINS");
@AutoValue
abstract static class GetRegisteredToolchainsValue implements SkyValue {
abstract ImmutableList<String> registeredToolchains();
static GetRegisteredToolchainsValue create(Iterable<String> registeredToolchains) {
return new AutoValue_ExternalPackageUtilTest_GetRegisteredToolchainsValue(
ImmutableList.copyOf(registeredToolchains));
}
}
private static final class GetRegisteredToolchainsFunction implements SkyFunction {
@Nullable
@Override
public SkyValue compute(SkyKey skyKey, Environment env)
throws SkyFunctionException, InterruptedException {
List<String> registeredToolchains = RegisteredToolchainsFunction.getRegisteredToolchains(env);
if (registeredToolchains == null) {
return null;
}
return GetRegisteredToolchainsValue.create(registeredToolchains);
}
@Nullable
@Override
public String extractTag(SkyKey skyKey) {
return null;
}
}
// GetRegisteredExecutionPlatforms.
private static SkyKey getRegisteredExecutionPlatformsKey() {
return () -> GET_REGISTERED_EXECUTION_PLATFORMS_FUNCTION;
}
private static final SkyFunctionName GET_REGISTERED_EXECUTION_PLATFORMS_FUNCTION =
SkyFunctionName.createHermetic("GET_REGISTERED_EXECUTION_PLATFORMS_FUNCTION");
@AutoValue
abstract static class GetRegisteredExecutionPlatformsValue implements SkyValue {
abstract ImmutableList<String> registeredExecutionPlatforms();
static GetRegisteredExecutionPlatformsValue create(
Iterable<String> registeredExecutionPlatforms) {
return new AutoValue_ExternalPackageUtilTest_GetRegisteredExecutionPlatformsValue(
ImmutableList.copyOf(registeredExecutionPlatforms));
}
}
private static final class GetRegisteredExecutionPlatformsFunction implements SkyFunction {
@Nullable
@Override
public SkyValue compute(SkyKey skyKey, Environment env)
throws SkyFunctionException, InterruptedException {
List<String> registeredExecutionPlatforms =
RegisteredExecutionPlatformsFunction.getWorkspaceExecutionPlatforms(env);
if (registeredExecutionPlatforms == null) {
return null;
}
return GetRegisteredExecutionPlatformsValue.create(registeredExecutionPlatforms);
}
@Nullable
@Override
public String extractTag(SkyKey skyKey) {
return null;
}
}
static class Key extends AbstractSkyKey<String> {
private Key(String arg) {
super(arg);
}
@Override
public SkyFunctionName functionName() {
return GET_RULE_BY_NAME_FUNCTION;
}
}
}