diff options
author | Yun Peng <pcloudy@google.com> | 2017-07-28 10:22:57 +0200 |
---|---|---|
committer | Dmitry Lomov <dslomov@google.com> | 2017-07-31 16:05:56 +0200 |
commit | c2c938ae2e75b5b881f06b18cce86dc87bae6fe6 (patch) | |
tree | 391b596c7ef66eeac9a0ef864f112bc6eb0f09c3 | |
parent | 639bba946ea01f5cf0a2cdb51c46012a602814a1 (diff) |
Apply native binary launcher to sh_binary
This change:
1. Added launcher to @bazel_tools
If the host platform is Windows, we use a prebuilt launcher.exe
, otherwise the launcher needs to be built with MSVC first.
2. Launching sh_binary using native launcher.
Change-Id: I5a63135455057fbfe04ff0cce7ec7994ef0c347a
PiperOrigin-RevId: 163442540
17 files changed, 260 insertions, 90 deletions
@@ -147,6 +147,7 @@ py_binary( "//src/tools/android/java/com/google/devtools/build/android/idlclass:embedded_tools", "//src/tools/android/java/com/google/devtools/build/android/dexer:embedded_tools", "//src/tools/android/java/com/google/devtools/build/android/ziputils:embedded_tools", + "//src/tools/launcher:srcs", "//src/tools/singlejar:embedded_tools", "//src/main/cpp/util:embedded_tools", "//src/main/native:embedded_tools", @@ -179,12 +180,15 @@ py_binary( # is resolved, use cc implementation of singlejar on windows ":windows": [ "//src/java_tools/singlejar:SingleJar_deploy.jar", + "//src/tools/launcher:launcher", ], ":windows_msys": [ "//src/java_tools/singlejar:SingleJar_deploy.jar", + "//src/tools/launcher:launcher", ], ":windows_msvc": [ "//src/java_tools/singlejar:SingleJar_deploy.jar", + "//src/tools/launcher:launcher", ], ":arm": [ "//src/java_tools/singlejar:SingleJar_deploy.jar", diff --git a/src/BUILD.tools b/src/BUILD.tools index d4e0a4164c..46a13e6e71 100644 --- a/src/BUILD.tools +++ b/src/BUILD.tools @@ -21,3 +21,21 @@ config_setting( values = {"cpu": "x64_windows_msys"}, visibility = ["//visibility:public"], ) + +config_setting( + name = "host_windows", + values = {"host_cpu": "x64_windows"}, + visibility = ["//visibility:public"], +) + +config_setting( + name = "host_windows_msvc", + values = {"host_cpu": "x64_windows_msvc"}, + visibility = ["//visibility:public"], +) + +config_setting( + name = "host_windows_msys", + values = {"host_cpu": "x64_windows_msys"}, + visibility = ["//visibility:public"], +) diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD index 291bb98fb6..6236d6b643 100644 --- a/src/main/java/com/google/devtools/build/lib/BUILD +++ b/src/main/java/com/google/devtools/build/lib/BUILD @@ -732,7 +732,6 @@ 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/ConfiguredRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java index 42826523e1..2d13abd9ec 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java @@ -227,11 +227,19 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { registeredSkylarkProviders = ImmutableBiMap.builder(); private Map<String, String> platformRegexps = new TreeMap<>(); + // TODO(pcloudy): Remove this field after Bazel rule definitions are not used internally. + private String nativeLauncherLabel; + public Builder setProductName(String productName) { this.productName = productName; return this; } + public Builder setNativeLauncherLabel(String label) { + this.nativeLauncherLabel = label; + return this; + } + public void addWorkspaceFilePrefix(String contents) { defaultWorkspaceFilePrefix.append(contents); } @@ -455,6 +463,14 @@ public class ConfiguredRuleClassProvider implements RuleClassProvider { } @Override + public Label getLauncherLabel() { + if (nativeLauncherLabel == null) { + return null; + } + return getToolsLabel(nativeLauncherLabel); + } + + @Override public String getToolsRepository() { return toolsRepository; } diff --git a/src/main/java/com/google/devtools/build/lib/analysis/RuleDefinitionEnvironment.java b/src/main/java/com/google/devtools/build/lib/analysis/RuleDefinitionEnvironment.java index 5880e954dd..e388b86284 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/RuleDefinitionEnvironment.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/RuleDefinitionEnvironment.java @@ -16,6 +16,8 @@ package com.google.devtools.build.lib.analysis; import com.google.devtools.build.lib.cmdline.Label; +import javax.annotation.Nullable; + /** * Encapsulates the services available for implementors of the {@link RuleDefinition} * interface. @@ -37,4 +39,14 @@ public interface RuleDefinitionEnvironment { * Returns the tools repository prefix. */ String getToolsRepository(); + + /** + * Returns the label for Bazel binary launcher. + * In bazel, it should be //tools/launcher:launcher, otherwise it should be null. + * + * TODO(pcloudy): Remove this after Bazel rule definitions are not used internally anymore. + * Related bug b/63658220 + */ + @Nullable + Label getLauncherLabel(); } diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java index e88ff5af88..e93a154d44 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java @@ -213,6 +213,7 @@ public class BazelRuleClassProvider { .setProductName("bazel") .setConfigurationCollectionFactory(new BazelConfigurationCollection()) .setPrelude("//tools/build_rules:prelude_bazel") + .setNativeLauncherLabel("//tools/launcher:launcher") .setRunfilesPrefix(Label.DEFAULT_REPOSITORY_DIRECTORY) .setPrerequisiteValidator(new BazelPrerequisiteValidator()); diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/BazelShBinaryRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/BazelShBinaryRule.java index 02bcffae87..7ba0859968 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/BazelShBinaryRule.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/BazelShBinaryRule.java @@ -13,10 +13,15 @@ // limitations under the License. package com.google.devtools.build.lib.bazel.rules.sh; +import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST; +import static com.google.devtools.build.lib.packages.Attribute.attr; +import static com.google.devtools.build.lib.packages.BuildType.LABEL; + import com.google.devtools.build.lib.analysis.BaseRuleClasses; import com.google.devtools.build.lib.analysis.RuleDefinition; import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; import com.google.devtools.build.lib.bazel.rules.sh.BazelShRuleClasses.ShRule; +import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.packages.RuleClass.Builder; @@ -26,6 +31,10 @@ import com.google.devtools.build.lib.packages.RuleClass.Builder; public final class BazelShBinaryRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { + Label launcher = environment.getLauncherLabel(); + if (launcher != null) { + builder.add(attr("$launcher", LABEL).cfg(HOST).value(launcher)); + } return builder.build(); } diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/BazelShTestRule.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/BazelShTestRule.java index aed6ef5d10..8001a4ecd2 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/BazelShTestRule.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/BazelShTestRule.java @@ -13,10 +13,15 @@ // limitations under the License. package com.google.devtools.build.lib.bazel.rules.sh; +import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST; +import static com.google.devtools.build.lib.packages.Attribute.attr; +import static com.google.devtools.build.lib.packages.BuildType.LABEL; + import com.google.devtools.build.lib.analysis.BaseRuleClasses; import com.google.devtools.build.lib.analysis.RuleDefinition; import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; import com.google.devtools.build.lib.bazel.rules.sh.BazelShRuleClasses.ShRule; +import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.packages.RuleClass.Builder; import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; @@ -27,6 +32,10 @@ import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; public final class BazelShTestRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { + Label launcher = environment.getLauncherLabel(); + if (launcher != null) { + builder.add(attr("$launcher", LABEL).cfg(HOST).value(launcher)); + } return builder.build(); } 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 66b0ec4a1d..47a15dec3f 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 @@ -13,7 +13,11 @@ // limitations under the License. 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; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; @@ -22,23 +26,23 @@ import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.Runfiles; import com.google.devtools.build.lib.analysis.RunfilesProvider; 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.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.analysis.actions.SpawnAction; 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; -import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; import com.google.devtools.build.lib.util.OS; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +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 { @@ -68,7 +72,7 @@ public class ShBinary implements RuleConfiguredTargetFactory { ruleContext.getConfiguration().legacyExternalRunfiles()); Artifact mainExecutable = - (OS.getCurrent() == OS.WINDOWS) ? wrapperForWindows(ruleContext, symlink, src) : symlink; + (OS.getCurrent() == OS.WINDOWS) ? launcherForWindows(ruleContext, symlink, src) : symlink; if (symlink != mainExecutable) { filesToBuildBuilder.add(mainExecutable); runfilesBuilder.addArtifact(symlink); @@ -92,33 +96,98 @@ public class ShBinary implements RuleConfiguredTargetFactory { .build(); } - private static Artifact wrapperForWindows( - RuleContext ruleContext, Artifact primaryOutput, Artifact mainFile) { - if (primaryOutput.getFilename().endsWith(".exe") - || primaryOutput.getFilename().endsWith(".bat") - || primaryOutput.getFilename().endsWith(".cmd")) { - String suffix = - primaryOutput.getFilename().substring(primaryOutput.getFilename().length() - 4); - if (mainFile.getFilename().endsWith(suffix)) { + // Write launch info to buffer, return the number of bytes written. + private static int writeLaunchInfo(ByteArrayOutputStream buffer, String key, String value) + throws IOException { + byte[] keyBytes = key.getBytes(UTF_8); + byte[] valueBytes = value.getBytes(UTF_8); + buffer.write(keyBytes); + buffer.write('='); + buffer.write(valueBytes); + buffer.write('\0'); + return keyBytes.length + valueBytes.length + 2; + } + + private static boolean isWindowsExecutable(Artifact artifact) { + return artifact.getExtension().equals("exe") + || artifact.getExtension().equals("cmd") + || artifact.getExtension().equals("bat"); + } + + private static Artifact launcherForWindows( + RuleContext ruleContext, Artifact primaryOutput, 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(); } } - Artifact wrapper = - ruleContext.getImplicitOutputArtifact(ruleContext.getTarget().getName() + ".cmd"); + // 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. + Artifact launcher = ruleContext.getPrerequisiteArtifact("$launcher", Mode.HOST); + Artifact bashLauncher = + ruleContext.getImplicitOutputArtifact(ruleContext.getTarget().getName() + ".exe"); + Artifact launchInfoFile = + ruleContext.getRelatedArtifact(bashLauncher.getRootRelativePath(), ".launch_info"); + + ByteArrayOutputStream launchInfo = new ByteArrayOutputStream(); + Long dataSize = 0L; + try { + dataSize += writeLaunchInfo(launchInfo, "binary_type", "Bash"); + dataSize += writeLaunchInfo(launchInfo, "workspace_name", ruleContext.getWorkspaceName()); + dataSize += + writeLaunchInfo( + launchInfo, + "bash_bin_path", + ruleContext + .getFragment(BazelConfiguration.class) + .getShellExecutable() + .getPathString()); + dataSize += writeLaunchInfo(launchInfo, "bash_main_file", mainFile.getRunfilesPathString()); + + ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); + // All Windows versions are little endian. + buffer.order(ByteOrder.LITTLE_ENDIAN); + buffer.putLong(dataSize); + + launchInfo.write(buffer.array()); + } catch (IOException e) { + ruleContext.ruleError(e.getMessage()); + throw new RuleErrorException(); + } + ruleContext.registerAction( - new TemplateExpansionAction( + new BinaryFileWriteAction( ruleContext.getActionOwner(), - wrapper, - STUB_SCRIPT_WINDOWS, - ImmutableList.of( - Substitution.of( - "%bash_exe_path%", - ruleContext - .getFragment(BazelConfiguration.class) - .getShellExecutable() - .getPathString())), - true)); - return wrapper; + launchInfoFile, + ByteSource.wrap(launchInfo.toByteArray()), + /*makeExecutable=*/ false)); + String path = ruleContext.getConfiguration().getActionEnvironment().getFixedEnv().get("PATH"); + ruleContext.registerAction( + new SpawnAction.Builder() + .addInput(launcher) + .addInput(launchInfoFile) + .addOutput(bashLauncher) + .setShellCommand( + "cmd.exe /c \"copy /Y /B " + + launcher.getExecPathString().replace('/', '\\') + + "+" + + launchInfoFile.getExecPathString().replace('/', '\\') + + " " + + bashLauncher.getExecPathString().replace('/', '\\') + + " > nul\"") + .setEnvironment(ImmutableMap.of("PATH", path)) + .setMnemonic("BuildBashLauncher") + .build(ruleContext)); + + return bashLauncher; } } 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 deleted file mode 100644 index 5c30e7fb8c..0000000000 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/sh/sh_stub_template_windows.txt +++ /dev/null @@ -1,33 +0,0 @@ -@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:\=/% - -rem replaces $ with \$ in $*, then puts it on the command line -rem Cribbed from here: http://ss64.com/nt/syntax-replace.html -set all_args=%* -call %bash_path% -c "'%sh_path%' %all_args:$=\$%" diff --git a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java index a840876685..a8aaa9735d 100644 --- a/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java +++ b/src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java @@ -147,6 +147,12 @@ public final class BazelAnalysisMock extends AnalysisMock { "package(default_visibility=['//visibility:public'])", "exports_files(['precompile.py'])", "cc_binary(name='zipper', srcs=['zip_main.cc'])"); + + config.create( + "/bazel_tools_workspace/tools/launcher/BUILD", + "package(default_visibility=['//visibility:public'])", + "cc_binary(name='launcher', srcs=['launcher_main.cc'])"); + ccSupport().setup(config); } diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java index e8d3d5b5cd..5610d429bb 100644 --- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java +++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleContextTest.java @@ -642,7 +642,7 @@ public class SkylarkRuleContextTest extends SkylarkTestCase { public void testGetExecutablePrerequisite() throws Exception { SkylarkRuleContext ruleContext = createRuleContext("//foo:androidlib"); Object result = evalRuleContextCode(ruleContext, "ruleContext.executable._jarjar_bin"); - assertThat(((Artifact) result).getFilename()).matches("^jarjar_bin(\\.cmd){0,1}$"); + assertThat(((Artifact) result).getFilename()).matches("^jarjar_bin(\\.exe){0,1}$"); } @Test @@ -659,7 +659,7 @@ public class SkylarkRuleContextTest extends SkylarkTestCase { (SpawnAction) Iterables.getOnlyElement( ruleContext.getRuleContext().getAnalysisEnvironment().getRegisteredActions()); - assertThat(action.getCommandFilename()).matches("^.*/jarjar_bin(\\.cmd){0,1}$"); + assertThat(action.getCommandFilename()).matches("^.*/jarjar_bin(\\.exe){0,1}$"); } @Test diff --git a/src/test/py/bazel/BUILD b/src/test/py/bazel/BUILD index f744c21e1f..73bd4a383a 100644 --- a/src/test/py/bazel/BUILD +++ b/src/test/py/bazel/BUILD @@ -65,8 +65,8 @@ py_test( ) py_test( - name = "launcher_script_test", + name = "launcher_test", size = "medium", - srcs = ["launcher_script_test.py"], + srcs = ["launcher_test.py"], deps = [":test_base"], ) diff --git a/src/test/py/bazel/launcher_script_test.py b/src/test/py/bazel/launcher_test.py index 690c7fb6d5..6c92dd7c0c 100644 --- a/src/test/py/bazel/launcher_script_test.py +++ b/src/test/py/bazel/launcher_test.py @@ -19,7 +19,7 @@ import unittest from src.test.py.bazel import test_base -class LauncherScriptTest(test_base.TestBase): +class LauncherTest(test_base.TestBase): def testJavaBinaryLauncher(self): self.ScratchFile('WORKSPACE') @@ -75,13 +75,9 @@ class LauncherScriptTest(test_base.TestBase): # 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 rule's name and the srcs's name end with the - # same extension, and this extension is one of ".exe", ".cmd", or - # ".bat", then sh_binary makes a copy of the output file, with the - # same name as the rule. Otherwise (if the rule's name doesn't end - # with such an extension, or the extension of it doesn't match the - # main file's) then Bazel creates a %{rulename}.cmd output which is - # a similar launcher script to that generated by java_binary rules. + # 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"],', @@ -112,30 +108,37 @@ class LauncherScriptTest(test_base.TestBase): self.AssertExitCode(exit_code, 0, stderr) bazel_bin = stdout[0] - exit_code, _, stderr = self.RunBazel(['build', '//foo:all']) + exit_code, _, stderr = self.RunBazel(['build', '//foo:bin1.sh']) self.AssertExitCode(exit_code, 0, stderr) - bin1 = os.path.join(bazel_bin, 'foo', 'bin1.sh.cmd' + bin1 = os.path.join(bazel_bin, 'foo', 'bin1.sh.exe' 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']) + self.AssertExitCode(exit_code, 0, stderr) + bin2 = os.path.join(bazel_bin, 'foo/bin2.cmd') self.assertTrue(os.path.exists(bin2)) self.assertTrue( os.path.isdir(os.path.join(bazel_bin, 'foo/bin2.cmd.runfiles'))) - bin3 = os.path.join(bazel_bin, 'foo', 'bin3.bat.cmd' - if self.IsWindows() else 'bin3.bat') - self.assertTrue(os.path.exists(bin3)) - self.assertTrue( - os.path.isdir(os.path.join(bazel_bin, 'foo/bin3.bat.runfiles'))) + exit_code, _, stderr = self.RunBazel(['build', '//foo:bin3.bat']) + if self.IsWindows(): + self.AssertExitCode(exit_code, 1, stderr) + self.assertIn('target name extension should match source file extension.', + os.linesep.join(stderr)) + else: + bin3 = os.path.join(bazel_bin, 'foo', 'bin3.bat') + self.assertTrue(os.path.exists(bin3)) + self.assertTrue( + os.path.isdir(os.path.join(bazel_bin, 'foo/bin3.bat.runfiles'))) if self.IsWindows(): self.assertTrue(os.path.isfile(bin1)) self.assertTrue(os.path.isfile(bin2)) - self.assertTrue(os.path.isfile(bin3)) else: self.assertTrue(os.path.islink(bin1)) self.assertTrue(os.path.islink(bin2)) @@ -148,9 +151,6 @@ class LauncherScriptTest(test_base.TestBase): self.AssertRunfilesManifestContains( os.path.join(bazel_bin, 'foo/bin2.cmd.runfiles/MANIFEST'), '__main__/bar/bar.txt') - self.AssertRunfilesManifestContains( - os.path.join(bazel_bin, 'foo/bin3.bat.runfiles/MANIFEST'), - '__main__/bar/bar.txt') else: self.assertTrue( os.path.islink( @@ -174,9 +174,43 @@ class LauncherScriptTest(test_base.TestBase): self.AssertExitCode(exit_code, 0, stderr) self.assertEqual(stdout[0], 'hello batch') - exit_code, stdout, stderr = self.RunProgram([bin3]) - self.AssertExitCode(exit_code, 0, stderr) - self.assertEqual(stdout[0], 'hello batch') + def testShBinaryArgumentPassing(self): + self.ScratchFile('WORKSPACE') + self.ScratchFile('foo/BUILD', [ + 'sh_binary(', + ' name = "bin",', + ' srcs = ["bin.sh"],', + ')', + ]) + foo_sh = self.ScratchFile('foo/bin.sh', [ + '#!/bin/bash', + '# Store arguments in a array', + 'args=("$@")', + '# Get the number of arguments', + 'N=${#args[@]}', + '# Echo each argument', + 'for (( i=0;i<$N;i++)); do', + ' echo ${args[${i}]}', + 'done', + ]) + os.chmod(foo_sh, 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] + + exit_code, _, stderr = self.RunBazel(['build', '//foo:bin']) + self.AssertExitCode(exit_code, 0, stderr) + + bin1 = os.path.join(bazel_bin, 'foo', 'bin.exe' + if self.IsWindows() else 'bin') + self.assertTrue(os.path.exists(bin1)) + self.assertTrue(os.path.isdir(os.path.join(bazel_bin, 'foo/bin.runfiles'))) + + arguments = ['a', 'a b', '"b"', 'C:\\a\\b\\', '"C:\\a b\\c\\"'] + exit_code, stdout, stderr = self.RunProgram([bin1] + arguments) + self.AssertExitCode(exit_code, 0, stderr) + self.assertEqual(stdout, arguments) def testPyBinaryLauncher(self): self.ScratchFile('WORKSPACE') @@ -220,7 +254,7 @@ class LauncherScriptTest(test_base.TestBase): ' with open(sys.argv[1], "w") as f:', ' f.write("Hello World!")', 'else:', - ' print "Hello World!"', + ' print("Hello World!")', ]) self.ScratchFile('bar/BUILD', ['exports_files(["bar.txt"])']) self.ScratchFile('bar/bar.txt', ['hello']) diff --git a/tools/BUILD b/tools/BUILD index 9f9052ea10..49cd6290a7 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -19,6 +19,7 @@ filegroup( "//tools/coverage:srcs", "//tools/ide:srcs", "//tools/jdk:srcs", + "//tools/launcher:srcs", "//tools/platforms:srcs", "//tools/genrule:srcs", "//tools/cpp:srcs", @@ -50,6 +51,7 @@ filegroup( "//tools/j2objc:srcs", "//tools/jdk:package-srcs", "//tools/jdk:srcs", + "//tools/launcher:srcs", "//tools/platforms:package-srcs", "//tools/objc:srcs", "//tools/python:srcs", diff --git a/tools/launcher/BUILD b/tools/launcher/BUILD new file mode 100644 index 0000000000..0158b25589 --- /dev/null +++ b/tools/launcher/BUILD @@ -0,0 +1,11 @@ +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "srcs", + srcs = glob(["**"]), +) + +alias( + name = "launcher", + actual = "//src/tools/launcher:launcher", +) diff --git a/tools/launcher/BUILD.tools b/tools/launcher/BUILD.tools new file mode 100644 index 0000000000..8679c1a32f --- /dev/null +++ b/tools/launcher/BUILD.tools @@ -0,0 +1,13 @@ +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "launcher", + srcs = select({ + "//src:host_windows": ["launcher.exe"], + "//src:host_windows_msvc": ["launcher.exe"], + "//src:host_windows_msys": ["launcher.exe"], + "//conditions:default": [ + "//src/tools/launcher:launcher", + ], + }), +) |