diff options
Diffstat (limited to 'src/main/java')
8 files changed, 145 insertions, 63 deletions
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:$=\$%" |