diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java index 76193e976f695b..e0762b33bb4fea 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java @@ -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 isSkyFunction(final SkyFunctionName functionName) { return new Predicate() { diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java index 1dfe85f30c337a..463e3f3d5fe929 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java @@ -456,6 +456,7 @@ private ImmutableMap 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(); } diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainResolutionFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainResolutionFunction.java new file mode 100644 index 00000000000000..7a61c672716376 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainResolutionFunction.java @@ -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 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); + } + } +} diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainResolutionValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainResolutionValue.java new file mode 100644 index 00000000000000..1f553e63508d58 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/skyframe/ToolchainResolutionValue.java @@ -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(); +} diff --git a/src/test/java/com/google/devtools/build/lib/rules/platform/ToolchainTestCase.java b/src/test/java/com/google/devtools/build/lib/rules/platform/ToolchainTestCase.java index 87bce409db6d79..5d170baac09aa6 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/platform/ToolchainTestCase.java +++ b/src/test/java/com/google/devtools/build/lib/rules/platform/ToolchainTestCase.java @@ -16,6 +16,7 @@ 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; @@ -23,11 +24,15 @@ /** 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( @@ -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", diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/ToolchainResolutionFunctionTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/ToolchainResolutionFunctionTest.java new file mode 100644 index 00000000000000..88bc501d1271e3 --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/skyframe/ToolchainResolutionFunctionTest.java @@ -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 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 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 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"))); + } +}