aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/google/devtools/build/lib/BUILD1
-rw-r--r--src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java16
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/sh/ShBinary.java62
-rw-r--r--src/main/java/com/google/devtools/build/lib/bazel/rules/sh/sh_stub_template_windows.txt35
-rw-r--r--src/test/py/bazel/launcher_test.py105
5 files changed, 154 insertions, 65 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index fb1870b6e8..f17579cdfa 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -734,6 +734,7 @@ java_library(
"bazel/rules/java/java_stub_template.txt",
"bazel/rules/java/java_stub_template_windows.txt",
"bazel/rules/python/python_stub_template.txt",
+ "bazel/rules/sh/sh_stub_template_windows.txt",
],
deps = [
":bazel",
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
index 106a066666..a96156aa42 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/config/BuildConfiguration.java
@@ -1129,6 +1129,17 @@ public final class BuildConfiguration implements BuildEvent {
)
public TriState buildPythonZip;
+ @Option(
+ name = "windows_exe_launcher",
+ defaultValue = "true",
+ documentationCategory = OptionDocumentationCategory.UNDOCUMENTED,
+ effectTags = {OptionEffectTag.UNKNOWN},
+ help =
+ "Build a Windows exe launcher for sh_binary rule, "
+ + "it has no effect on other platforms than Windows"
+ )
+ public boolean windowsExeLauncher;
+
@Override
public FragmentOptions getHost(boolean fallback) {
Options host = (Options) getDefault();
@@ -1139,6 +1150,7 @@ public final class BuildConfiguration implements BuildEvent {
host.useDynamicConfigurations = useDynamicConfigurations;
host.enableRunfiles = enableRunfiles;
host.buildPythonZip = buildPythonZip;
+ host.windowsExeLauncher = windowsExeLauncher;
host.commandLineBuildVariables = commandLineBuildVariables;
host.enforceConstraints = enforceConstraints;
host.separateGenfilesDirectory = separateGenfilesDirectory;
@@ -2711,6 +2723,10 @@ public final class BuildConfiguration implements BuildEvent {
}
}
+ public boolean enableWindowsExeLauncher() {
+ return options.windowsExeLauncher;
+ }
+
/**
* Collects executables defined by fragments.
*/
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/ShBinary.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/ShBinary.java
index 0b3d37dfd3..f264df8d49 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/ShBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/ShBinary.java
@@ -16,7 +16,6 @@ package com.google.devtools.build.lib.bazel.rules.sh;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
import com.google.common.io.ByteSource;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
@@ -29,6 +28,9 @@ import com.google.devtools.build.lib.analysis.RunfilesSupport;
import com.google.devtools.build.lib.analysis.actions.BinaryFileWriteAction;
import com.google.devtools.build.lib.analysis.actions.ExecutableSymlinkAction;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
+import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction;
+import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Substitution;
+import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Template;
import com.google.devtools.build.lib.bazel.rules.BazelConfiguration;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
@@ -43,6 +45,8 @@ import java.nio.ByteOrder;
* Implementation for the sh_binary rule.
*/
public class ShBinary implements RuleConfiguredTargetFactory {
+ private static final Template STUB_SCRIPT_WINDOWS =
+ Template.forResource(ShBinary.class, "sh_stub_template_windows.txt");
@Override
public ConfiguredTarget create(RuleContext ruleContext) throws RuleErrorException {
@@ -114,21 +118,8 @@ public class ShBinary implements RuleConfiguredTargetFactory {
|| artifact.getExtension().equals("bat");
}
- private static Artifact launcherForWindows(
- RuleContext ruleContext, Artifact primaryOutput, Artifact mainFile)
+ private static Artifact createWindowsExeLauncher(RuleContext ruleContext, Artifact mainFile)
throws RuleErrorException {
- if (isWindowsExecutable(mainFile)) {
- // If the extensions don't match, we should always respect mainFile's extension.
- if (mainFile.getExtension().equals(primaryOutput.getExtension())) {
- return primaryOutput;
- } else {
- ruleContext.ruleError(
- "Source file is a Windows executable file,"
- + " target name extension should match source file extension");
- throw new RuleErrorException();
- }
- }
-
// The launcher file consists of a base launcher binary and the launch information appended to
// the binary. The length of launch info is a signed 64-bit integer written at the end of
// the binary in little endian.
@@ -170,7 +161,7 @@ public class ShBinary implements RuleConfiguredTargetFactory {
launchInfoFile,
ByteSource.wrap(launchInfo.toByteArray()),
/*makeExecutable=*/ false));
- String path = ruleContext.getConfiguration().getActionEnvironment().getFixedEnv().get("PATH");
+
ruleContext.registerAction(
new SpawnAction.Builder()
.addInput(launcher)
@@ -184,10 +175,47 @@ public class ShBinary implements RuleConfiguredTargetFactory {
+ " "
+ bashLauncher.getExecPathString().replace('/', '\\')
+ " > nul\"")
- .setEnvironment(ImmutableMap.of("PATH", path))
+ .useDefaultShellEnvironment()
.setMnemonic("BuildBashLauncher")
.build(ruleContext));
return bashLauncher;
}
+
+ private static Artifact launcherForWindows(
+ RuleContext ruleContext, Artifact primaryOutput, Artifact mainFile)
+ throws RuleErrorException {
+ if (isWindowsExecutable(mainFile)) {
+ if (mainFile.getExtension().equals(primaryOutput.getExtension())) {
+ return primaryOutput;
+ } else {
+ // If the extensions don't match, we should always respect mainFile's extension.
+ ruleContext.ruleError(
+ "Source file is a Windows executable file,"
+ + " target name extension should match source file extension");
+ throw new RuleErrorException();
+ }
+ }
+
+ if (ruleContext.getConfiguration().enableWindowsExeLauncher()) {
+ return createWindowsExeLauncher(ruleContext, mainFile);
+ }
+
+ Artifact wrapper =
+ ruleContext.getImplicitOutputArtifact(ruleContext.getTarget().getName() + ".cmd");
+ ruleContext.registerAction(
+ new TemplateExpansionAction(
+ ruleContext.getActionOwner(),
+ wrapper,
+ STUB_SCRIPT_WINDOWS,
+ ImmutableList.of(
+ Substitution.of(
+ "%bash_exe_path%",
+ ruleContext
+ .getFragment(BazelConfiguration.class)
+ .getShellExecutable()
+ .getPathString())),
+ true));
+ return wrapper;
+ }
}
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/sh_stub_template_windows.txt b/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/sh_stub_template_windows.txt
new file mode 100644
index 0000000000..175985fac6
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/sh_stub_template_windows.txt
@@ -0,0 +1,35 @@
+@rem Copyright 2016 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 sh_stub_template_windows.txt. Please
+@rem don't edit it directly.
+@rem See the header comments in the accompanying shell script (same path as that
+@rem of this file, minus the ".cmd" extension) for available command line flags.
+
+@echo off
+SETLOCAL ENABLEEXTENSIONS
+
+set bash_path=%bash_exe_path%
+
+rem launcher=${$0%.cmd}
+set launcher=%~dp0%~n0
+
+set sh_path=%launcher:\=/%
+
+set RUNFILES_MANIFEST_ONLY=1
+set RUNFILES_MANIFEST_FILE=%sh_path%.runfiles/MANIFEST
+set all_args=%*
+rem replaces $ with \$ in $*, then puts it on the command line
+rem Cribbed from here: http://ss64.com/nt/syntax-replace.html
+call %bash_path% -c "'%sh_path%' %all_args:$=\$%"
diff --git a/src/test/py/bazel/launcher_test.py b/src/test/py/bazel/launcher_test.py
index 1baf5d29c6..3487545820 100644
--- a/src/test/py/bazel/launcher_test.py
+++ b/src/test/py/bazel/launcher_test.py
@@ -82,59 +82,20 @@ class LauncherTest(test_base.TestBase):
self.assertEqual(stdout[2], 'runfiles_manifest_only=')
self.assertRegexpMatches(stdout[3], r'^runfiles_manifest_file.*MANIFEST$')
- def testShBinaryLauncher(self):
- self.ScratchFile('WORKSPACE')
- self.ScratchFile(
- 'foo/BUILD',
- [
- # On Linux/MacOS, all sh_binary rules generate an output file with
- # the same name as the rule, and this is a symlink to the file in
- # `srcs`. (Bazel allows only one file in `sh_binary.srcs`.)
- # On Windows, if the srcs's extension is one of ".exe", ".cmd", or
- # ".bat", then Bazel requires the rule's name has the same
- # extension, and the output file will be a copy of the source file.
- 'sh_binary(',
- ' name = "bin1.sh",',
- ' srcs = ["foo.sh"],',
- ' data = ["//bar:bar.txt"],',
- ')',
- 'sh_binary(',
- ' name = "bin2.cmd",', # name's extension matches that of srcs[0]
- ' srcs = ["foo.cmd"],',
- ' data = ["//bar:bar.txt"],',
- ')',
- 'sh_binary(',
- ' name = "bin3.bat",', # name's extension doesn't match srcs[0]'s
- ' srcs = ["foo.cmd"],',
- ' data = ["//bar:bar.txt"],',
- ')',
- ])
- foo_sh = self.ScratchFile('foo/foo.sh', [
- '#!/bin/bash',
- 'echo hello shell',
- 'echo runfiles_manifest_only=${RUNFILES_MANIFEST_ONLY:-}',
- 'echo runfiles_manifest_file=${RUNFILES_MANIFEST_FILE:-}',
- ])
- foo_cmd = self.ScratchFile('foo/foo.cmd', ['@echo hello batch'])
- self.ScratchFile('bar/BUILD', ['exports_files(["bar.txt"])'])
- self.ScratchFile('bar/bar.txt', ['hello'])
- os.chmod(foo_sh, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
- os.chmod(foo_cmd, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
-
- exit_code, stdout, stderr = self.RunBazel(['info', 'bazel-bin'])
+ def _buildShBinaryTargets(self, bazel_bin, launcher_flag, bin1_suffix):
+ exit_code, _, stderr = self.RunBazel(['build', '//foo:bin1.sh'] +
+ launcher_flag)
self.AssertExitCode(exit_code, 0, stderr)
- bazel_bin = stdout[0]
- exit_code, _, stderr = self.RunBazel(['build', '//foo:bin1.sh'])
- self.AssertExitCode(exit_code, 0, stderr)
-
- bin1 = os.path.join(bazel_bin, 'foo', 'bin1.sh.exe'
+ bin1 = os.path.join(bazel_bin, 'foo', 'bin1.sh.%s' % bin1_suffix
if self.IsWindows() else 'bin1.sh')
+
self.assertTrue(os.path.exists(bin1))
self.assertTrue(
os.path.isdir(os.path.join(bazel_bin, 'foo/bin1.sh.runfiles')))
- exit_code, _, stderr = self.RunBazel(['build', '//foo:bin2.cmd'])
+ exit_code, _, stderr = self.RunBazel(['build', '//foo:bin2.cmd'] +
+ launcher_flag)
self.AssertExitCode(exit_code, 0, stderr)
bin2 = os.path.join(bazel_bin, 'foo/bin2.cmd')
@@ -142,7 +103,8 @@ class LauncherTest(test_base.TestBase):
self.assertTrue(
os.path.isdir(os.path.join(bazel_bin, 'foo/bin2.cmd.runfiles')))
- exit_code, _, stderr = self.RunBazel(['build', '//foo:bin3.bat'])
+ exit_code, _, stderr = self.RunBazel(['build', '//foo:bin3.bat'] +
+ launcher_flag)
if self.IsWindows():
self.AssertExitCode(exit_code, 1, stderr)
self.assertIn('target name extension should match source file extension.',
@@ -202,6 +164,52 @@ class LauncherTest(test_base.TestBase):
self.AssertExitCode(exit_code, 0, stderr)
self.assertEqual(stdout[0], 'hello batch')
+ def testShBinaryLauncher(self):
+ self.ScratchFile('WORKSPACE')
+ self.ScratchFile(
+ 'foo/BUILD',
+ [
+ # On Linux/MacOS, all sh_binary rules generate an output file with
+ # the same name as the rule, and this is a symlink to the file in
+ # `srcs`. (Bazel allows only one file in `sh_binary.srcs`.)
+ # On Windows, if the srcs's extension is one of ".exe", ".cmd", or
+ # ".bat", then Bazel requires the rule's name has the same
+ # extension, and the output file will be a copy of the source file.
+ 'sh_binary(',
+ ' name = "bin1.sh",',
+ ' srcs = ["foo.sh"],',
+ ' data = ["//bar:bar.txt"],',
+ ')',
+ 'sh_binary(',
+ ' name = "bin2.cmd",', # name's extension matches that of srcs[0]
+ ' srcs = ["foo.cmd"],',
+ ' data = ["//bar:bar.txt"],',
+ ')',
+ 'sh_binary(',
+ ' name = "bin3.bat",', # name's extension doesn't match srcs[0]'s
+ ' srcs = ["foo.cmd"],',
+ ' data = ["//bar:bar.txt"],',
+ ')',
+ ])
+ foo_sh = self.ScratchFile('foo/foo.sh', [
+ '#!/bin/bash',
+ 'echo hello shell',
+ 'echo runfiles_manifest_only=${RUNFILES_MANIFEST_ONLY:-}',
+ 'echo runfiles_manifest_file=${RUNFILES_MANIFEST_FILE:-}',
+ ])
+ foo_cmd = self.ScratchFile('foo/foo.cmd', ['@echo hello batch'])
+ self.ScratchFile('bar/BUILD', ['exports_files(["bar.txt"])'])
+ self.ScratchFile('bar/bar.txt', ['hello'])
+ os.chmod(foo_sh, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
+ os.chmod(foo_cmd, 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]
+
+ self._buildShBinaryTargets(bazel_bin, ['--windows_exe_launcher=0'], 'cmd')
+ self._buildShBinaryTargets(bazel_bin, [], 'exe')
+
def testShBinaryArgumentPassing(self):
self.ScratchFile('WORKSPACE')
self.ScratchFile('foo/BUILD', [
@@ -227,7 +235,8 @@ class LauncherTest(test_base.TestBase):
self.AssertExitCode(exit_code, 0, stderr)
bazel_bin = stdout[0]
- exit_code, _, stderr = self.RunBazel(['build', '//foo:bin'])
+ exit_code, _, stderr = self.RunBazel(
+ ['build', '--windows_exe_launcher', '//foo:bin'])
self.AssertExitCode(exit_code, 0, stderr)
bin1 = os.path.join(bazel_bin, 'foo', 'bin.exe'