// 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.auto.value.AutoValue; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.analysis.BlazeDirectories; import com.google.devtools.build.lib.analysis.ToolchainContext; import com.google.devtools.build.lib.analysis.util.AnalysisMock; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.rules.platform.ToolchainTestCase; import com.google.devtools.build.lib.skyframe.ToolchainUtil.InvalidPlatformException; import com.google.devtools.build.lib.skyframe.ToolchainUtil.ToolchainContextException; import com.google.devtools.build.lib.skyframe.ToolchainUtil.UnresolvedToolchainsException; import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils; import com.google.devtools.build.skyframe.EvaluationResult; 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.Set; import javax.annotation.Nullable; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** Tests for {@link ToolchainUtil}. */ @RunWith(JUnit4.class) public class ToolchainUtilTest extends ToolchainTestCase { /** * An {@link AnalysisMock} that injects {@link CreateToolchainContextFunction} into the Skyframe * executor. */ private static final class AnalysisMockWithCreateToolchainContextFunction extends AnalysisMock.Delegate { AnalysisMockWithCreateToolchainContextFunction() { super(AnalysisMock.get()); } @Override public ImmutableMap getSkyFunctions( BlazeDirectories directories) { return ImmutableMap.builder() .putAll(super.getSkyFunctions(directories)) .put(CREATE_TOOLCHAIN_CONTEXT_FUNCTION, new CreateToolchainContextFunction()) .build(); } }; @Override protected AnalysisMock getAnalysisMock() { return new AnalysisMockWithCreateToolchainContextFunction(); } @Test public void createToolchainContext() throws Exception { // This should select platform mac, toolchain extra_toolchain_mac, because platform // mac is listed first. addToolchain( "extra", "extra_toolchain_linux", ImmutableList.of("//constraints:linux"), ImmutableList.of("//constraints:linux"), "baz"); addToolchain( "extra", "extra_toolchain_mac", ImmutableList.of("//constraints:mac"), ImmutableList.of("//constraints:linux"), "baz"); rewriteWorkspace( "register_toolchains('//extra:extra_toolchain_linux', '//extra:extra_toolchain_mac')", "register_execution_platforms('//platforms:mac', '//platforms:linux')"); useConfiguration("--platforms=//platforms:linux"); CreateToolchainContextKey key = CreateToolchainContextKey.create( "test", ImmutableSet.of(testToolchainType), targetConfigKey); EvaluationResult result = createToolchainContext(key); assertThatEvaluationResult(result).hasNoError(); ToolchainContext toolchainContext = result.get(key).toolchainContext(); assertThat(toolchainContext).isNotNull(); assertThat(toolchainContext.getRequiredToolchains()).containsExactly(testToolchainType); assertThat(toolchainContext.getResolvedToolchainLabels()) .containsExactly(Label.parseAbsoluteUnchecked("//extra:extra_toolchain_mac_impl")); assertThat(toolchainContext.getExecutionPlatform()).isNotNull(); assertThat(toolchainContext.getExecutionPlatform().label()) .isEqualTo(Label.parseAbsoluteUnchecked("//platforms:mac")); assertThat(toolchainContext.getTargetPlatform()).isNotNull(); assertThat(toolchainContext.getTargetPlatform().label()) .isEqualTo(Label.parseAbsoluteUnchecked("//platforms:linux")); } @Test public void createToolchainContext_unavailableToolchainType_single() throws Exception { useConfiguration( "--host_platform=//platforms:linux", "--platforms=//platforms:mac"); CreateToolchainContextKey key = CreateToolchainContextKey.create( "test", ImmutableSet.of( testToolchainType, Label.parseAbsoluteUnchecked("//fake/toolchain:type_1")), targetConfigKey); EvaluationResult result = createToolchainContext(key); assertThatEvaluationResult(result) .hasErrorEntryForKeyThat(key) .hasExceptionThat() .isInstanceOf(ToolchainContextException.class); assertThatEvaluationResult(result) .hasErrorEntryForKeyThat(key) .hasExceptionThat() .hasCauseThat() .isInstanceOf(UnresolvedToolchainsException.class); assertThatEvaluationResult(result) .hasErrorEntryForKeyThat(key) .hasExceptionThat() .hasCauseThat() .hasMessageThat() .contains("no matching toolchains found for types //fake/toolchain:type_1"); } @Test public void createToolchainContext_unavailableToolchainType_multiple() throws Exception { useConfiguration( "--host_platform=//platforms:linux", "--platforms=//platforms:mac"); CreateToolchainContextKey key = CreateToolchainContextKey.create( "test", ImmutableSet.of( testToolchainType, Label.parseAbsoluteUnchecked("//fake/toolchain:type_1"), Label.parseAbsoluteUnchecked("//fake/toolchain:type_2")), targetConfigKey); EvaluationResult result = createToolchainContext(key); assertThatEvaluationResult(result) .hasErrorEntryForKeyThat(key) .hasExceptionThat() .isInstanceOf(ToolchainContextException.class); assertThatEvaluationResult(result) .hasErrorEntryForKeyThat(key) .hasExceptionThat() .hasCauseThat() .isInstanceOf(UnresolvedToolchainsException.class); // Only one of the missing types will be reported, so do not check the specific error message. } @Test public void createToolchainContext_invalidTargetPlatform() throws Exception { scratch.file("invalid/BUILD", "filegroup(name = 'not_a_platform')"); useConfiguration("--platforms=//invalid:not_a_platform"); CreateToolchainContextKey key = CreateToolchainContextKey.create( "test", ImmutableSet.of(testToolchainType), targetConfigKey); EvaluationResult result = createToolchainContext(key); assertThatEvaluationResult(result).hasError(); assertThatEvaluationResult(result) .hasErrorEntryForKeyThat(key) .hasExceptionThat() .isInstanceOf(ToolchainContextException.class); assertThatEvaluationResult(result) .hasErrorEntryForKeyThat(key) .hasExceptionThat() .hasCauseThat() .isInstanceOf(InvalidPlatformException.class); assertThatEvaluationResult(result) .hasErrorEntryForKeyThat(key) .hasExceptionThat() .hasCauseThat() .hasMessageThat() .contains("//invalid:not_a_platform"); } @Test public void createToolchainContext_invalidHostPlatform() throws Exception { scratch.file("invalid/BUILD", "filegroup(name = 'not_a_platform')"); useConfiguration("--host_platform=//invalid:not_a_platform"); CreateToolchainContextKey key = CreateToolchainContextKey.create( "test", ImmutableSet.of(testToolchainType), targetConfigKey); EvaluationResult result = createToolchainContext(key); assertThatEvaluationResult(result).hasError(); assertThatEvaluationResult(result) .hasErrorEntryForKeyThat(key) .hasExceptionThat() .isInstanceOf(ToolchainContextException.class); assertThatEvaluationResult(result) .hasErrorEntryForKeyThat(key) .hasExceptionThat() .hasCauseThat() .isInstanceOf(InvalidPlatformException.class); assertThatEvaluationResult(result) .hasErrorEntryForKeyThat(key) .hasExceptionThat() .hasCauseThat() .hasMessageThat() .contains("//invalid:not_a_platform"); } @Test public void createToolchainContext_invalidExecutionPlatform() throws Exception { scratch.file("invalid/BUILD", "filegroup(name = 'not_a_platform')"); useConfiguration("--extra_execution_platforms=//invalid:not_a_platform"); CreateToolchainContextKey key = CreateToolchainContextKey.create( "test", ImmutableSet.of(testToolchainType), targetConfigKey); EvaluationResult result = createToolchainContext(key); assertThatEvaluationResult(result).hasError(); assertThatEvaluationResult(result) .hasErrorEntryForKeyThat(key) .hasExceptionThat() .isInstanceOf(ToolchainContextException.class); assertThatEvaluationResult(result) .hasErrorEntryForKeyThat(key) .hasExceptionThat() .hasCauseThat() .isInstanceOf(InvalidPlatformException.class); assertThatEvaluationResult(result) .hasErrorEntryForKeyThat(key) .hasExceptionThat() .hasCauseThat() .hasMessageThat() .contains("//invalid:not_a_platform"); } // Calls ToolchainUtil.createToolchainContext. private static final SkyFunctionName CREATE_TOOLCHAIN_CONTEXT_FUNCTION = SkyFunctionName.create("CREATE_TOOLCHAIN_CONTEXT_FUNCTION"); @AutoValue abstract static class CreateToolchainContextKey implements SkyKey { @Override public SkyFunctionName functionName() { return CREATE_TOOLCHAIN_CONTEXT_FUNCTION; } abstract String targetDescription(); abstract Set