From ac9ed30d400e7ab46a48eb5ec75b9d1cebcb8b33 Mon Sep 17 00:00:00 2001 From: Wez Furlong Date: Wed, 29 Apr 2020 14:38:41 -0700 Subject: [PATCH] fb_py_win_main.c: fix File Not Found errors on windows Summary: This commit resolves an issue with our zipapp executables on Windows that meant that the only reliable way to start them was to use the fully path to the executable. The root cause is that the __wargv array is produced by parsing the process command line into an array, and if you ran `watchman-wait -h` __wargv[0] would have `watchman-wait` rather than the fully qualified path to the executable that the zipapp plumbing requires. The fix is to ask the system for the fully qualified path and ensure that that gets set as both argv[0] AND argv[1]. Reviewed By: xavierd Differential Revision: D21190350 fbshipit-source-id: eeb95084592d30a028a93b2b03877f8cc6c72729 --- build/fbcode_builder/CMake/fb_py_win_main.c | 44 ++++++++++++++++++++- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/build/fbcode_builder/CMake/fb_py_win_main.c b/build/fbcode_builder/CMake/fb_py_win_main.c index a5aee3b4a..6630262be 100644 --- a/build/fbcode_builder/CMake/fb_py_win_main.c +++ b/build/fbcode_builder/CMake/fb_py_win_main.c @@ -19,9 +19,49 @@ int wmain() { fprintf(stderr, "error: failed to allocate argument vector\n"); return 1; } - pyargv[0] = __wargv[0]; - for (int n = 0; n < __argc; ++n) { + + /* Py_Main wants the wide character version of the argv so we pull those + * values from the global __wargv array that has been prepared by MSVCRT. + * + * In order for the zipapp to run we need to insert an extra argument in + * the front of the argument vector that points to ourselves. + * + * An additional complication is that, depending on who prepared the argument + * string used to start our process, the computed __wargv[0] can be a simple + * shell word like `watchman-wait` which is normally resolved together with + * the PATH by the shell. + * That unresolved path isn't sufficient to start the zipapp on windows; + * we need the fully qualified path. + * + * Given: + * __wargv == {"watchman-wait", "-h"} + * + * we want to pass the following to Py_Main: + * + * { + * "z:\build\watchman\python\watchman-wait.exe", + * "z:\build\watchman\python\watchman-wait.exe", + * "-h" + * } + */ + +#define PATH_SIZE 1024 + wchar_t full_path_to_argv0[PATH_SIZE]; + DWORD len = GetModuleFileNameW(NULL, full_path_to_argv0, PATH_SIZE); + if (len == 0 || + len == PATH_SIZE && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + fprintf( + stderr, + "error: %d while retrieving full path to this executable\n", + GetLastError()); + return 1; + } + + for (int n = 1; n < __argc; ++n) { pyargv[n + 1] = __wargv[n]; } + pyargv[0] = full_path_to_argv0; + pyargv[1] = full_path_to_argv0; + return Py_Main(__argc + 1, pyargv); }