Merge pull request #800 from andkit/portable-improvements

Portable improvements
This commit is contained in:
Oskar Hahn 2013-07-06 23:57:00 -07:00
commit aca2570cb7
5 changed files with 170 additions and 22 deletions

View File

@ -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 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 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 in the 'dist' directory

View File

@ -12,6 +12,17 @@
#include <Python.h> #include <Python.h>
#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 = static const char *run_openslides_code =
"import openslides_gui.gui;" "import openslides_gui.gui;"
"openslides_gui.gui.main()"; "openslides_gui.gui.main()";
@ -71,14 +82,94 @@ _get_module_name()
return NULL; 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 static int
_run() _run()
{ {
if (PyRun_SimpleString(run_openslides_code) != 0) if (py_run_simple_string_flags(run_openslides_code, NULL) != 0)
{ _fatal_error("Failed to execute openslides");
fprintf(stderr, "ERROR: failed to execute openslides\n");
return 1;
}
return 0; return 0;
} }
@ -91,33 +182,33 @@ WinMain(HINSTANCE inst, HINSTANCE prev_inst, LPSTR cmdline, int show)
int run_py_main = __argc > 1; int run_py_main = __argc > 1;
char *py_home, *sep = NULL; char *py_home, *sep = NULL;
Py_SetProgramName(__argv[0]);
py_home = _get_module_name(); 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 */ /* should always be the true */
if (sep) if (sep)
{
*sep = '\0'; *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) if (run_py_main)
{ {
/* we where given extra arguments, behave like python.exe */ /* we where given extra arguments, behave like python.exe */
returncode = Py_Main(__argc, __argv); returncode = py_main(__argc, __argv);
} }
else else
{ {
/* no arguments given => start openslides gui */ /* no arguments given => start openslides gui */
Py_Initialize(); py_initialize();
PySys_SetArgvEx(__argc, __argv, 0); py_sys_set_argv_ex(__argc, __argv, 0);
returncode = _run(); returncode = _run();
Py_Finalize(); py_finalize();
} }
free(py_home); free(py_home);

Binary file not shown.

48
extras/win32-portable/prepare_portable.py Normal file → Executable file
View File

@ -318,7 +318,7 @@ def compile_openslides_launcher():
return False return False
cc.add_include_dir(distutils.sysconfig.get_python_inc()) cc.add_include_dir(distutils.sysconfig.get_python_inc())
cc.add_library_dir(os.path.join(sys.exec_prefix, "Libs")) cc.define_macro("_CRT_SECURE_NO_WARNINGS")
gui_data_dir = os.path.dirname(openslides_gui.__file__) gui_data_dir = os.path.dirname(openslides_gui.__file__)
gui_data_dir = os.path.join(gui_data_dir, "data") gui_data_dir = os.path.join(gui_data_dir, "data")
@ -343,11 +343,52 @@ def compile_openslides_launcher():
]) ])
cc.link_executable( cc.link_executable(
objs, "extras/win32-portable/openslides", objs, "extras/win32-portable/openslides",
extra_preargs=["/subsystem:windows"], extra_preargs=["/subsystem:windows", "/nodefaultlib:python27.lib"],
libraries=["user32"]
) )
return True 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): def copy_dlls(odir):
dll_src = os.path.join(sys.exec_prefix, "DLLs") dll_src = os.path.join(sys.exec_prefix, "DLLs")
dll_dest = os.path.join(odir, "DLLs") dll_dest = os.path.join(odir, "DLLs")
@ -361,7 +402,7 @@ def copy_dlls(odir):
pydllname = "python{0}{1}.dll".format(*sys.version_info[:2]) pydllname = "python{0}{1}.dll".format(*sys.version_info[:2])
src = os.path.join(os.environ["WINDIR"], "System32", pydllname) src = os.path.join(os.environ["WINDIR"], "System32", pydllname)
dest = os.path.join(odir, pydllname) dest = os.path.join(dll_dest, pydllname)
shutil.copyfile(src, dest) shutil.copyfile(src, dest)
@ -442,6 +483,7 @@ def main():
if not compile_openslides_launcher(): if not compile_openslides_launcher():
sys.stdout.write("Using prebuild openslides.exe\n") sys.stdout.write("Using prebuild openslides.exe\n")
openslides_launcher_update_version_resource()
shutil.copyfile( shutil.copyfile(
"extras/win32-portable/openslides.exe", "extras/win32-portable/openslides.exe",

View File

@ -349,6 +349,14 @@ def get_user_data_path(*args):
def is_portable(): 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() exename = os.path.basename(sys.executable).lower()
return exename == "openslides.exe" return exename == "openslides.exe"