diff --git a/src/test/py/bazel/launcher_test.py b/src/test/py/bazel/launcher_test.py index 2c34722a110810..c65ba749780591 100644 --- a/src/test/py/bazel/launcher_test.py +++ b/src/test/py/bazel/launcher_test.py @@ -398,6 +398,33 @@ def testPyBinaryArgumentPassing(self): 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 f980f8fd85b3ee..653a73746f0450 100644 --- a/src/test/py/bazel/test_base.py +++ b/src/test/py/bazel/test_base.py @@ -450,7 +450,8 @@ def RunProgram(self, 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 @@ def RunProgram(self, 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 @@ def RunProgram(self, 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 5e0e142e2ef8de..e2fc4735e47276 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 + #include +#include #include "src/tools/launcher/bash_launcher.h" #include "src/tools/launcher/java_launcher.h" @@ -33,11 +52,21 @@ using bazel::launcher::die; using std::make_unique; using std::unique_ptr; +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); +} + int wmain(int argc, wchar_t* argv[]) { + const std::wstring executable_file = GetExecutableFileName(); LaunchDataParser::LaunchInfo launch_info; - - if (!LaunchDataParser::GetLaunchInfo(GetBinaryPathWithExtension(argv[0]), - &launch_info)) { + if (!LaunchDataParser::GetLaunchInfo(executable_file, &launch_info)) { die(L"Failed to parse launch info."); } @@ -49,8 +78,8 @@ int wmain(int argc, wchar_t* argv[]) { unique_ptr binary_launcher; if (result->second == L"Python") { - binary_launcher = - make_unique(launch_info, argc, argv); + binary_launcher = make_unique( + launch_info, executable_file, argc, argv); } else if (result->second == L"Bash") { binary_launcher = make_unique(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 cd856d9248ef4c..ebdd219936dcb7 100644 --- a/src/tools/launcher/python_launcher.cc +++ b/src/tools/launcher/python_launcher.cc @@ -59,7 +59,7 @@ ExitCode PythonBinaryLauncher::Launch() { // 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 e0d5c547a6873c..eb1ca6d2a17ce8 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 +#include + #include "src/tools/launcher/launcher.h" namespace bazel { @@ -23,10 +26,14 @@ namespace launcher { 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