Python launcher: don’t assume that argv[0] is the program name.
This should fix https://github.com/bazelbuild/bazel/issues/14343.
PiperOrigin-RevId: 419401678
diff --git a/src/test/py/bazel/launcher_test.py b/src/test/py/bazel/launcher_test.py
index 2c34722..c65ba74 100644
--- a/src/test/py/bazel/launcher_test.py
+++ b/src/test/py/bazel/launcher_test.py
@@ -398,6 +398,33 @@
self._buildAndCheckArgumentPassing('foo', 'bin')
+ def testPyBinaryLauncherWithDifferentArgv0(self):
+ """Test for https://github.com/bazelbuild/bazel/issues/14343."""
+ self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
+ self.ScratchFile('foo/BUILD', [
+ 'py_binary(',
+ ' name = "bin",',
+ ' srcs = ["bin.py"],',
+ ')',
+ ])
+ self.ScratchFile('foo/bin.py', ['print("Hello world")'])
+
+ 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:bin'])
+ self.AssertExitCode(exit_code, 0, stderr)
+
+ # Try to run the built py_binary.
+ binary_suffix = '.exe' if self.IsWindows() else ''
+ foo_bin = os.path.join(bazel_bin, 'foo', 'bin%s' % binary_suffix)
+ args = [r'C:\Invalid.exe' if self.IsWindows() else '/invalid']
+ exit_code, stdout, stderr = self.RunProgram(args, executable=foo_bin)
+ self.AssertExitCode(exit_code, 0, stderr)
+ self.assertEqual(stdout[0], 'Hello world')
+
def testWindowsJavaExeLauncher(self):
# Skip this test on non-Windows platforms
if not self.IsWindows():
diff --git a/src/test/py/bazel/test_base.py b/src/test/py/bazel/test_base.py
index f980f8f..653a737 100644
--- a/src/test/py/bazel/test_base.py
+++ b/src/test/py/bazel/test_base.py
@@ -450,7 +450,8 @@
env_add=None,
shell=False,
cwd=None,
- allow_failure=True):
+ allow_failure=True,
+ executable=None):
"""Runs a program (args[0]), waits for it to exit.
Args:
@@ -464,6 +465,8 @@
cwd: string; the current working dirctory, will be self._test_cwd if not
specified.
allow_failure: bool; if false, the function checks the return code is 0
+ executable: string or None; executable program to run; use args[0]
+ if None
Returns:
(int, [string], [string]) tuple: exit code, stdout lines, stderr lines
"""
@@ -471,6 +474,7 @@
with tempfile.TemporaryFile(dir=self._test_cwd) as stderr:
proc = subprocess.Popen(
args,
+ executable=executable,
stdout=stdout,
stderr=stderr,
cwd=(str(cwd) if cwd else self._test_cwd),
diff --git a/src/tools/launcher/launcher_main.cc b/src/tools/launcher/launcher_main.cc
index 5e0e142..e2fc473 100644
--- a/src/tools/launcher/launcher_main.cc
+++ b/src/tools/launcher/launcher_main.cc
@@ -12,7 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#ifndef STRICT
+#define STRICT
+#endif
+
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <windows.h>
+
#include <memory>
+#include <string>
#include "src/tools/launcher/bash_launcher.h"
#include "src/tools/launcher/java_launcher.h"
@@ -33,11 +52,21 @@
using std::make_unique;
using std::unique_ptr;
-int wmain(int argc, wchar_t* argv[]) {
- LaunchDataParser::LaunchInfo launch_info;
+static std::wstring GetExecutableFileName() {
+ // https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation
+ constexpr std::wstring::size_type maximum_file_name_length = 0x8000;
+ std::wstring buffer(maximum_file_name_length, L'\0');
+ DWORD length = GetModuleFileNameW(nullptr, &buffer.front(), buffer.size());
+ if (length == 0 || length >= buffer.size()) {
+ die(L"Failed to obtain executable filename");
+ }
+ return buffer.substr(0, length);
+}
- if (!LaunchDataParser::GetLaunchInfo(GetBinaryPathWithExtension(argv[0]),
- &launch_info)) {
+int wmain(int argc, wchar_t* argv[]) {
+ const std::wstring executable_file = GetExecutableFileName();
+ LaunchDataParser::LaunchInfo launch_info;
+ if (!LaunchDataParser::GetLaunchInfo(executable_file, &launch_info)) {
die(L"Failed to parse launch info.");
}
@@ -49,8 +78,8 @@
unique_ptr<BinaryLauncherBase> binary_launcher;
if (result->second == L"Python") {
- binary_launcher =
- make_unique<PythonBinaryLauncher>(launch_info, argc, argv);
+ binary_launcher = make_unique<PythonBinaryLauncher>(
+ launch_info, executable_file, argc, argv);
} else if (result->second == L"Bash") {
binary_launcher = make_unique<BashBinaryLauncher>(launch_info, argc, argv);
} else if (result->second == L"Java") {
diff --git a/src/tools/launcher/python_launcher.cc b/src/tools/launcher/python_launcher.cc
index cd856d9..ebdd219 100644
--- a/src/tools/launcher/python_launcher.cc
+++ b/src/tools/launcher/python_launcher.cc
@@ -59,7 +59,7 @@
// In case the given binary path is a shortened Windows 8dot3 path, we need to
// convert it back to its long path form before using it to find the python
// file.
- wstring full_binary_path = GetWindowsLongPath(args[0]);
+ wstring full_binary_path = GetWindowsLongPath(executable_file_);
if (use_zip_file == L"1") {
python_file = GetBinaryPathWithoutExtension(full_binary_path) + L".zip";
} else {
diff --git a/src/tools/launcher/python_launcher.h b/src/tools/launcher/python_launcher.h
index e0d5c54..eb1ca6d 100644
--- a/src/tools/launcher/python_launcher.h
+++ b/src/tools/launcher/python_launcher.h
@@ -15,6 +15,9 @@
#ifndef BAZEL_SRC_TOOLS_LAUNCHER_PYTHON_LAUNCHER_H_
#define BAZEL_SRC_TOOLS_LAUNCHER_PYTHON_LAUNCHER_H_
+#include <string>
+#include <utility>
+
#include "src/tools/launcher/launcher.h"
namespace bazel {
@@ -23,10 +26,14 @@
class PythonBinaryLauncher : public BinaryLauncherBase {
public:
PythonBinaryLauncher(const LaunchDataParser::LaunchInfo& launch_info,
- int argc, wchar_t* argv[])
- : BinaryLauncherBase(launch_info, argc, argv) {}
+ std::wstring executable_file, int argc, wchar_t* argv[])
+ : BinaryLauncherBase(launch_info, argc, argv),
+ executable_file_(std::move(executable_file)) {}
~PythonBinaryLauncher() override = default;
ExitCode Launch() override;
+
+ private:
+ std::wstring executable_file_;
};
} // namespace launcher