aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/main/java/com/google/devtools/build/lib/BUILD2
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPythonSemantics.java23
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/python/python_stub_template.txt (renamed from src/main/java/com/google/devtools/build/lib/bazel/rules/python/stub_template.txt)0
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/python/python_stub_template_windows.txt23
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/python/PyBinary.java8
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java20
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/python/PythonSemantics.java2
-rw-r--r--src/test/py/bazel/launcher_script_test.py87
8 files changed, 154 insertions, 11 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index cd5e566d61..b920246a68 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -737,7 +737,7 @@ java_library(
resources = [
"bazel/rules/java/java_stub_template.txt",
"bazel/rules/java/java_stub_template_windows.txt",
- "bazel/rules/python/stub_template.txt",
+ "bazel/rules/python/python_stub_template.txt",
"bazel/rules/sh/sh_stub_template_windows.txt",
],
deps = [
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPythonSemantics.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPythonSemantics.java
index 2e3ef240cd..1202cc575f 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPythonSemantics.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPythonSemantics.java
@@ -41,6 +41,7 @@ import com.google.devtools.build.lib.rules.python.PythonSemantics;
import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector.InstrumentationSpec;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.util.FileTypeSet;
+import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Collection;
@@ -51,7 +52,9 @@ import java.util.List;
*/
public class BazelPythonSemantics implements PythonSemantics {
private static final Template STUB_TEMPLATE =
- Template.forResource(BazelPythonSemantics.class, "stub_template.txt");
+ Template.forResource(BazelPythonSemantics.class, "python_stub_template.txt");
+ private static final Template STUB_TEMPLATE_WINDOWS =
+ Template.forResource(BazelPythonSemantics.class, "python_stub_template_windows.txt");
public static final InstrumentationSpec PYTHON_COLLECTION_SPEC = new InstrumentationSpec(
FileTypeSet.of(BazelPyRuleClasses.PYTHON_SOURCE),
"srcs", "deps", "data");
@@ -123,7 +126,7 @@ public class BazelPythonSemantics implements PythonSemantics {
}
@Override
- public void createExecutable(
+ public Artifact createExecutable(
RuleContext ruleContext,
PyCommon common,
CcLinkParamsStore ccLinkParamsStore,
@@ -180,7 +183,21 @@ public class BazelPythonSemantics implements PythonSemantics {
.useDefaultShellEnvironment()
.setMnemonic("BuildBinary")
.build(ruleContext));
+
+ if (OS.getCurrent() == OS.WINDOWS) {
+ Artifact executableWrapper = common.getExecutableWrapper();
+ ruleContext.registerAction(
+ new TemplateExpansionAction(
+ ruleContext.getActionOwner(),
+ executableWrapper,
+ STUB_TEMPLATE_WINDOWS,
+ ImmutableList.of(Substitution.of("%python_path%", pythonBinary)),
+ true));
+ return executableWrapper;
+ }
}
+
+ return executable;
}
@Override
@@ -216,7 +233,7 @@ public class BazelPythonSemantics implements PythonSemantics {
}
// We put the whole runfiles tree under the ZIP_RUNFILES_DIRECTORY_NAME directory, by doing this
// , we avoid the conflict between default workspace name "__main__" and __main__.py file.
- // Note: This name has to be the same with the one in stub_template.txt.
+ // Note: This name has to be the same with the one in python_stub_template.txt.
return ZIP_RUNFILES_DIRECTORY_NAME.getRelative(zipRunfilesPath).toString();
}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/stub_template.txt b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/python_stub_template.txt
index 218f6aeae2..218f6aeae2 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/stub_template.txt
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/python_stub_template.txt
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/python/python_stub_template_windows.txt b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/python_stub_template_windows.txt
new file mode 100644
index 0000000000..13ce2f52a9
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/python/python_stub_template_windows.txt
@@ -0,0 +1,23 @@
+@rem Copyright 2017 The Bazel Authors. All rights reserved.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem http://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+@rem This script was generated from python_stub_template_windows.txt. Please
+@rem don't edit it directly.
+
+@SETLOCAL ENABLEEXTENSIONS
+
+@rem launcher=${$0%.cmd}
+@set launcher=%~dp0%~n0
+
+@call %python_path% %launcher% %*
diff --git a/src/main/java/com/google/devtools/build/lib/rules/python/PyBinary.java b/src/main/java/com/google/devtools/build/lib/rules/python/PyBinary.java
index fd659781cf..181cb8511e 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/python/PyBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/python/PyBinary.java
@@ -77,7 +77,8 @@ public abstract class PyBinary implements RuleConfiguredTargetFactory {
return null;
}
- semantics.createExecutable(ruleContext, common, ccLinkParamsStore, imports);
+ Artifact realExecutable =
+ semantics.createExecutable(ruleContext, common, ccLinkParamsStore, imports);
Runfiles commonRunfiles = collectCommonRunfiles(ruleContext, common, semantics);
Runfiles.Builder defaultRunfilesBuilder = new Runfiles.Builder(
@@ -117,7 +118,7 @@ public abstract class PyBinary implements RuleConfiguredTargetFactory {
return builder
.setFilesToBuild(common.getFilesToBuild())
.add(RunfilesProvider.class, runfilesProvider)
- .setRunfilesSupport(runfilesSupport, common.getExecutable())
+ .setRunfilesSupport(runfilesSupport, realExecutable)
.addNativeDeclaredProvider(new CcLinkParamsProvider(ccLinkParamsStore))
.add(PythonImportsProvider.class, new PythonImportsProvider(imports));
}
@@ -127,6 +128,9 @@ public abstract class PyBinary implements RuleConfiguredTargetFactory {
Runfiles.Builder builder = new Runfiles.Builder(
ruleContext.getWorkspaceName(), ruleContext.getConfiguration().legacyExternalRunfiles());
builder.addArtifact(common.getExecutable());
+ if (common.getExecutableWrapper() != null) {
+ builder.addArtifact(common.getExecutableWrapper());
+ }
if (common.getConvertedFiles() != null) {
builder.addSymlinks(common.getConvertedFiles());
} else {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java b/src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java
index 5ffa4dc610..de9d22e329 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/python/PyCommon.java
@@ -51,6 +51,7 @@ import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
import com.google.devtools.build.lib.syntax.SkylarkType;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.util.FileType;
+import com.google.devtools.build.lib.util.OS;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.protobuf.GeneratedMessage.GeneratedExtension;
@@ -80,6 +81,7 @@ public final class PyCommon {
private final RuleContext ruleContext;
private Artifact executable = null;
+ private Artifact executableWrapper = null;
private NestedSet<Artifact> transitivePythonSources;
@@ -114,15 +116,21 @@ public final class PyCommon {
validatePackageName();
executable = ruleContext.createOutputArtifact();
+ if (OS.getCurrent() == OS.WINDOWS) {
+ executableWrapper =
+ ruleContext.getImplicitOutputArtifact(ruleContext.getTarget().getName() + ".cmd");
+ }
if (this.version == PythonVersion.PY2AND3) {
// TODO(bazel-team): we need to create two actions
ruleContext.ruleError("PY2AND3 is not yet implemented");
}
- filesToBuild = NestedSetBuilder.<Artifact>stableOrder()
- .addAll(srcs)
- .add(executable)
- .build();
+ NestedSetBuilder<Artifact> filesToBuildBuilder =
+ NestedSetBuilder.<Artifact>stableOrder().addAll(srcs).add(executable);
+ if (executableWrapper != null) {
+ filesToBuildBuilder.add(executableWrapper);
+ }
+ filesToBuild = filesToBuildBuilder.build();
if (ruleContext.hasErrors()) {
return;
@@ -447,6 +455,10 @@ public final class PyCommon {
return executable;
}
+ public Artifact getExecutableWrapper() {
+ return executableWrapper;
+ }
+
public Map<PathFragment, Artifact> getConvertedFiles() {
return convertedFiles;
}
diff --git a/src/main/java/com/google/devtools/build/lib/rules/python/PythonSemantics.java b/src/main/java/com/google/devtools/build/lib/rules/python/PythonSemantics.java
index bbaaa8f423..a87ef4e049 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/python/PythonSemantics.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/python/PythonSemantics.java
@@ -69,7 +69,7 @@ public interface PythonSemantics {
*
* <p>This should create a generating action for {@code common.getExecutable()}.
*/
- void createExecutable(
+ Artifact createExecutable(
RuleContext ruleContext,
PyCommon common,
CcLinkParamsStore ccLinkParamsStore,
diff --git a/src/test/py/bazel/launcher_script_test.py b/src/test/py/bazel/launcher_script_test.py
index 38bc557f91..3a14cf3375 100644
--- a/src/test/py/bazel/launcher_script_test.py
+++ b/src/test/py/bazel/launcher_script_test.py
@@ -178,6 +178,93 @@ class LauncherScriptTest(test_base.TestBase):
self.AssertExitCode(exit_code, 0, stderr)
self.assertEqual(stdout[0], 'hello batch')
+ def testPyBinaryLauncher(self):
+ self.ScratchFile('WORKSPACE')
+ self.ScratchFile('foo/foo.bzl', [
+ 'def _impl(ctx):',
+ ' ctx.action(',
+ ' arguments=[ctx.outputs.out.path],',
+ ' outputs=[ctx.outputs.out],',
+ ' executable=ctx.executable._hello_world,',
+ ' use_default_shell_env=True)',
+ '',
+ 'helloworld = rule(',
+ ' implementation=_impl,',
+ ' attrs={',
+ ' "srcs": attr.label_list(allow_files=True),',
+ ' "out": attr.output(mandatory=True),',
+ ' "_hello_world": attr.label(executable=True, cfg="host",',
+ ' allow_files=True,',
+ ' default=Label("//foo:foo"))',
+ ' }',
+ ')',
+ ])
+ self.ScratchFile('foo/BUILD', [
+ 'load(":foo.bzl", "helloworld")',
+ '',
+ 'py_binary(',
+ ' name = "foo",',
+ ' srcs = ["foo.py"],',
+ ' data = ["//bar:bar.txt"],',
+ ')',
+ '',
+ 'helloworld(',
+ ' name = "hello",',
+ ' out = "hello.txt",',
+ ')'
+ ])
+ foo_py = self.ScratchFile('foo/foo.py', [
+ '#!/usr/bin/env python',
+ 'import sys',
+ 'if len(sys.argv) == 2:',
+ ' with open(sys.argv[1], "w") as f:',
+ ' f.write("Hello World!")',
+ 'else:',
+ ' print "Hello World!"',
+ ])
+ self.ScratchFile('bar/BUILD', ['exports_files(["bar.txt"])'])
+ self.ScratchFile('bar/bar.txt', ['hello'])
+ os.chmod(foo_py, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
+
+ exit_code, stdout, stderr = self.RunBazel(['info', 'bazel-bin'])
+ self.AssertExitCode(exit_code, 0, stderr)
+ bazel_bin = stdout[0]
+
+ # Verify that the build of our py_binary succeeds.
+ exit_code, _, stderr = self.RunBazel(['build', '//foo:foo'])
+ self.AssertExitCode(exit_code, 0, stderr)
+
+ # Verify that generated files exist.
+ foo_bin = os.path.join(bazel_bin, 'foo', 'foo.cmd'
+ if self.IsWindows() else 'foo')
+ self.assertTrue(os.path.isfile(foo_bin))
+ self.assertTrue(os.path.isdir(os.path.join(bazel_bin, 'foo/foo.runfiles')))
+
+ # Verify contents of runfiles (manifest).
+ if self.IsWindows():
+ self.AssertRunfilesManifestContains(
+ os.path.join(bazel_bin, 'foo/foo.runfiles/MANIFEST'),
+ '__main__/bar/bar.txt')
+ else:
+ self.assertTrue(
+ os.path.islink(
+ os.path.join(bazel_bin, 'foo/foo.runfiles/__main__/bar/bar.txt')))
+
+ # Try to run the built py_binary.
+ exit_code, stdout, stderr = self.RunProgram([foo_bin])
+ self.AssertExitCode(exit_code, 0, stderr)
+ self.assertEqual(stdout[0], 'Hello World!')
+
+ # Try to use the py_binary as an executable in a Skylark rule.
+ exit_code, stdout, stderr = self.RunBazel(['build', '//foo:hello'])
+ self.AssertExitCode(exit_code, 0, stderr)
+
+ # Verify that the Skylark action generated the right output.
+ hello_path = os.path.join(bazel_bin, 'foo', 'hello.txt')
+ self.assertTrue(os.path.isfile(hello_path))
+ with open(hello_path, 'r') as f:
+ self.assertEqual(f.read(), 'Hello World!')
+
def AssertRunfilesManifestContains(self, manifest, entry):
with open(manifest, 'r') as f:
for l in f: