blob: 313d2bbfa8a4c69b37d7641bf7fbabeafd3621c0 [file] [log] [blame]
// Copyright 2018 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 com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.configuredtargets.FileConfiguredTarget;
import com.google.devtools.build.lib.testutil.TestConstants;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for {@code py_binary}. */
@RunWith(JUnit4.class)
public class PyBinaryConfiguredTargetTest extends PyExecutableConfiguredTargetTestBase {
public PyBinaryConfiguredTargetTest() {
super("py_binary");
}
@Test
public void python2WithPy3Dependency() throws Exception {
eventCollector.clear();
reporter.removeHandler(failFastHandler); // expect errors
scratch.file("python2/BUILD",
"py_library(name = 'py3lib',",
" srcs = [],",
" srcs_version = 'PY3')",
"py_binary(name = 'pybin',",
" srcs = ['pybin.py'],",
" deps = [':py3lib'],",
" srcs_version = 'PY2',",
" default_python_version = 'PY2')");
assertThat(view.hasErrors(getConfiguredTarget("//python2:pybin"))).isTrue();
assertContainsEvent("//python2:py3lib: Rule '//python2:py3lib' can only be used with Python 3");
}
@Test
public void python2WithPy3OnlyDependency() throws Exception {
eventCollector.clear();
reporter.removeHandler(failFastHandler); // expect errors
scratch.file("python2/BUILD",
"py_library(",
" name = 'py3lib',",
" srcs = [],",
" srcs_version = 'PY3ONLY')",
"py_binary(",
" name = 'pybin',",
" srcs = ['pybin.py'],",
" deps = [':py3lib'],",
" srcs_version = 'PY2',",
" default_python_version = 'PY2')");
assertThat(view.hasErrors(getConfiguredTarget("//python2:pybin"))).isTrue();
assertContainsEvent("//python2:py3lib: Rule '//python2:py3lib' can only be used with Python 3");
}
@Test
public void python3WithPy2OnlyDependency() throws Exception {
eventCollector.clear();
reporter.removeHandler(failFastHandler); // expect errors
scratch.file("python3/BUILD",
"py_library(",
" name = 'py2lib',",
" srcs = [],",
" srcs_version = 'PY2ONLY')",
"py_binary(",
" name = 'pybin',",
" srcs = ['pybin.py'],",
" deps = [':py2lib'],",
" srcs_version = 'PY3',",
" default_python_version = 'PY3')");
assertThat(view.hasErrors(getConfiguredTarget("//python3:pybin"))).isTrue();
assertContainsEvent("//python3:py2lib: Rule '//python3:py2lib' can only be used with Python 2");
}
@Test
public void filesToBuild() throws Exception {
scratch.file("pkg/BUILD",
"py_binary(",
" name = 'foo',",
" srcs = ['foo.py'])");
ConfiguredTarget target = getConfiguredTarget("//pkg:foo");
FileConfiguredTarget srcFile = getFileConfiguredTarget("//pkg:foo.py");
assertThat(getFilesToBuild(target))
.containsExactly(getExecutable(target), srcFile.getArtifact());
assertThat(getExecutable(target).getExecPath().getPathString())
.containsMatch(TestConstants.PRODUCT_NAME + "-out/.*/bin/pkg/foo");
}
@Test
public void srcsIsMandatory() throws Exception {
// This case is somewhat dominated by the test that the default main must be in srcs, but the
// error message is different here.
checkError("pkg", "foo",
// error:
"missing value for mandatory attribute 'srcs'",
// build file:
"py_binary(",
" name = 'foo',",
" deps = [':bar'])",
"py_library(",
" name = 'bar',",
" srcs = ['bar.py'])");
}
@Test
public void defaultMainMustBeInSrcs() throws Exception {
checkError("pkg", "app",
// error:
"corresponding default 'app.py' does not appear",
// build file:
"py_binary(",
" name = 'app',",
" srcs = ['foo.py', 'bar.py'])");
}
@Test
public void explicitMain() throws Exception {
scratch.file("pkg/BUILD",
"py_binary(",
" name = 'foo',",
" main = 'foo.py',",
" srcs = ['foo.py', 'bar.py'])");
getConfiguredTarget("//pkg:foo"); // should not fail
}
@Test
public void explicitMainMustBeInSrcs() throws Exception {
checkError("pkg", "foo",
// error:
"could not find 'foo.py'",
// build file:
"py_binary(",
" name = 'foo',",
" main = 'foo.py',",
" srcs = ['bar.py', 'baz.py'])");
}
@Test
public void defaultMainCannotBeAmbiguous() throws Exception {
scratch.file("pkg1/BUILD",
"py_binary(",
" name = 'foo',",
" srcs = ['bar.py'])");
checkError("pkg2", "bar",
// error:
"default main file name 'bar.py' matches multiple files. Perhaps specify an explicit file "
+ "with 'main' attribute? Matches were: 'pkg2/bar.py' and 'pkg1/bar.py'",
// build file:
"py_binary(",
" name = 'bar',",
" srcs = ['bar.py', '//pkg1:bar.py'])");
}
@Test
public void explicitMainCannotBeAmbiguous() throws Exception {
scratch.file("pkg1/BUILD",
"py_binary(",
" name = 'foo',",
" srcs = ['bar.py'])");
checkError("pkg2", "foo",
// error:
"file name 'bar.py' specified by 'main' attribute matches multiple files: e.g., "
+ "'pkg2/bar.py' and 'pkg1/bar.py'",
// build file:
"py_binary(",
" name = 'foo',",
" main = 'bar.py',",
" srcs = ['bar.py', '//pkg1:bar.py'])");
}
@Test
public void nameCannotEndInPy() throws Exception {
checkError("pkg", "foo.py",
// error:
"name must not end in '.py'",
// build file:
"py_binary(",
" name = 'foo.py',",
" srcs = ['bar.py'])");
}
@Test
public void defaultMainCanBeGenerated() throws Exception {
scratch.file("pkg/BUILD",
"genrule(",
" name = 'gen_py',",
" cmd = 'touch $(location foo.py)',",
" outs = ['foo.py'])",
"py_binary(",
" name = 'foo',",
" srcs = [':gen_py'])");
getConfiguredTarget("//pkg:foo"); // should not fail
}
@Test
public void defaultMainCanHaveMultiplePathSegments() throws Exception {
// Regression test for crash caused by use of getChild on a multi-segment rule name.
scratch.file("pkg/BUILD",
"py_binary(",
" name = 'foo/bar',",
" srcs = ['foo/bar.py'])");
getConfiguredTarget("//pkg:foo/bar"); // should not fail
}
// TODO(brandjon): Add tests for content of stub Python script (particularly for choosing python
// 2 or 3).
}