blob: 4883a0c8825378e8304aaefa659a7c4573d75623 [file] [log] [blame]
// Copyright 2019 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.rules.python;
import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.lib.syntax.Starlark.NONE;
import com.google.devtools.build.lib.analysis.platform.ToolchainInfo;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.testutil.TestConstants;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Tests for the standard Python toolchain definitions.
*
* <p>This covers invariants of the toolchain definitions, and the interaction with a user-defined
* consuming rule, but not the behavior of {@code py_binary} and {@code py_test}. Those tests are
* under {@link PyExecutableConfiguredTargetTestBase} instead.
*/
@RunWith(JUnit4.class)
public class PythonToolchainTest extends BuildViewTestCase {
private static final String TOOLCHAIN_BZL =
TestConstants.TOOLS_REPOSITORY + "//tools/python:toolchain.bzl";
private static final String TOOLCHAIN_TYPE =
TestConstants.TOOLS_REPOSITORY + "//tools/python:toolchain_type";
private static final String PY2_PATH_CONSTRAINT =
TestConstants.TOOLS_REPOSITORY + "//tools/python:py2_interpreter_path";
private static final String PY3_PATH_CONSTRAINT =
TestConstants.TOOLS_REPOSITORY + "//tools/python:py3_interpreter_path";
@Test
public void userDefinedConsumerUsingToolchainResolution() throws Exception {
// A simple platform with unique constraint values.
scratch.file(
"platforms/BUILD",
"constraint_value(",
" name = 'my_py2_path',",
" constraint_setting = '" + PY2_PATH_CONSTRAINT + "',",
")",
"constraint_value(",
" name = 'my_py3_path',",
" constraint_setting = '" + PY3_PATH_CONSTRAINT + "',",
")",
"platform(",
" name = 'my_platform',",
" constraint_values = [':my_py2_path', ':my_py3_path'],",
")");
// A user rule that requires the Python toolchain type and spits out the resulting info.
scratch.file(
"pkg/rules.bzl",
"def _myrule_impl(ctx):",
" info = ctx.toolchains['" + TOOLCHAIN_TYPE + "']",
" print('PY2 path: ' + info.py2_runtime.interpreter_path)",
" print('PY3 path: ' + info.py3_runtime.interpreter_path)",
"myrule = rule(",
" implementation = _myrule_impl,",
" toolchains = ['" + TOOLCHAIN_TYPE + "'],",
")");
// A toolchain implementation and an instance of the rule that will use it.
scratch.file(
"pkg/BUILD",
"load('" + TOOLCHAIN_BZL + "', 'py_runtime_pair')",
"load(':rules.bzl', 'myrule')",
"py_runtime(",
" name = 'my_py2_runtime',",
" interpreter_path = '/system/python2',",
" python_version = 'PY2',",
")",
"py_runtime(",
" name = 'my_py3_runtime',",
" interpreter_path = '/system/python3',",
" python_version = 'PY3',",
")",
"py_runtime_pair(",
" name = 'my_py_runtime_pair',",
" py2_runtime = ':my_py2_runtime',",
" py3_runtime = ':my_py3_runtime',",
")",
"toolchain(",
" name = 'my_toolchain',",
" target_compatible_with = ['//platforms:my_py2_path', '//platforms:my_py3_path'],",
" toolchain = ':my_py_runtime_pair',",
" toolchain_type = '" + TOOLCHAIN_TYPE + "',",
")",
"myrule(",
" name = 'mytarget',",
")");
// Register the toolchain and ask for the platform.
useConfiguration(
"--platforms=//platforms:my_platform", "--extra_toolchains=//pkg:my_toolchain");
getConfiguredTarget("//pkg:mytarget");
assertContainsEvent("PY2 path: /system/python2");
assertContainsEvent("PY3 path: /system/python3");
}
@Test
public void okToOmitRuntimes() throws Exception {
scratch.file(
"pkg/BUILD",
"load('" + TOOLCHAIN_BZL + "', 'py_runtime_pair')",
"py_runtime_pair(",
" name = 'my_py_runtime_pair',",
")");
ToolchainInfo info =
getConfiguredTarget("//pkg:my_py_runtime_pair").get(ToolchainInfo.PROVIDER);
assertThat(info.getValue("py2_runtime")).isEqualTo(NONE);
assertThat(info.getValue("py3_runtime")).isEqualTo(NONE);
}
@Test
public void wrongVersionInToolchainAttribute() throws Exception {
reporter.removeHandler(failFastHandler);
scratch.file(
"pkg/BUILD",
"load('" + TOOLCHAIN_BZL + "', 'py_runtime_pair')",
"py_runtime(",
" name = 'bad_py2_runtime',",
" interpreter_path = '/system/python2',",
" python_version = 'PY3',",
")",
"py_runtime(",
" name = 'bad_py3_runtime',",
" interpreter_path = '/system/python3',",
" python_version = 'PY2',",
")",
"py_runtime_pair(",
" name = 'with_bad_py2_runtime',",
" py2_runtime = ':bad_py2_runtime',",
")",
"py_runtime_pair(",
" name = 'with_bad_py3_runtime',",
" py3_runtime = ':bad_py3_runtime',",
")");
getConfiguredTarget("//pkg:with_bad_py2_runtime");
getConfiguredTarget("//pkg:with_bad_py3_runtime");
assertContainsEvent(
"The Python runtime in the 'py2_runtime' attribute did not have version 'PY2'");
assertContainsEvent(
"The Python runtime in the 'py3_runtime' attribute did not have version 'PY3'");
}
@Test
public void missingProviderInToolchainAttribute() throws Exception {
reporter.removeHandler(failFastHandler);
scratch.file(
"pkg/BUILD",
"load('" + TOOLCHAIN_BZL + "', 'py_runtime_pair')",
"sh_binary(",
" name = 'not_a_runtime',",
" srcs = ['not_a_runtime.sh'],",
")",
"py_runtime_pair(",
" name = 'bad_py_runtime_pair',",
" py2_runtime = ':not_a_runtime',",
")");
getConfiguredTarget("//pkg:bad_py_runtime_pair");
assertContainsEvent("'//pkg:not_a_runtime' does not have mandatory providers: 'PyRuntimeInfo'");
}
}