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 #2219.

Change-Id: I339009c13639144ca756eb07c520df7d430a64e3
PiperOrigin-RevId: 161826487
  • Loading branch information
katre authored and laszlocsomor committed Jul 14, 2017
1 parent 8002b62 commit 98375a2
Show file tree
Hide file tree
Showing 6 changed files with 302 additions and 3 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,125 @@
// 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.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,75 @@
// 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);
}

/** {@link SkyKey} implementation used for {@link ToolchainResolutionFunction}. */
@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
Expand Up @@ -16,18 +16,23 @@

import com.google.devtools.build.lib.analysis.platform.ConstraintSettingInfo;
import com.google.devtools.build.lib.analysis.platform.ConstraintValueInfo;
import com.google.devtools.build.lib.analysis.platform.PlatformInfo;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.skylark.util.SkylarkTestCase;
import org.junit.Before;

/** Utility methods for setting up platform and toolchain related tests. */
public abstract class ToolchainTestCase extends SkylarkTestCase {

public Label testToolchainType;
public PlatformInfo targetPlatform;
public PlatformInfo hostPlatform;

public ConstraintSettingInfo setting;
public ConstraintValueInfo linuxConstraint;
public ConstraintValueInfo macConstraint;

public Label testToolchainType;

@Before
public void createConstraints() throws Exception {
scratch.file(
Expand All @@ -43,10 +48,16 @@ public void createConstraints() throws Exception {
macConstraint = ConstraintValueInfo.create(setting, makeLabel("//constraint:mac"));
}

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

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

scratch.file(
"toolchain/BUILD",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// 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.testing.EqualsTester;
import com.google.devtools.build.lib.rules.platform.ToolchainTestCase;
import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils;
import com.google.devtools.build.skyframe.EvaluationResult;
import com.google.devtools.build.skyframe.SkyKey;
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 ToolchainTestCase {

private EvaluationResult<ToolchainResolutionValue> invokeToolchainResolution(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, testToolchainType, targetPlatform, hostPlatform);
EvaluationResult<ToolchainResolutionValue> result = invokeToolchainResolution(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, testToolchainType, targetPlatform, hostPlatform);
EvaluationResult<ToolchainResolutionValue> result = invokeToolchainResolution(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 98375a2

Please sign in to comment.