From b639d97fc24786c7866ae842b6163fa891a904ad Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Sun, 2 Nov 2014 21:51:00 +0000 Subject: [PATCH] WIP --- .../windows/installer/installer/installer.cpp | 183 ++++++++---------- .../installer/installer/installer.vcproj | 4 + .../windows/installer/installer/stdafx.h | 1 + .../setuptools/command/scriptsetup.py | 15 +- 4 files changed, 98 insertions(+), 105 deletions(-) diff --git a/misc/build_helpers/windows/installer/installer/installer.cpp b/misc/build_helpers/windows/installer/installer/installer.cpp index fd072d75..f372e47d 100644 --- a/misc/build_helpers/windows/installer/installer/installer.cpp +++ b/misc/build_helpers/windows/installer/installer/installer.cpp @@ -10,18 +10,19 @@ void empty_directory(wchar_t *destination_dir); void unzip_from_executable(wchar_t *executable_path, wchar_t *destination_dir); size_t read_uint32_le(unsigned char *b); void unzip(wchar_t *zip_path, wchar_t *destination_dir); -bool spawn_with_redirect(FILE *redirect, unsigned char *output_buf, size_t output_size, const wchar_t *argv[]); void install_python(wchar_t *python_installer_dir); void scriptsetup(wchar_t *destination_dir); -void pause(); +bool spawn_with_redirect(FILE *redirect, unsigned char *output_buf, size_t output_size, const wchar_t *argv[]); #define fail_unless(x, s) if (!(x)) { fail(s); } void fail(char *s); void warn(char *s); +void pause(); #define REQUIRED_PYTHON_VERSION_PREFIX "Python 2.7." +#define PYTHON_INSTALLER_FILESPEC L"python*.msi" -// defines PKGNAME_AND_VERSION +// defines PKGNAME_AND_VERSION, written by setup.py #include "_version.h" @@ -37,9 +38,14 @@ int wmain(int argc, wchar_t *argv[]) { if (argc >= 2 && wcscmp(argv[1], L"--help") == 0) { printf("installer \n"); + return 1; } wchar_t *destination_dir = (argc >= 2) ? argv[1] : get_default_destination_dir(); + size_t len = wcslen(destination_dir); + // FIXME: strip trailing slash rather than rejecting it + fail_unless(len > 0 && destination_dir[len-1] != '\\' && len < MAX_PATH-1, "Invalid destination directory."); + self_extract(destination_dir); install_python(destination_dir); scriptsetup(destination_dir); @@ -66,31 +72,6 @@ void self_extract(wchar_t *destination_dir) { } void empty_directory(wchar_t *destination_dir) { -#if 0 - // Delete contents of destination_dir if it already exists. - - struct _stat buf; - if (_wstat(destination_dir, &buf) == 0) { - wchar_t destination_dir_dblnul[MAX_PATH+1]; - size_t len = wcslen(destination_dir); - fail_unless(len < MAX_PATH, "Destination path is too long."); - wcscpy(destination_dir_dblnul, destination_dir); - destination_dir_dblnul[len+1] = L'\0'; - - SHFILEOPSTRUCTW shell_file_op = { - NULL, - FO_DELETE, - destination_dir_dblnul, - NULL, - FOF_SILENT | FOF_NOERRORUI | FOF_NOCONFIRMATION, - FALSE, - NULL, - NULL - }; - int res = SHFileOperationW(&shell_file_op); - fail_unless(res == 0, "Could not delete existing contents of destination directory."); - } -#endif // Create an empty directory at destination_dir. errno = 0; int res = _wmkdir(destination_dir); @@ -269,6 +250,76 @@ void unzip(wchar_t *zip_path, wchar_t *destination_dir) { CoUninitialize(); } +void install_python(wchar_t *python_installer_dir) { + printf("Checking for " REQUIRED_PYTHON_VERSION_PREFIX "..\n"); + + unsigned char output_buf[1024]; + const wchar_t *argv[] = { L"python", L"-V", NULL }; + bool res = spawn_with_redirect(stderr, output_buf, sizeof(output_buf), &argv[0]); + if (res) { + printf("Found %s", (char *) output_buf); + if (strncmp((char *) output_buf, REQUIRED_PYTHON_VERSION_PREFIX, strlen(REQUIRED_PYTHON_VERSION_PREFIX)) == 0) { + return; + } else { + printf("but we need a newer version.\n"); + } + } else { + printf("No Python found.\n"); + } + + wchar_t installer_pattern[MAX_PATH]; + int n = _snwprintf(installer_pattern, MAX_PATH, L"%ls\\%ls", python_installer_dir, PYTHON_INSTALLER_FILESPEC); + fail_unless(n >= 0 && n < MAX_PATH, "Could not construct wildcard path to Python installer."); + + WIN32_FIND_DATA find_data; + HANDLE search_handle = FindFirstFileW(installer_pattern, &find_data); + fail_unless(search_handle != INVALID_HANDLE_VALUE, "Could not find the Python installer.") + + wchar_t installer_path[MAX_PATH]; + n = _snwprintf(installer_path, MAX_PATH, L"%ls\\%ls", python_installer_dir, find_data.cFileName); + fail_unless(n >= 0 && n < MAX_PATH, "Could not construct path to Python installer."); + printf("%ls\n", installer_path); + + wchar_t installer_path_quoted[2+MAX_PATH]; + n = _snwprintf(installer_path_quoted, 2+MAX_PATH, L"\"%ls\"", installer_path); + fail_unless(n >= 0 && n < 2+MAX_PATH, "Could not construct quoted path to Python installer."); + printf("%ls\n", installer_path_quoted); + + // + // XXX I'm not sure whether or not we want "/passive". These options + // may silently remove a previous Python installation that was not + // detected by the check above, and we'd like that to prompt. + const wchar_t *python_installer_argv[] = { + L"msiexec", L"/i", installer_path_quoted, + L"/passive", L"/norestart", L"ALLUSERS=1", L"ADDLOCAL=Extensions", NULL + }; + errno = 0; + intptr_t exit_code = _wspawnvp(P_WAIT, python_installer_argv[0], python_installer_argv); + int saved_errno = errno; + + _wunlink(installer_path); // ignore errors + + fail_unless(saved_errno == 0, "Could not execute Python installer."); + fail_unless(exit_code == 0, "Python installer failed."); +} + +void scriptsetup(wchar_t *destination_dir) { + // 12 = length of --addpath="" + wchar_t addpath_option_quoted[12+MAX_PATH]; + int n = _snwprintf(addpath_option_quoted, 12+MAX_PATH, L"--addpath=\"%ls\\%ls\\bin\"", destination_dir, PKGNAME_AND_VERSION); + fail_unless(n >= 0 && n < 12+MAX_PATH, "Could not construct path for bin directory."); + + unsigned char output_buf[10240]; + const wchar_t *scriptsetup_argv[] = { + L"python", L"setup.py", L"scriptsetup", + L"--allusers", addpath_option_quoted, + NULL + }; + bool res = spawn_with_redirect(stdout, output_buf, sizeof(output_buf), &scriptsetup_argv[0]); + puts((char *) output_buf); + fail_unless(res, "Could not set up Python to run the 'tahoe' command."); +} + bool spawn_with_redirect(FILE *redirect, unsigned char *output_buf, size_t output_size, const wchar_t *argv[]) { bool result = false; fail_unless(output_size > 0, "Invalid output_size."); @@ -350,79 +401,10 @@ bool spawn_with_redirect(FILE *redirect, unsigned char *output_buf, size_t outpu return (exit_code == 0); } -void install_python(wchar_t *python_installer_dir) { - printf("Checking for " REQUIRED_PYTHON_VERSION_PREFIX "..\n"); - - unsigned char output_buf[1024]; - const wchar_t *argv[] = { L"python", L"-V", NULL }; - bool res = spawn_with_redirect(stderr, output_buf, sizeof(output_buf), &argv[0]); - if (res) { - printf("Found %s", (char *) output_buf); - if (strncmp((char *) output_buf, REQUIRED_PYTHON_VERSION_PREFIX, strlen(REQUIRED_PYTHON_VERSION_PREFIX)) == 0) { - return; - } else { - printf("but we need a newer version.\n"); - } - } else { - printf("No Python found.\n"); - } - - wchar_t installer_wildcard[] = L"\\python*.msi"; - if (python_installer_dir[wcslen(python_installer_dir)-1] == '\\') { - wcscpy(installer_wildcard, L"*.msi"); - } - wchar_t installer_pattern[MAX_PATH]; - fail_unless(wcslen(python_installer_dir) < MAX_PATH - wcslen(installer_wildcard), - "Could not construct pattern for Python installer.") - wcscpy(installer_pattern, python_installer_dir); - wcscat(installer_pattern, installer_wildcard); - - WIN32_FIND_DATA find_data; - HANDLE search_handle = FindFirstFileW(installer_pattern, &find_data); - fail_unless(search_handle != INVALID_HANDLE_VALUE, - "Could not find the Python installer.") - - fail_unless(wcslen(python_installer_dir) < MAX_PATH - wcslen(find_data.cFileName), - "Could not construct path to Python installer.") - - wchar_t installer_path[MAX_PATH]; - wcscpy(installer_path, python_installer_dir); - wcscat(installer_path, find_data.cFileName); - - // - // "/qb!" works, but it may silently remove a previous Python installation - // that was not detected by the check above, and we want that to prompt. - const wchar_t *python_installer_argv[] = { - L"msiexec", L"/i", installer_path, - // L"/qb!", - L"ALLUSERS=1", L"ADDLOCAL=Extensions", NULL - }; - errno = 0; - intptr_t exit_code = _wspawnvp(P_WAIT, python_installer_argv[0], python_installer_argv); - fail_unless(errno == 0, "Could not execute Python installer."); - fail_unless(exit_code == 0, "Python installer failed."); -} - -void scriptsetup(wchar_t *destination_dir) { - wchar_t bin_dir[MAX_PATH]; - int n = wsnprintf(bin_dir, L"%ls\\%ls\\bin", destination_dir, PKGNAME_AND_VERSION); - fail_unless(n >= 0 && n < MAX_PATH, "Could not construct path for bin directory."); - - unsigned char output_buf[10240]; - const wchar_t *scriptsetup_argv[] = { - L"python", L"setup.py", L"scriptsetup", - L"--allusers", L"--addpaths", bin_dir, - NULL - }; - bool res = spawn_with_redirect(stdout, output_buf, sizeof(output_buf), &scriptsetup_argv[0]); - puts((char *) output_buf); - fail_unless(res, "Could not set up Python to run the 'tahoe' command."); -} - void fail(char *s) { // TODO: show dialog box fprintf(stderr, "%s\n", s); - pause() + pause(); exit(1); } @@ -431,7 +413,6 @@ void warn(char *s) { } void pause() { - printf("Press any key to finish."); - char buf[2]; - fgets(buf, 1, stdin); + printf("Press any key to finish.\n"); + _getch(); } \ No newline at end of file diff --git a/misc/build_helpers/windows/installer/installer/installer.vcproj b/misc/build_helpers/windows/installer/installer/installer.vcproj index a2fb0851..a2699a2b 100644 --- a/misc/build_helpers/windows/installer/installer/installer.vcproj +++ b/misc/build_helpers/windows/installer/installer/installer.vcproj @@ -204,6 +204,10 @@ Filter="h;hpp;hxx;hm;inl;inc;xsd" UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}" > + + diff --git a/misc/build_helpers/windows/installer/installer/stdafx.h b/misc/build_helpers/windows/installer/installer/stdafx.h index 5cd085a4..9f8375b2 100644 --- a/misc/build_helpers/windows/installer/installer/stdafx.h +++ b/misc/build_helpers/windows/installer/installer/stdafx.h @@ -15,6 +15,7 @@ #include #include #include +#include // Turn off the warnings nagging you to use the more complicated *_s // "secure" functions that are actually more difficult to use securely. diff --git a/setuptools-0.6c16dev5.egg/setuptools/command/scriptsetup.py b/setuptools-0.6c16dev5.egg/setuptools/command/scriptsetup.py index db68c07a..e4dd9dd0 100644 --- a/setuptools-0.6c16dev5.egg/setuptools/command/scriptsetup.py +++ b/setuptools-0.6c16dev5.egg/setuptools/command/scriptsetup.py @@ -1,6 +1,6 @@ from distutils.errors import DistutilsSetupError from setuptools import Command -import sys +import sys, os class scriptsetup(Command): action = (sys.platform == "win32" @@ -10,11 +10,14 @@ class scriptsetup(Command): user_options = [ ('allusers', 'a', 'make changes for all users of this Windows installation (requires Administrator privileges)'), + ('addpath=', 'p', + 'add a directory to the PATH (user by default; system if --allusers is specified)'), ] boolean_options = ['allusers'] def initialize_options(self): self.allusers = False + self.addpath = None def finalize_options(self): pass @@ -23,11 +26,13 @@ class scriptsetup(Command): if sys.platform != "win32": print "\n'scriptsetup' isn't needed on non-Windows platforms." else: - do_scriptsetup(self.allusers) + do_scriptsetup(self.allusers, self.addpath) -def do_scriptsetup(allusers=False): +def do_scriptsetup(allusers=False, addpath=None): print "\nSetting up environment to run scripts for %s..." % (allusers and "all users" or "the current user") + if addpath: + print "%r will be added to the PATH." % (addpath,) from _winreg import HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, HKEY_CLASSES_ROOT, \ REG_SZ, REG_EXPAND_SZ, KEY_QUERY_VALUE, KEY_SET_VALUE, \ @@ -241,13 +246,15 @@ def do_scriptsetup(allusers=False): except Exception, e: print "Warning: %r" % (e,) - changed_assoc = associate(".pyscript", "Python.File", allusers) changed_env = False try: changed_env |= add_to_environment("PATHEXT", ".pyscript", allusers) changed_env |= add_to_environment("PATHEXT", ".pyw", allusers) + if addpath: + abs_path = os.path.abspath(os.path.normpath(addpath)) + changed_env |= add_to_environment("PATH", abs_path, allusers) finally: CloseKey(user_env) CloseKey(system_env) -- 2.45.2