From 5ba09ddb937eef2a5d09387f303d11c88df11069 Mon Sep 17 00:00:00 2001 From: Andy Kittner Date: Sat, 29 Jun 2013 14:31:11 +0200 Subject: [PATCH 1/3] Implement #791: dynamically load python27.dll from DLLs/ directory --- extras/win32-portable/openslides.c | 125 +++++++++++++++++++--- extras/win32-portable/openslides.exe | Bin 31232 -> 31744 bytes extras/win32-portable/prepare_portable.py | 7 +- 3 files changed, 112 insertions(+), 20 deletions(-) diff --git a/extras/win32-portable/openslides.c b/extras/win32-portable/openslides.c index 7e6abdcc4..e69d9824b 100644 --- a/extras/win32-portable/openslides.c +++ b/extras/win32-portable/openslides.c @@ -12,6 +12,17 @@ #include +#define PYTHON_DLL_PATH "\\Dlls\\python27.dll" + +static void (*py_initialize)(void) = 0; +static void (*py_finalize)(void) = 0; +static void (*py_set_program_name)(char *) = 0; +static void (*py_set_python_home)(char *) = 0; +static int (*py_run_simple_string_flags)(const char *, PyCompilerFlags *) = 0; +static void (*py_sys_set_argv_ex)(int, char **, int) = 0; +static int (*py_main)(int, char **) = 0; +static int *py_ignore_environment_flag = 0; + static const char *run_openslides_code = "import openslides_gui.gui;" "openslides_gui.gui.main()"; @@ -71,14 +82,94 @@ _get_module_name() return NULL; } +static void +_fatal_error(const char *text) +{ + MessageBoxA(NULL, text, "Fatal error", MB_OK | MB_ICONERROR); + exit(1); +} + +static void +_fatal_error_fmt(const char *fmt, ...) +{ + int size = 512; + char *buf = malloc(size); + va_list args; + int bytes_written; + + if (!buf) + abort(); + + va_start(args, fmt); + for (;;) + { + bytes_written = vsnprintf(buf, size, fmt, args); + if (bytes_written > -1 && bytes_written < size) + break; + else if (bytes_written > size) + size = bytes_written + 1; + else + size *= 2; + + buf = realloc(buf, size); + if (!buf) + abort(); + } + va_end(args); + + _fatal_error(buf); +} + +static void * +_load_func(HMODULE module, const char *name) +{ + void *address = GetProcAddress(module, name); + if (!address) + _fatal_error_fmt("Failed to look up symbol %s", name); + return address; +} + +static void +_load_python(const char *pyhome) +{ + size_t pyhome_len = strlen(pyhome); + size_t size = pyhome_len + strlen(PYTHON_DLL_PATH) + 1; + char *buf = malloc(size); + HMODULE py_dll; + + if (!buf) + abort(); + memcpy(buf, pyhome, pyhome_len); + memcpy(buf + pyhome_len, PYTHON_DLL_PATH, sizeof(PYTHON_DLL_PATH)); + buf[size - 1] = '\0'; + + py_dll = LoadLibrary(buf); + if (!py_dll) + { + DWORD error = GetLastError(); + _fatal_error_fmt("Failed to load %s (error %d)", buf, error); + } + + py_initialize = (void (*)(void))_load_func(py_dll, "Py_Initialize"); + py_finalize = (void (*)(void))_load_func(py_dll, "Py_Finalize"); + py_set_program_name = (void (*)(char *)) + _load_func(py_dll, "Py_SetProgramName"); + py_set_python_home = (void (*)(char *)) + _load_func(py_dll, "Py_SetPythonHome"); + py_run_simple_string_flags = (int (*)(const char *, PyCompilerFlags *)) + _load_func(py_dll, "PyRun_SimpleStringFlags"); + py_sys_set_argv_ex = (void (*)(int, char **, int)) + _load_func(py_dll, "PySys_SetArgvEx"); + py_main = (int (*)(int, char **))_load_func(py_dll, "Py_Main"); + py_ignore_environment_flag = (int *) + _load_func(py_dll, "Py_IgnoreEnvironmentFlag"); +} + static int _run() { - if (PyRun_SimpleString(run_openslides_code) != 0) - { - fprintf(stderr, "ERROR: failed to execute openslides\n"); - return 1; - } + if (py_run_simple_string_flags(run_openslides_code, NULL) != 0) + _fatal_error("Failed to execute openslides"); return 0; } @@ -91,33 +182,33 @@ WinMain(HINSTANCE inst, HINSTANCE prev_inst, LPSTR cmdline, int show) int run_py_main = __argc > 1; char *py_home, *sep = NULL; - Py_SetProgramName(__argv[0]); - py_home = _get_module_name(); + if (!py_home) + _fatal_error("Could not determine portable root directory"); - if (py_home) - sep = strrchr(py_home, '\\'); + sep = strrchr(py_home, '\\'); /* should always be the true */ if (sep) - { *sep = '\0'; - Py_SetPythonHome(py_home); - Py_IgnoreEnvironmentFlag = 1; - } + + _load_python(py_home); + py_set_program_name(__argv[0]); + py_set_python_home(py_home); + *py_ignore_environment_flag = 1; if (run_py_main) { /* we where given extra arguments, behave like python.exe */ - returncode = Py_Main(__argc, __argv); + returncode = py_main(__argc, __argv); } else { /* no arguments given => start openslides gui */ - Py_Initialize(); - PySys_SetArgvEx(__argc, __argv, 0); + py_initialize(); + py_sys_set_argv_ex(__argc, __argv, 0); returncode = _run(); - Py_Finalize(); + py_finalize(); } free(py_home); diff --git a/extras/win32-portable/openslides.exe b/extras/win32-portable/openslides.exe index 812eabdc0cf2064e4ac60720fa1031ec9fdd55fe..7d1bd450fe74ab330c2696a45f2a87f949bfcba8 100755 GIT binary patch delta 3692 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 delta 2876 zcmd^B|4$p|8Gr9Mk^>Alu@4+bTJx>VB9gcmvNQ>r3vo-^v@_5EUILDnIGinFGIB>? zYv*(>TFDhPecm%v z`C1S9QsT- z)ZP{$+TW>EtM3zBv0E_3r-eXQr#p4ZKNZksx;mk$bQlI-t9;nyAyi5+uaw6}rD?3% zsFVq_Z-pgl;T9pVubBD7p_KBFto)Z)G%?AczbSWWYHn^W2CzrVzllIi8i#bJ;*WD} zh3}@?wRMfx#AYhZn^^_p(|=Q{t>P44i=0Lmzx=A$uC^J1KJS=G?qG zttM)cS%hy!pNpB$Sev%Kbs9mN4Rn2IMVd5o&o_g1B+M8wLR!$r5;I7d!?tW4){etI z{J#$S8`i@z@1|5T5d78XHjAAk@*_naVr<8CCeoPKtWY_}##XlonPf}(785BZCLySF z+-%*Ryp7#Z>LoEE8hf$`@lQoMkT#9{&;9()15BBkJhn+AqGL|5pUWz;$89~POI~R&E>d-gE@Vca zrs+a_+HMYIbAkqb=7{E9G|JJ>6np#h21{h84@oi~^On@dE$`SR4B}h@5yTf?5nJ2Z z6E|IYF#p}cGpuOUIWtO@9C~3vDGyV1!L!?ocHu?C`uhcqjG8JaJ2Z~zEuU!{@vh^_ ztq9W84Y!JK*N(M&l}e6Hz1!3y-c|S=(@P2~VmA@``ND7=OVJfL_0bfqdIe6j>Qz&| zX3A@jNAsDh%Kvlfz773i`fr=I_e?nksnL_#%F8QzF{b4DV|Xj_*O-)6ncTC&WSzle z{SxH)*CcC=CkCA_)UfTIw8>^KnyTuqsrKK2di(Wk5iXjSBsZ|tyh4&-KjIaW7RC>y zHz=LWZi0=&zXUTTH_+{D_9|qKKvJ{WYgnz2a02K827s%;j{yO`UsMpX3j7856EF|V0M}t-&luL%00oEu-vXL| zBY+dwdhTM3gp_8sJU-Hc5+jftvu4-Qgg@^`3)tk(lx40#a*UQ0=>sG0Qg?crqLf_JqG1B5%U6b?{Q4HKaVz z*WK?OARWy9bTq7lGaj96|6DK}B_~ZwceJ-Jd}9C4o4gQcYc`@f<7v(nCqajTV^5O?=*`SUy>O9FU2 zw Date: Sat, 29 Jun 2013 15:15:32 +0200 Subject: [PATCH 2/3] Update version resource with pywin32 when no compiler is available --- extras/win32-portable/create_portable.txt | 11 ++++-- extras/win32-portable/prepare_portable.py | 43 ++++++++++++++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) mode change 100644 => 100755 extras/win32-portable/prepare_portable.py diff --git a/extras/win32-portable/create_portable.txt b/extras/win32-portable/create_portable.txt index 4cad67913..dda2949a4 100644 --- a/extras/win32-portable/create_portable.txt +++ b/extras/win32-portable/create_portable.txt @@ -6,11 +6,18 @@ How to create a new portable Windows distribution of OpenSlides: easy_install -Z django django-mptt beautifulsoup4 bleach pillow qrcode reportlab tornado -2.) Run in the main directory of the OpenSlides checkout: +2.) To update the version resource of the prebuild openslides.exe + pywin32 should be installed (it is not strictly required but at + least for releases that are to be published it is highly advisable) + + To install it just grab the binary installer from: + http://sourceforge.net/projects/pywin32/files/pywin32/Build%20218/pywin32-218.win32-py2.7.exe/download + +3.) Run in the main directory of the OpenSlides checkout: python extras\win32-portable\prepare_portable.py -3.) The portable OpenSlides distribution is now ready as a zip archive +4.) The portable OpenSlides distribution is now ready as a zip archive in the 'dist' directory diff --git a/extras/win32-portable/prepare_portable.py b/extras/win32-portable/prepare_portable.py old mode 100644 new mode 100755 index 41d81c9e1..593e5b051 --- a/extras/win32-portable/prepare_portable.py +++ b/extras/win32-portable/prepare_portable.py @@ -344,11 +344,51 @@ def compile_openslides_launcher(): cc.link_executable( objs, "extras/win32-portable/openslides", extra_preargs=["/subsystem:windows", "/nodefaultlib:python27.lib"], - libraries = ["user32"] + libraries=["user32"] ) return True +def openslides_launcher_update_version_resource(): + try: + import win32api + import win32verstamp + except ImportError: + sys.stderr.write( + "Using precompiled executable and pywin32 is not available - " + "version resource may be out of date!\n") + return False + import struct + + sys.stdout.write("Updating version resource") + # code based on win32verstamp.stamp() with some minor differences in + # version handling + major, minor, sub = openslides.VERSION[:3] + build = openslides.VERSION[4] + pre_release = openslides.VERSION[3] != "final" + version_str = openslides.get_version() + + sdata = { + "CompanyName": "OpenSlides team", + "FileDescription": "OpenSlides", + "FileVersion": version_str, + "InternalName": "OpenSlides", + "LegalCopyright": u"Copyright \xa9 2011-2013", + "OriginalFilename": "openslides.exe", + "ProductName": "OpenSlides", + "ProductVersion": version_str, + } + vdata = { + "Translation": struct.pack("hh", 0x409, 0x4e4), + } + + vs = win32verstamp.VS_VERSION_INFO( + major, minor, sub, build, sdata, vdata, pre_release, False) + h = win32api.BeginUpdateResource("extras/win32-portable/openslides.exe", 0) + win32api.UpdateResource(h, 16, 1, vs) + win32api.EndUpdateResource(h, 0) + + def copy_dlls(odir): dll_src = os.path.join(sys.exec_prefix, "DLLs") dll_dest = os.path.join(odir, "DLLs") @@ -443,6 +483,7 @@ def main(): if not compile_openslides_launcher(): sys.stdout.write("Using prebuild openslides.exe\n") + openslides_launcher_update_version_resource() shutil.copyfile( "extras/win32-portable/openslides.exe", From b568546fb8554120673963e98098757534c7f826 Mon Sep 17 00:00:00 2001 From: Andy Kittner Date: Sat, 29 Jun 2013 15:21:58 +0200 Subject: [PATCH 3/3] Add some explanation to is_portable(); fixes #729 --- openslides/main.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/openslides/main.py b/openslides/main.py index e4cd32b61..0d609b641 100644 --- a/openslides/main.py +++ b/openslides/main.py @@ -349,6 +349,14 @@ def get_user_data_path(*args): def is_portable(): + """Return True if openslides is run as portable version""" + + # NOTE: sys.executable is the path of the *interpreter* + # the portable version embeds python so it *is* the interpreter. + # The wrappers generated by pip and co. will spawn + # the usual python(w).exe, so there is no danger of mistaking + # them for the portable even though they may also be called + # openslides.exe exename = os.path.basename(sys.executable).lower() return exename == "openslides.exe"