diff options
Diffstat (limited to 'src/main/java/com/google')
3 files changed, 73 insertions, 44 deletions
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 3ea19fe034..3d95040236 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,7 +41,6 @@ import com.google.devtools.build.lib.rules.test.InstrumentedFilesCollector.Instr import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.FileTypeSet; import com.google.devtools.build.lib.vfs.PathFragment; -import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -105,6 +104,16 @@ public class BazelPythonSemantics implements PythonSemantics { return result; } + /** @return An artifact next to the executable file with ".zip" suffix */ + public Artifact getPythonZipArtifact(RuleContext ruleContext, Artifact executable) { + return ruleContext.getRelatedArtifact(executable.getRootRelativePath(), ".zip"); + } + + /** @return An artifact next to the executable file with ".temp" suffix */ + public Artifact getPythonTemplateMainArtifact(RuleContext ruleContext, Artifact executable) { + return ruleContext.getRelatedArtifact(executable.getRootRelativePath(), ".temp"); + } + @Override public void createExecutable( RuleContext ruleContext, @@ -133,46 +142,45 @@ public class BazelPythonSemantics implements PythonSemantics { Substitution.of("%main%", main), Substitution.of("%python_binary%", pythonBinary), Substitution.of("%imports%", Joiner.on(":").join(imports)), - Substitution.of("%workspace_name%", ruleContext.getWorkspaceName())), + Substitution.of("%workspace_name%", ruleContext.getWorkspaceName()), + Substitution.of("%is_zipfile%", "False")), true)); } else { - Artifact zipFile = common.getPythonZipArtifact(); + Artifact zipFile = getPythonZipArtifact(ruleContext, executable); + Artifact templateMain = getPythonTemplateMainArtifact(ruleContext, executable); PathFragment workspaceName = getWorkspaceNameForPythonZip(ruleContext.getWorkspaceName()); main = workspaceName.getRelative(common.determineMainExecutableSource(false)).toString(); PathFragment defaultWorkspacename = new PathFragment(Label.DEFAULT_REPOSITORY_DIRECTORY); - StringBuilder importPaths = new StringBuilder(); - importPaths.append(File.pathSeparator).append("$PYTHON_RUNFILES/").append(workspaceName); + List<PathFragment> importPaths = new ArrayList<>(); for (PathFragment path : imports) { if (path.startsWith(defaultWorkspacename)) { path = new PathFragment(workspaceName, path.subFragment(1, path.segmentCount())); } - importPaths.append(File.pathSeparator).append("$PYTHON_RUNFILES/").append(path); + importPaths.add(path); } // The executable zip file wil unzip itself into a tmp directory and then run from there - String zipHeader = - "#!/bin/sh\n" - + "export TMPDIR=${TMPDIR:-/tmp/Bazel}\n" - + "mkdir -p \"${TMPDIR}\"\n" - + "export PYTHON_RUNFILES=$(mktemp -d \"${TMPDIR%%/}/runfiles.XXXXXXXX\")\n" - + "export PYTHONPATH=\"$PYTHONPATH" - + importPaths - + "\"\n" - + "unzip -q -o $0 -d \"$PYTHON_RUNFILES\" 2> /dev/null\n" - + "retCode=0\n" - + pythonBinary - + " \"$PYTHON_RUNFILES/" - + main - + "\" $@ || retCode=$?\n" - + "rm -rf \"$PYTHON_RUNFILES\"\n" - + "exit $retCode\n"; + ruleContext.registerAction( + new TemplateExpansionAction( + ruleContext.getActionOwner(), + templateMain, + STUB_TEMPLATE, + ImmutableList.of( + Substitution.of("%main%", main), + Substitution.of("%python_binary%", pythonBinary), + Substitution.of("%imports%", Joiner.on(":").join(importPaths)), + Substitution.of("%workspace_name%", ruleContext.getWorkspaceName()), + Substitution.of("%is_zipfile%", "True")), + true)); + ruleContext.registerAction( new SpawnAction.Builder() .addInput(zipFile) + .addInput(templateMain) .addOutput(executable) .setShellCommand( - "echo '" - + zipHeader - + "' | cat - " + "cat " + + templateMain.getExecPathString() + + " " + zipFile.getExecPathString() + " > " + executable.getExecPathString()) @@ -187,12 +195,13 @@ public class BazelPythonSemantics implements PythonSemantics { PyCommon common) throws InterruptedException { if (ruleContext.getConfiguration().buildPythonZip()) { FilesToRunProvider zipper = ruleContext.getExecutablePrerequisite("$zipper", Mode.HOST); + Artifact executable = common.getExecutable(); if (!ruleContext.hasErrors()) { createPythonZipAction( ruleContext, - common.getExecutable(), - common.getPythonZipArtifact(), - common.determineMainExecutableSource(false), + executable, + getPythonZipArtifact(ruleContext, executable), + getPythonTemplateMainArtifact(ruleContext, executable), zipper, runfilesSupport); } @@ -230,15 +239,15 @@ public class BazelPythonSemantics implements PythonSemantics { RuleContext ruleContext, Artifact executable, Artifact zipFile, - String main, + Artifact templateMain, FilesToRunProvider zipper, RunfilesSupport runfilesSupport) { NestedSetBuilder<Artifact> inputsBuilder = NestedSetBuilder.stableOrder(); PathFragment workspaceName = getWorkspaceNameForPythonZip(ruleContext.getWorkspaceName()); CustomCommandLine.Builder argv = new CustomCommandLine.Builder(); - - argv.add("__main__.py=" + main); + inputsBuilder.add(templateMain); + argv.add("__main__.py=" + templateMain.getExecPathString()); // Creating __init__.py files under each directory argv.add("__init__.py="); 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/stub_template.txt index 8e561a55c3..ac0b8bd337 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/stub_template.txt @@ -1,9 +1,12 @@ -#!/usr/bin/python +#!/usr/bin/env python import os import re +import tempfile +import shutil import sys import subprocess +import zipfile # Return True if running on Windows def IsWindows(): @@ -23,6 +26,9 @@ def SearchPath(name): return path return None +def IsRunningFromZip(): + return %is_zipfile% + # Find the real Python binary if it's not a normal absolute path def FindPythonBinary(): if PYTHON_BINARY.startswith('//'): @@ -43,11 +49,8 @@ def CreatePythonPathEntries(python_imports, module_space): parts = python_imports.split(':'); return [module_space] + ["%s/%s" % (module_space, path) for path in parts] -def Main(): - args = sys.argv[1:] - - new_env = {} - +# Find the runfiles tree +def FindModuleSpace(): # Follow symlinks, looking for my module space stub_filename = os.path.abspath(sys.argv[0]) while True: @@ -73,6 +76,24 @@ def Main(): raise AssertionError('Cannot find .runfiles directory for %s' % sys.argv[0]) + return module_space + +# Create the runfiles tree by extracting the zip file +def CreateModuleSpace(): + module_space = tempfile.mkdtemp("", "Bazel.runfiles_") + zf = zipfile.ZipFile(os.path.dirname(__file__)) + zf.extractall(module_space) + return module_space + +def Main(): + args = sys.argv[1:] + + new_env = {} + + if IsRunningFromZip(): + module_space = CreateModuleSpace() + else: + module_space = FindModuleSpace() python_imports = '%imports%' python_path_entries = CreatePythonPathEntries(python_imports, module_space) @@ -109,7 +130,12 @@ def Main(): try: sys.stdout.flush() - os.execv(args[0], args) + if IsRunningFromZip(): + retCode = subprocess.call(args) + shutil.rmtree(module_space, True) + exit(retCode) + else: + os.execv(args[0], args) except EnvironmentError as e: # This exception occurs when os.execv() fails for some reason. if not getattr(e, 'filename', None): 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 d03f380e9d..ab3f3210f3 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 @@ -448,12 +448,6 @@ public final class PyCommon { public Artifact getExecutable() { return executable; } - /** @return An artifact next to the executable file with ".zip" suffix */ - public Artifact getPythonZipArtifact() { - PathFragment original = executable.getRootRelativePath(); - return ruleContext.getDerivedArtifact( - original.replaceName(original.getBaseName() + ".zip"), executable.getRoot()); - } public Map<PathFragment, Artifact> getConvertedFiles() { return convertedFiles; |