Skip to content

Commit

Permalink
Add a SkyFunction to perform toolchain resolution.
Browse files Browse the repository at this point in the history
Part of bazelbuild#2219.

Change-Id: I339009c13639144ca756eb07c520df7d430a64e3
  • Loading branch information
katre committed Jul 10, 2017
1 parent 397a10c commit 950bb3e
Show file tree
Hide file tree
Showing 5 changed files with 355 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ public final class SkyFunctions {
SkyFunctionName.create("LOCAL_REPOSITORY_LOOKUP");
public static final SkyFunctionName REGISTERED_TOOLCHAINS =
SkyFunctionName.create("REGISTERED_TOOLCHAINS");
public static final SkyFunctionName TOOLCHAIN_RESOLUTION =
SkyFunctionName.create("TOOLCHAIN_RESOLUTION");

public static Predicate<SkyKey> isSkyFunction(final SkyFunctionName functionName) {
return new Predicate<SkyKey>() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ private ImmutableMap<SkyFunctionName, SkyFunction> skyFunctions(
new ActionTemplateExpansionFunction(removeActionsAfterEvaluation));
map.put(SkyFunctions.LOCAL_REPOSITORY_LOOKUP, new LocalRepositoryLookupFunction());
map.put(SkyFunctions.REGISTERED_TOOLCHAINS, new RegisteredToolchainsFunction());
map.put(SkyFunctions.TOOLCHAIN_RESOLUTION, new ToolchainResolutionFunction());
map.putAll(extraSkyFunctions);
return map.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package com.google.devtools.build.lib.skyframe;

import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.analysis.platform.DeclaredToolchainInfo;
import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.packages.NoSuchThingException;
import com.google.devtools.build.lib.skyframe.ConfiguredTargetFunction.ConfiguredValueCreationException;
import com.google.devtools.build.lib.skyframe.RegisteredToolchainsFunction.InvalidTargetException;
import com.google.devtools.build.lib.skyframe.ToolchainResolutionValue.ToolchainResolutionKey;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunctionException;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import javax.annotation.Nullable;

/** {@link SkyFunction} which performs toolchain resolution for a class of rules. */
public class ToolchainResolutionFunction implements SkyFunction {

@Nullable
@Override
public SkyValue compute(SkyKey skyKey, Environment env)
throws SkyFunctionException, InterruptedException {

ToolchainResolutionKey key = (ToolchainResolutionKey) skyKey.argument();

// Get all toolchains.
RegisteredToolchainsValue toolchains;
try {
toolchains =
(RegisteredToolchainsValue)
env.getValueOrThrow(
RegisteredToolchainsValue.key(key.configuration()),
ConfiguredValueCreationException.class,
InvalidTargetException.class,
EvalException.class);
if (toolchains == null) {
return null;
}
} catch (ConfiguredValueCreationException e) {
throw new ToolchainResolutionFunctionException(e);
} catch (InvalidTargetException e) {
throw new ToolchainResolutionFunctionException(e);
} catch (EvalException e) {
throw new ToolchainResolutionFunctionException(e);
}

// Find the right one.
DeclaredToolchainInfo toolchain =
resolveConstraints(
key.toolchainType(),
key.targetPlatform(),
key.execPlatform(),
toolchains.registeredToolchains());
return ToolchainResolutionValue.create(toolchain.toolchainLabel());
}

// TODO(katre): Implement real resolution.
private DeclaredToolchainInfo resolveConstraints(
Label toolchainType,
PlatformInfo targetPlatform,
PlatformInfo execPlatform,
ImmutableList<DeclaredToolchainInfo> toolchains)
throws ToolchainResolutionFunctionException {
for (DeclaredToolchainInfo toolchain : toolchains) {
if (toolchain.toolchainType().equals(toolchainType)) {
return toolchain;
}
}
throw new ToolchainResolutionFunctionException(new NoToolchainFoundException(toolchainType));
}

@Nullable
@Override
public String extractTag(SkyKey skyKey) {
return null;
}

/** Used to indicate that a toolchain was not found for the current request. */
public static final class NoToolchainFoundException extends NoSuchThingException {
private final Label missingToolchainType;

public NoToolchainFoundException(Label missingToolchainType) {
super(String.format("no matching toolchain found for %s", missingToolchainType));
this.missingToolchainType = missingToolchainType;
}

public Label missingToolchainType() {
return missingToolchainType;
}
}

/** Used to indicate errors during the computation of an {@link ToolchainResolutionValue}. */
private static final class ToolchainResolutionFunctionException extends SkyFunctionException {
public ToolchainResolutionFunctionException(NoToolchainFoundException e) {
super(e, Transience.PERSISTENT);
}

public ToolchainResolutionFunctionException(ConfiguredValueCreationException e) {
super(e, Transience.PERSISTENT);
}

public ToolchainResolutionFunctionException(InvalidTargetException e) {
super(e, Transience.PERSISTENT);
}

public ToolchainResolutionFunctionException(EvalException e) {
super(e, Transience.PERSISTENT);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// 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.skyframe;

import com.google.auto.value.AutoValue;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.skyframe.SkyFunctionName;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;

/** A value which represents the selected toolchain for a specific target and execution platform. */
@AutoValue
public abstract class ToolchainResolutionValue implements SkyValue {

// A key representing the input data.
public static SkyKey key(
BuildConfiguration configuration,
Label toolchainType,
PlatformInfo targetPlatform,
PlatformInfo execPlatform) {
return ToolchainResolutionKey.create(
configuration, toolchainType, targetPlatform, execPlatform);
}

@AutoValue
public abstract static class ToolchainResolutionKey implements SkyKey {
@Override
public SkyFunctionName functionName() {
return SkyFunctions.TOOLCHAIN_RESOLUTION;
}

@Override
public ToolchainResolutionKey argument() {
return this;
}

public abstract BuildConfiguration configuration();

public abstract Label toolchainType();

public abstract PlatformInfo targetPlatform();

public abstract PlatformInfo execPlatform();

public static ToolchainResolutionKey create(
BuildConfiguration configuration,
Label toolchainType,
PlatformInfo targetPlatform,
PlatformInfo execPlatform) {
return new AutoValue_ToolchainResolutionValue_ToolchainResolutionKey(
configuration, toolchainType, targetPlatform, execPlatform);
}
}

public static ToolchainResolutionValue create(Label toolchainLabel) {
return new AutoValue_ToolchainResolutionValue(toolchainLabel);
}

public abstract Label toolchainLabel();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// 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.skyframe;

import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.skyframe.EvaluationResultSubjectFactory.assertThatEvaluationResult;

import com.google.common.collect.ImmutableList;
import com.google.common.testing.EqualsTester;
import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils;
import com.google.devtools.build.lib.skylark.util.SkylarkTestCase;
import com.google.devtools.build.skyframe.EvaluationResult;
import com.google.devtools.build.skyframe.SkyKey;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/** Tests for {@link ToolchainResolutionValue} and {@link ToolchainResolutionFunction}. */
@RunWith(JUnit4.class)
public class ToolchainResolutionFunctionTest extends SkylarkTestCase {

private Label toolchainType;

private PlatformInfo targetPlatform;
private PlatformInfo hostPlatform;

@Before
public void createPlatforms() throws Exception {
targetPlatform =
PlatformInfo.builder().setLabel(makeLabel("//platforms:target_platform")).build();
hostPlatform = PlatformInfo.builder().setLabel(makeLabel("//platforms:host_platform")).build();
}

private void rewriteWorkspace(String... lines) throws Exception {
scratch.overwriteFile(
"WORKSPACE",
new ImmutableList.Builder<String>()
.addAll(analysisMock.getWorkspaceContents(mockToolsConfig))
.addAll(ImmutableList.copyOf(lines))
.build());

invalidatePackages();
// Need to re-initialize the workspace status.
getSkyframeExecutor().injectWorkspaceStatusData("test");
}

@Before
public void createToolchains() throws Exception {
rewriteWorkspace(
"register_toolchains(",
" '//toolchain:toolchain_1',",
" '//toolchain:toolchain_2')");

scratch.file(
"constraint/BUILD",
"constraint_setting(name = 'os')",
"constraint_value(name = 'linux',",
" constraint_setting = ':os')",
"constraint_value(name = 'mac',",
" constraint_setting = ':os')");
scratch.file(
"toolchain/BUILD",
"load(':toolchain_def.bzl', 'test_toolchain')",
"toolchain_type(name = 'test_toolchain')",
"toolchain(",
" name = 'toolchain_1',",
" toolchain_type = ':test_toolchain',",
" exec_compatible_with = ['//constraint:linux'],",
" target_compatible_with = ['//constraint:mac'],",
" toolchain = ':test_toolchain_1')",
"toolchain(",
" name = 'toolchain_2',",
" toolchain_type = ':test_toolchain',",
" exec_compatible_with = ['//constraint:mac'],",
" target_compatible_with = ['//constraint:linux'],",
" toolchain = ':test_toolchain_2')",
"test_toolchain(",
" name='test_toolchain_1',",
" data = 'foo')",
"test_toolchain(",
" name='test_toolchain_2',",
" data = 'bar')");
scratch.file(
"toolchain/toolchain_def.bzl",
"def _impl(ctx):",
" toolchain = platform_common.ToolchainInfo(",
" type = Label('//toolchain:test_toolchain'),",
" data = ctx.attr.data)",
" return [toolchain]",
"test_toolchain = rule(",
" implementation = _impl,",
" attrs = {'data': attr.string()})");

invalidatePackages();
// Need to re-initialize the workspace status.
getSkyframeExecutor().injectWorkspaceStatusData("test");

toolchainType = makeLabel("//toolchain:test_toolchain");
}

private EvaluationResult<ToolchainResolutionValue> eval(SkyKey key) throws InterruptedException {
try {
getSkyframeExecutor().getSkyframeBuildView().enableAnalysis(true);
return SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), key, /*keepGoing=*/ false, reporter);
} finally {
getSkyframeExecutor().getSkyframeBuildView().enableAnalysis(false);
}
}

// TODO(katre): Current toolchain resolution does not actually check the constraints, it just
// returns the first toolchain available.
@Test
public void testResolution() throws Exception {

SkyKey key =
ToolchainResolutionValue.key(targetConfig, toolchainType, targetPlatform, hostPlatform);
EvaluationResult<ToolchainResolutionValue> result = eval(key);

assertThatEvaluationResult(result).hasNoError();

ToolchainResolutionValue toolchainResolutionValue = result.get(key);
assertThat(toolchainResolutionValue.toolchainLabel())
.isEqualTo(makeLabel("//toolchain:test_toolchain_1"));
}

@Test
public void testResolution_noneFound() throws Exception {
// Clear the toolchains.
rewriteWorkspace();

SkyKey key =
ToolchainResolutionValue.key(targetConfig, toolchainType, targetPlatform, hostPlatform);
EvaluationResult<ToolchainResolutionValue> result = eval(key);

assertThatEvaluationResult(result)
.hasErrorEntryForKeyThat(key)
.hasExceptionThat()
.hasMessageThat()
.contains("no matching toolchain found for //toolchain:test_toolchain");
}

@Test
public void testToolchainResolutionValue_equalsAndHashCode() {
new EqualsTester()
.addEqualityGroup(
ToolchainResolutionValue.create(makeLabel("//test:toolchain_impl_1")),
ToolchainResolutionValue.create(makeLabel("//test:toolchain_impl_1")))
.addEqualityGroup(
ToolchainResolutionValue.create(makeLabel("//test:toolchain_impl_2")),
ToolchainResolutionValue.create(makeLabel("//test:toolchain_impl_2")));
}
}

0 comments on commit 950bb3e

Please sign in to comment.