diff --git a/direct/src/showutil/FreezeTool.py b/direct/src/showutil/FreezeTool.py index 77f2be1a5a..1ebea45de1 100644 --- a/direct/src/showutil/FreezeTool.py +++ b/direct/src/showutil/FreezeTool.py @@ -1616,7 +1616,10 @@ class Freezer: '-x %s to exclude the entire package.' % (moduleName, moduleName.split('.')[0])) # Build from pre-built binary stub - stub_path = os.path.join(os.path.dirname(ExecutionEnvironment.get_dtool_name()), '..', 'bin', 'deploy-stub') + dtool_path = Filename(ExecutionEnvironment.get_dtool_name()).to_os_specific() + stub_path = os.path.join(os.path.dirname(dtool_path), '..', 'bin', 'deploy-stub') + if sys.platform in ('win32', 'cygwin'): + stub_path += '.exe' with open(stub_path, 'rb') as f: stubbin = f.read() diff --git a/direct/src/showutil/dist.py b/direct/src/showutil/dist.py index dda2e9f3e7..7e1f60c0ee 100644 --- a/direct/src/showutil/dist.py +++ b/direct/src/showutil/dist.py @@ -45,9 +45,9 @@ class build(distutils.command.build.build): freezer.generateRuntimeFromStub(basename) # Copy Panda3D libs - dtool_dir = os.path.dirname(p3d.ExecutionEnvironment.get_dtool_name()) - libdir = os.path.join(dtool_dir, '..', 'lib') - src = os.path.join(libdir, '..', 'panda3d') + dtool_fn = p3d.Filename(p3d.ExecutionEnvironment.get_dtool_name()) + libdir = os.path.dirname(dtool_fn.to_os_specific()) + src = os.path.normpath(os.path.join(libdir, '..', 'panda3d')) dst = os.path.join(builddir, 'panda3d') distutils.dir_util.copy_tree(src, dst) @@ -59,7 +59,7 @@ class build(distutils.command.build.build): distutils.file_util.copy_file(source_path, target_path) # Copy etc - src = os.path.join(dtool_dir, '..', 'etc') + src = os.path.join(libdir, '..', 'etc') dst = os.path.join(builddir, 'etc') distutils.dir_util.copy_tree(src, dst) diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index db99f4a483..8a3ad1d190 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -6358,12 +6358,17 @@ if (PkgSkip("CONTRIB")==0 and not RUNTIME): if True: # TODO OPTS=['DIR:pandatool/src/deploy-stub', 'BUILDING:DEPLOYSTUB', 'PYTHON'] TargetAdd('deploy-stub.obj', opts=OPTS, input='deploy-stub.c') + if GetTarget() == 'windows': + TargetAdd('frozen_dllmain.obj', opts=OPTS, input='frozen_dllmain.c') + if GetTarget() == 'linux': # Setup rpath so libs can be found in the same directory as the deployed game LibName('DEPLOYSTUB', "-Wl,-rpath,\$ORIGIN") LibName('DEPLOYSTUB', "-Wl,-z,origin") TargetAdd('deploy-stub.exe', input='deploy-stub.obj') - TargetAdd('deploy-stub.exe', opts=['PYTHON', 'DEPLOYSTUB']) + if GetTarget() == 'windows': + TargetAdd('deploy-stub.exe', input='frozen_dllmain.obj') + TargetAdd('deploy-stub.exe', opts=['PYTHON', 'DEPLOYSTUB', 'NOICON']) # # Generate the models directory and samples directory diff --git a/pandatool/src/deploy-stub/deploy-stub.c b/pandatool/src/deploy-stub/deploy-stub.c index 0c0215be90..4a08b554ff 100644 --- a/pandatool/src/deploy-stub/deploy-stub.c +++ b/pandatool/src/deploy-stub/deploy-stub.c @@ -20,6 +20,10 @@ extern void PyWinFreeze_ExeInit(void); extern void PyWinFreeze_ExeTerm(void); extern DL_IMPORT(int) PyImport_ExtendInittab(struct _inittab *newtab); + +static struct _inittab extensions[] = { + {0, 0}, +}; #endif static unsigned char *modblob = NULL; @@ -165,7 +169,8 @@ main(int argc, char *argv[]) { for (modidx = 0; modidx < nummods; ++modidx) { struct _frozen *moddef = &_PyImport_FrozenModules[modidx]; char *name = NULL, namebuf[256] = {0}; - unsigned int nsize, codeptr; + size_t nsize; + unsigned int codeptr; int codesize; // Name diff --git a/pandatool/src/deploy-stub/frozen_dllmain.c b/pandatool/src/deploy-stub/frozen_dllmain.c new file mode 100644 index 0000000000..3e374cf558 --- /dev/null +++ b/pandatool/src/deploy-stub/frozen_dllmain.c @@ -0,0 +1,134 @@ +/* FreezeDLLMain.cpp + +This is a DLLMain suitable for frozen applications/DLLs on +a Windows platform. + +The general problem is that many Python extension modules may define +DLL main functions, but when statically linked together to form +a frozen application, this DLLMain symbol exists multiple times. + +The solution is: +* Each module checks for a frozen build, and if so, defines its DLLMain + function as "__declspec(dllexport) DllMain%module%" + (eg, DllMainpythoncom, or DllMainpywintypes) + +* The frozen .EXE/.DLL links against this module, which provides + the single DllMain. + +* This DllMain attempts to locate and call the DllMain for each + of the extension modules. + +* This code also has hooks to "simulate" DllMain when used from + a frozen .EXE. + +At this stage, there is a static table of "possibly embedded modules". +This should change to something better, but it will work OK for now. + +Note that this scheme does not handle dependencies in the order +of DllMain calls - except it does call pywintypes first :-) + +As an example of how an extension module with a DllMain should be +changed, here is a snippet from the pythoncom extension module. + + // end of example code from pythoncom's DllMain.cpp + #ifndef BUILD_FREEZE + #define DLLMAIN DllMain + #define DLLMAIN_DECL + #else + #define DLLMAIN DllMainpythoncom + #define DLLMAIN_DECL __declspec(dllexport) + #endif + + extern "C" DLLMAIN_DECL + BOOL WINAPI DLLMAIN(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) + // end of example code from pythoncom's DllMain.cpp + +***************************************************************************/ +#include "windows.h" + +static char *possibleModules[] = { + "pywintypes", + "pythoncom", + "win32ui", + NULL, +}; + +BOOL CallModuleDllMain(char *modName, DWORD dwReason); + + +/* + Called by a frozen .EXE only, so that built-in extension + modules are initialized correctly +*/ +void PyWinFreeze_ExeInit(void) +{ + char **modName; + for (modName = possibleModules;*modName;*modName++) { +/* printf("Initialising '%s'\n", *modName); */ + CallModuleDllMain(*modName, DLL_PROCESS_ATTACH); + } +} + +/* + Called by a frozen .EXE only, so that built-in extension + modules are cleaned up +*/ +void PyWinFreeze_ExeTerm(void) +{ + // Must go backwards + char **modName; + for (modName = possibleModules+(sizeof(possibleModules) / sizeof(char *))-2; + modName >= possibleModules; + *modName--) { +/* printf("Terminating '%s'\n", *modName);*/ + CallModuleDllMain(*modName, DLL_PROCESS_DETACH); + } +} + +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +{ + BOOL ret = TRUE; + switch (dwReason) { + case DLL_PROCESS_ATTACH: + { + char **modName; + for (modName = possibleModules;*modName;*modName++) { + BOOL ok = CallModuleDllMain(*modName, dwReason); + if (!ok) + ret = FALSE; + } + break; + } + case DLL_PROCESS_DETACH: + { + // Must go backwards + char **modName; + for (modName = possibleModules+(sizeof(possibleModules) / sizeof(char *))-2; + modName >= possibleModules; + *modName--) + CallModuleDllMain(*modName, DLL_PROCESS_DETACH); + break; + } + } + return ret; +} + +BOOL CallModuleDllMain(char *modName, DWORD dwReason) +{ + BOOL (WINAPI * pfndllmain)(HINSTANCE, DWORD, LPVOID); + + char funcName[255]; + HMODULE hmod = GetModuleHandleW(NULL); + strcpy(funcName, "_DllMain"); + strcat(funcName, modName); + strcat(funcName, "@12"); // stdcall convention. + pfndllmain = (BOOL (WINAPI *)(HINSTANCE, DWORD, LPVOID))GetProcAddress(hmod, funcName); + if (pfndllmain==NULL) { + /* No function by that name exported - then that module does + not appear in our frozen program - return OK + */ + return TRUE; + } + return (*pfndllmain)(hmod, dwReason, NULL); +} +