mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-03 10:22:45 -04:00
freezetool
This commit is contained in:
parent
e402dd2e43
commit
30c07c2714
722
direct/src/showutil/FreezeTool.py
Normal file
722
direct/src/showutil/FreezeTool.py
Normal file
@ -0,0 +1,722 @@
|
|||||||
|
""" This module contains code to freeze a number of Python modules
|
||||||
|
into a single (mostly) standalone DLL or EXE. """
|
||||||
|
|
||||||
|
import modulefinder
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import marshal
|
||||||
|
import imp
|
||||||
|
|
||||||
|
import direct
|
||||||
|
from pandac.PandaModules import *
|
||||||
|
|
||||||
|
# These are modules that Python always tries to import up-front. They
|
||||||
|
# must be frozen in any main.exe.
|
||||||
|
startupModules = [
|
||||||
|
'site', 'sitecustomize', 'os', 'encodings.cp1252',
|
||||||
|
'org',
|
||||||
|
]
|
||||||
|
|
||||||
|
# Our own Python source trees to watch out for.
|
||||||
|
sourceTrees = ['direct']
|
||||||
|
|
||||||
|
# The command to compile a c to an object file. Replace %(basename)s
|
||||||
|
# with the basename of the source file, and an implicit .c extension.
|
||||||
|
compileObj = 'error'
|
||||||
|
|
||||||
|
# The command to link a single object file into an executable. As
|
||||||
|
# above, replace $(basename)s with the basename of the original source
|
||||||
|
# file, and of the target executable.
|
||||||
|
linkExe = 'error'
|
||||||
|
|
||||||
|
# The command to link a single object file into a shared library.
|
||||||
|
linkDll = 'error'
|
||||||
|
|
||||||
|
# The root directory of the Python installation
|
||||||
|
Python = None
|
||||||
|
|
||||||
|
# The directory that includes Python.h.
|
||||||
|
PythonIPath = '/Developer/SDKs/MacOSX10.5.sdk/System/Library/Frameworks/Python.framework/Versions/2.5/include/python2.5'
|
||||||
|
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
compileObj = "cl /wd4996 /Fo%(basename)s.obj /nologo /c /MD /Zi /O2 /Ob2 /EHsc /Zm300 /W3 %(filename)s"
|
||||||
|
linkExe = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /STACK:4194304 /INCREMENTAL:NO /out:%(basename)s.exe; mt -manifest %(basename)s.manifest -outputresource:%(basename)s.exe;2'
|
||||||
|
linkDll = 'link /nologo /MAP:NUL /FIXED:NO /OPT:REF /INCREMENTAL:NO /out:%(basename)s.dll; mt -manifest %(basename)s.manifest -outputresource:%(basename)s.dll;1'
|
||||||
|
|
||||||
|
elif sys.platform == 'darwin':
|
||||||
|
# OSX
|
||||||
|
compileObj = "gcc -fPIC -c -o %(basename)s.o -O2 -arch i386 -arch ppc -I %(pythonIPath)s %(filename)s"
|
||||||
|
linkExe = "gcc -o %(basename)s %(basename)s.o -framework Python"
|
||||||
|
linkDll = "gcc -shared -o %(basename)s.so %(basename)s.o -framework Python"
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Linux
|
||||||
|
compileObj = "gcc -fPIC -c -o %(basename)s.o -O2 %(filename)s"
|
||||||
|
linkExe = "gcc -o %(basename)s %(basename)s.o"
|
||||||
|
linkDll = "gcc -shared -o %(basename)s.so %(basename)s.o"
|
||||||
|
|
||||||
|
# The code from frozenmain.c in the Python source repository.
|
||||||
|
frozenMainCode = """
|
||||||
|
/* Python interpreter main program for frozen scripts */
|
||||||
|
|
||||||
|
#include "Python.h"
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
extern void PyWinFreeze_ExeInit(void);
|
||||||
|
extern void PyWinFreeze_ExeTerm(void);
|
||||||
|
extern int PyInitFrozenExtensions(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Main program */
|
||||||
|
|
||||||
|
int
|
||||||
|
Py_FrozenMain(int argc, char **argv)
|
||||||
|
{
|
||||||
|
char *p;
|
||||||
|
int n, sts;
|
||||||
|
int inspect = 0;
|
||||||
|
int unbuffered = 0;
|
||||||
|
|
||||||
|
Py_FrozenFlag = 1; /* Suppress errors from getpath.c */
|
||||||
|
|
||||||
|
if ((p = Py_GETENV("PYTHONINSPECT")) && *p != '\\0')
|
||||||
|
inspect = 1;
|
||||||
|
if ((p = Py_GETENV("PYTHONUNBUFFERED")) && *p != '\\0')
|
||||||
|
unbuffered = 1;
|
||||||
|
|
||||||
|
if (unbuffered) {
|
||||||
|
setbuf(stdin, (char *)NULL);
|
||||||
|
setbuf(stdout, (char *)NULL);
|
||||||
|
setbuf(stderr, (char *)NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
PyInitFrozenExtensions();
|
||||||
|
#endif /* MS_WINDOWS */
|
||||||
|
Py_SetProgramName(argv[0]);
|
||||||
|
Py_Initialize();
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
PyWinFreeze_ExeInit();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (Py_VerboseFlag)
|
||||||
|
fprintf(stderr, "Python %s\\n%s\\n",
|
||||||
|
Py_GetVersion(), Py_GetCopyright());
|
||||||
|
|
||||||
|
PySys_SetArgv(argc, argv);
|
||||||
|
|
||||||
|
n = PyImport_ImportFrozenModule("__main__");
|
||||||
|
if (n == 0)
|
||||||
|
Py_FatalError("__main__ not frozen");
|
||||||
|
if (n < 0) {
|
||||||
|
PyErr_Print();
|
||||||
|
sts = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
sts = 0;
|
||||||
|
|
||||||
|
if (inspect && isatty((int)fileno(stdin)))
|
||||||
|
sts = PyRun_AnyFile(stdin, "<stdin>") != 0;
|
||||||
|
|
||||||
|
#ifdef MS_WINDOWS
|
||||||
|
PyWinFreeze_ExeTerm();
|
||||||
|
#endif
|
||||||
|
Py_Finalize();
|
||||||
|
return sts;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
# The code from frozen_dllmain.c in the Python source repository.
|
||||||
|
# Windows only.
|
||||||
|
frozenDllMainCode = """
|
||||||
|
#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 = GetModuleHandle(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);
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Our own glue code to start up a Python executable.
|
||||||
|
mainInitCode = """
|
||||||
|
%(frozenMainCode)s
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[]) {
|
||||||
|
PyImport_FrozenModules = _PyImport_FrozenModules;
|
||||||
|
return Py_FrozenMain(argc, argv);
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Our own glue code to start up a Python shared library.
|
||||||
|
dllInitCode = """
|
||||||
|
static PyMethodDef nullMethods[] = {
|
||||||
|
{NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
%(dllexport)svoid init%(moduleName)s() {
|
||||||
|
int count;
|
||||||
|
struct _frozen *new_FrozenModules;
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
while (PyImport_FrozenModules[count].name != NULL) {
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
new_FrozenModules = (struct _frozen *)malloc((count + %(newcount)s + 1) * sizeof(struct _frozen));
|
||||||
|
memcpy(new_FrozenModules, _PyImport_FrozenModules, %(newcount)s * sizeof(struct _frozen));
|
||||||
|
memcpy(new_FrozenModules + %(newcount)s, PyImport_FrozenModules, count * sizeof(struct _frozen));
|
||||||
|
memset(new_FrozenModules + count + %(newcount)s, 0, sizeof(struct _frozen));
|
||||||
|
|
||||||
|
PyImport_FrozenModules = new_FrozenModules;
|
||||||
|
|
||||||
|
Py_InitModule("%(moduleName)s", nullMethods);
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
programFile = """
|
||||||
|
#include "Python.h"
|
||||||
|
|
||||||
|
%(moduleDefs)s
|
||||||
|
|
||||||
|
static struct _frozen _PyImport_FrozenModules[] = {
|
||||||
|
%(moduleList)s
|
||||||
|
{NULL, NULL, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
%(initCode)s
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Windows needs this bit.
|
||||||
|
frozenExtensions = """
|
||||||
|
|
||||||
|
static struct _inittab extensions[] = {
|
||||||
|
/* Sentinel */
|
||||||
|
{0, 0}
|
||||||
|
};
|
||||||
|
extern DL_IMPORT(int) PyImport_ExtendInittab(struct _inittab *newtab);
|
||||||
|
|
||||||
|
int PyInitFrozenExtensions()
|
||||||
|
{
|
||||||
|
return PyImport_ExtendInittab(extensions);
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
okMissing = [
|
||||||
|
'Carbon.Folder', 'Carbon.Folders', 'HouseGlobals', 'Carbon.File',
|
||||||
|
'MacOS', '_emx_link', 'ce', 'mac', 'org.python.core', 'os.path',
|
||||||
|
'os2', 'posix', 'pwd', 'readline', 'riscos', 'riscosenviron',
|
||||||
|
'riscospath', 'dbm', 'fcntl', 'win32api',
|
||||||
|
'_winreg', 'ctypes', 'ctypes.wintypes', 'nt','msvcrt',
|
||||||
|
'EasyDialogs', 'SOCKS', 'ic', 'rourl2path', 'termios',
|
||||||
|
'OverrideFrom23._Res', 'email', 'email.Utils', 'email.Generator',
|
||||||
|
'email.Iterators', '_subprocess', 'gestalt',
|
||||||
|
'direct.extensions_native.extensions_darwin',
|
||||||
|
]
|
||||||
|
|
||||||
|
class Freezer:
|
||||||
|
# Module tokens:
|
||||||
|
MTAuto = 0
|
||||||
|
MTInclude = 1
|
||||||
|
MTExclude = 2
|
||||||
|
MTForbid = 3
|
||||||
|
|
||||||
|
def __init__(self, previous = None, debugLevel = 0):
|
||||||
|
# Normally, we are freezing for our own platform. Change this
|
||||||
|
# if untrue.
|
||||||
|
self.platform = sys.platform
|
||||||
|
|
||||||
|
# You will also need to change these for a cross-compiler
|
||||||
|
# situation.
|
||||||
|
self.compileObj = compileObj
|
||||||
|
self.linkExe = linkExe
|
||||||
|
self.linkDll = linkDll
|
||||||
|
|
||||||
|
# The filename extension to append to the source file before
|
||||||
|
# compiling.
|
||||||
|
self.sourceExtension = '.c'
|
||||||
|
|
||||||
|
# The filename extension to append to the object file.
|
||||||
|
self.objectExtension = '.o'
|
||||||
|
if self.platform == 'win32':
|
||||||
|
self.objectExtension = '.obj'
|
||||||
|
|
||||||
|
# True to compile to an executable, false to compile to a dll. If
|
||||||
|
# setMain() is called, this is automatically set to True.
|
||||||
|
self.compileToExe = False
|
||||||
|
|
||||||
|
# Change any of these to change the generated startup and glue
|
||||||
|
# code.
|
||||||
|
self.frozenMainCode = frozenMainCode
|
||||||
|
self.frozenDllMainCode = frozenDllMainCode
|
||||||
|
self.mainInitCode = mainInitCode
|
||||||
|
self.frozenExtensions = frozenExtensions
|
||||||
|
|
||||||
|
|
||||||
|
# End of public interface. These remaining members should not
|
||||||
|
# be directly manipulated by callers.
|
||||||
|
self.previousModules = {}
|
||||||
|
self.modules = {}
|
||||||
|
|
||||||
|
if previous:
|
||||||
|
self.previousModules = dict(previous.modules)
|
||||||
|
self.modules = dict(previous.modules)
|
||||||
|
|
||||||
|
self.mainModule = None
|
||||||
|
self.mf = None
|
||||||
|
|
||||||
|
# Make sure we know how to find "direct".
|
||||||
|
if direct.__path__:
|
||||||
|
modulefinder.AddPackagePath('direct', direct.__path__[0])
|
||||||
|
|
||||||
|
def excludeModule(self, moduleName, forbid = False):
|
||||||
|
""" Adds a module to the list of modules not to be exported by
|
||||||
|
this tool. If forbid is true, the module is furthermore
|
||||||
|
forbidden to be imported, even if it exists on disk. """
|
||||||
|
|
||||||
|
if forbid:
|
||||||
|
self.modules[moduleName] = self.MTForbid
|
||||||
|
else:
|
||||||
|
self.modules[moduleName] = self.MTExclude
|
||||||
|
|
||||||
|
def getModulePath(self, moduleName):
|
||||||
|
""" Looks for the indicated directory module and returns its
|
||||||
|
__path__ member: the list of directories in which its python
|
||||||
|
files can be found. If the module is a .py file and not a
|
||||||
|
directory, returns None. """
|
||||||
|
|
||||||
|
# First, try to import the module directly. That's the most
|
||||||
|
# reliable answer, if it works.
|
||||||
|
try:
|
||||||
|
module = __import__(moduleName)
|
||||||
|
except:
|
||||||
|
module = None
|
||||||
|
|
||||||
|
if module != None:
|
||||||
|
for symbol in moduleName.split('.')[1:]:
|
||||||
|
module = getattr(module, symbol)
|
||||||
|
return module.__path__
|
||||||
|
|
||||||
|
# If it didn't work--maybe the module is unimportable because
|
||||||
|
# it makes certain assumptions about the builtins, or
|
||||||
|
# whatever--then just look for file on disk. That's usually
|
||||||
|
# good enough.
|
||||||
|
path = None
|
||||||
|
baseName = moduleName
|
||||||
|
if '.' in baseName:
|
||||||
|
parentName, baseName = moduleName.rsplit('.', 1)
|
||||||
|
path = self.getModulePath(parentName)
|
||||||
|
if path == None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
file, pathname, description = imp.find_module(baseName, path)
|
||||||
|
|
||||||
|
if os.path.isdir(pathname):
|
||||||
|
return [pathname]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def addModule(self, moduleName, implicit = False):
|
||||||
|
""" Adds a module to the list of modules to be exported by
|
||||||
|
this tool. If implicit is true, it is OK if the module does
|
||||||
|
not actually exist.
|
||||||
|
|
||||||
|
The module name may end in ".*", which means to add all of the
|
||||||
|
.py files (other than __init__.py) in a particular directory.
|
||||||
|
It may also end in ".*.*", which means to cycle through all
|
||||||
|
directories within a particular directory.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if implicit:
|
||||||
|
token = self.MTAuto
|
||||||
|
else:
|
||||||
|
token = self.MTInclude
|
||||||
|
|
||||||
|
if moduleName.endswith('.*'):
|
||||||
|
# Find the parent module, so we can get its directory.
|
||||||
|
parentName = moduleName[:-2]
|
||||||
|
parentNames = [parentName]
|
||||||
|
|
||||||
|
if parentName.endswith('.*'):
|
||||||
|
# Another special case. The parent name "*" means to
|
||||||
|
# return all possible directories within a particular
|
||||||
|
# directory.
|
||||||
|
|
||||||
|
topName = parentName[:-2]
|
||||||
|
parentNames = []
|
||||||
|
for dirname in self.getModulePath(topName):
|
||||||
|
for filename in os.listdir(dirname):
|
||||||
|
if os.path.exists(os.path.join(dirname, filename, '__init__.py')):
|
||||||
|
parentName = '%s.%s' % (topName, filename)
|
||||||
|
if self.getModulePath(parentName):
|
||||||
|
parentNames.append(parentName)
|
||||||
|
|
||||||
|
for parentName in parentNames:
|
||||||
|
path = self.getModulePath(parentName)
|
||||||
|
|
||||||
|
if path == None:
|
||||||
|
# It's actually a regular module.
|
||||||
|
self.modules[parentName] = token
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Now get all the py files in the parent directory.
|
||||||
|
for dirname in path:
|
||||||
|
for filename in os.listdir(dirname):
|
||||||
|
if '-' in filename:
|
||||||
|
continue
|
||||||
|
if filename.endswith('.py') and filename != '__init__.py':
|
||||||
|
moduleName = '%s.%s' % (parentName, filename[:-3])
|
||||||
|
self.modules[moduleName] = token
|
||||||
|
else:
|
||||||
|
# A normal, explicit module name.
|
||||||
|
self.modules[moduleName] = token
|
||||||
|
|
||||||
|
def setMain(self, moduleName):
|
||||||
|
self.addModule(moduleName)
|
||||||
|
self.mainModule = moduleName
|
||||||
|
self.compileToExe = True
|
||||||
|
|
||||||
|
def done(self):
|
||||||
|
assert self.mf == None
|
||||||
|
|
||||||
|
if self.compileToExe:
|
||||||
|
# Ensure that each of our required startup modules is
|
||||||
|
# on the list.
|
||||||
|
for moduleName in startupModules:
|
||||||
|
if moduleName not in self.modules:
|
||||||
|
self.modules[moduleName] = self.MTAuto
|
||||||
|
|
||||||
|
# Excluding a parent module also excludes all its children.
|
||||||
|
# Walk through the list in sorted order, so we reach children
|
||||||
|
# before parents.
|
||||||
|
names = self.modules.items()
|
||||||
|
names.sort()
|
||||||
|
|
||||||
|
excludes = []
|
||||||
|
excludeDict = {}
|
||||||
|
includes = []
|
||||||
|
autoIncludes = []
|
||||||
|
for moduleName, token in names:
|
||||||
|
if '.' in moduleName:
|
||||||
|
parentName, baseName = moduleName.rsplit('.', 1)
|
||||||
|
if parentName in excludeDict:
|
||||||
|
token = excludeDict[parentName]
|
||||||
|
|
||||||
|
if token == self.MTInclude:
|
||||||
|
includes.append(moduleName)
|
||||||
|
elif token == self.MTAuto:
|
||||||
|
autoIncludes.append(moduleName)
|
||||||
|
elif token == self.MTExclude or token == self.MTForbid:
|
||||||
|
excludes.append(moduleName)
|
||||||
|
excludeDict[moduleName] = token
|
||||||
|
|
||||||
|
self.mf = modulefinder.ModuleFinder(excludes = excludes)
|
||||||
|
|
||||||
|
# Attempt to import the explicit modules into the modulefinder.
|
||||||
|
for moduleName in includes:
|
||||||
|
self.mf.import_hook(moduleName)
|
||||||
|
|
||||||
|
# Also attempt to import any implicit modules. If any of
|
||||||
|
# these fail to import, we don't care.
|
||||||
|
for moduleName in autoIncludes:
|
||||||
|
try:
|
||||||
|
self.mf.import_hook(moduleName)
|
||||||
|
except ImportError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Now, any new modules we found get added to the export list.
|
||||||
|
for moduleName in self.mf.modules.keys():
|
||||||
|
if moduleName not in self.modules:
|
||||||
|
self.modules[moduleName] = self.MTAuto
|
||||||
|
|
||||||
|
missing = []
|
||||||
|
for moduleName in self.mf.any_missing():
|
||||||
|
if moduleName in startupModules:
|
||||||
|
continue
|
||||||
|
if moduleName in self.previousModules:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# This module is missing. Let it be missing in the
|
||||||
|
# runtime also.
|
||||||
|
self.modules[moduleName] = self.MTExclude
|
||||||
|
|
||||||
|
if moduleName in okMissing:
|
||||||
|
# If it's listed in okMissing, don't even report it.
|
||||||
|
continue
|
||||||
|
|
||||||
|
prefix = moduleName.split('.')[0]
|
||||||
|
if prefix not in sourceTrees:
|
||||||
|
# If it's in not one of our standard source trees, assume
|
||||||
|
# it's some wacky system file we don't need.
|
||||||
|
continue
|
||||||
|
|
||||||
|
missing.append(moduleName)
|
||||||
|
|
||||||
|
if missing:
|
||||||
|
error = "There are some missing modules: %r" % missing
|
||||||
|
print error
|
||||||
|
raise StandardError, error
|
||||||
|
|
||||||
|
def mangleName(self, moduleName):
|
||||||
|
return 'M_' + moduleName.replace('.', '__')
|
||||||
|
|
||||||
|
def generateCode(self, basename):
|
||||||
|
|
||||||
|
# Collect a list of all of the modules we will be explicitly
|
||||||
|
# referencing.
|
||||||
|
moduleNames = []
|
||||||
|
|
||||||
|
for moduleName, token in self.modules.items():
|
||||||
|
prevToken = self.previousModules.get(moduleName, None)
|
||||||
|
if token == self.MTInclude or token == self.MTAuto:
|
||||||
|
# Include this module (even if a previous pass
|
||||||
|
# excluded it). But don't bother if we exported it
|
||||||
|
# previously.
|
||||||
|
if prevToken != self.MTInclude and prevToken != self.MTAuto:
|
||||||
|
if moduleName in self.mf.modules or \
|
||||||
|
moduleName in startupModules:
|
||||||
|
moduleNames.append(moduleName)
|
||||||
|
elif token == self.MTForbid:
|
||||||
|
if prevToken != self.MTForbid:
|
||||||
|
moduleNames.append(moduleName)
|
||||||
|
|
||||||
|
# Build up the replacement pathname table, so we can eliminate
|
||||||
|
# the personal information in the frozen pathnames. The
|
||||||
|
# actual filename we put in there is meaningful only for stack
|
||||||
|
# traces, so we'll just use the module name.
|
||||||
|
replace_paths = []
|
||||||
|
for moduleName, module in self.mf.modules.items():
|
||||||
|
if module.__code__:
|
||||||
|
origPathname = module.__code__.co_filename
|
||||||
|
replace_paths.append((origPathname, moduleName))
|
||||||
|
self.mf.replace_paths = replace_paths
|
||||||
|
|
||||||
|
# Now that we have built up the replacement mapping, go back
|
||||||
|
# through and actually replace the paths.
|
||||||
|
for moduleName, module in self.mf.modules.items():
|
||||||
|
if module.__code__:
|
||||||
|
co = self.mf.replace_paths_in_code(module.__code__)
|
||||||
|
module.__code__ = co;
|
||||||
|
|
||||||
|
# Now generate the actual export table.
|
||||||
|
moduleNames.sort()
|
||||||
|
|
||||||
|
moduleDefs = []
|
||||||
|
moduleList = []
|
||||||
|
|
||||||
|
for moduleName in moduleNames:
|
||||||
|
token = self.modules[moduleName]
|
||||||
|
if token == self.MTForbid:
|
||||||
|
# Explicitly disallow importing this module.
|
||||||
|
moduleList.append(self.makeForbiddenModuleListEntry(moduleName))
|
||||||
|
else:
|
||||||
|
assert token != self.MTExclude
|
||||||
|
# Allow importing this module.
|
||||||
|
module = self.mf.modules.get(moduleName, None)
|
||||||
|
code = getattr(module, "__code__", None)
|
||||||
|
if not code and moduleName in startupModules:
|
||||||
|
# Forbid the loading of this startup module.
|
||||||
|
moduleList.append(self.makeForbiddenModuleListEntry(moduleName))
|
||||||
|
else:
|
||||||
|
if moduleName in sourceTrees:
|
||||||
|
# This is one of our Python source trees.
|
||||||
|
# These are a special case: we don't compile
|
||||||
|
# the __init__.py files within them, since
|
||||||
|
# their only purpose is to munge the __path__
|
||||||
|
# variable anyway. Instead, we pretend the
|
||||||
|
# __init__.py files are empty.
|
||||||
|
code = compile('', moduleName, 'exec')
|
||||||
|
|
||||||
|
if code:
|
||||||
|
code = marshal.dumps(code)
|
||||||
|
|
||||||
|
mangledName = self.mangleName(moduleName)
|
||||||
|
moduleDefs.append(self.makeModuleDef(mangledName, code))
|
||||||
|
moduleList.append(self.makeModuleListEntry(mangledName, code, moduleName, module))
|
||||||
|
if moduleName == self.mainModule:
|
||||||
|
# Add a special entry for __main__.
|
||||||
|
moduleList.append(self.makeModuleListEntry(mangledName, code, '__main__', module))
|
||||||
|
|
||||||
|
if self.compileToExe:
|
||||||
|
code = self.frozenMainCode
|
||||||
|
if self.platform == 'win32':
|
||||||
|
code += self.frozenDllMainCode
|
||||||
|
initCode = self.mainInitCode % {
|
||||||
|
'frozenMainCode' : code,
|
||||||
|
'programName' : basename,
|
||||||
|
}
|
||||||
|
if self.platform == 'win32':
|
||||||
|
initCode += self.frozenExtensions
|
||||||
|
target = basename + '.exe'
|
||||||
|
else:
|
||||||
|
target = basename
|
||||||
|
|
||||||
|
doCompile = self.compileExe
|
||||||
|
|
||||||
|
else:
|
||||||
|
dllexport = ''
|
||||||
|
if self.platform == 'win32':
|
||||||
|
dllexport = '__declspec(dllexport) '
|
||||||
|
target = basename + '.pyd'
|
||||||
|
else:
|
||||||
|
target = basename + '.so'
|
||||||
|
|
||||||
|
initCode = dllInitCode % {
|
||||||
|
'dllexport' : dllexport,
|
||||||
|
'moduleName' : basename,
|
||||||
|
'newcount' : len(moduleList),
|
||||||
|
}
|
||||||
|
doCompile = self.compileDll
|
||||||
|
|
||||||
|
text = programFile % {
|
||||||
|
'moduleDefs' : '\n'.join(moduleDefs),
|
||||||
|
'moduleList' : '\n'.join(moduleList),
|
||||||
|
'initCode' : initCode,
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = basename + self.sourceExtension
|
||||||
|
file = open(filename, 'w')
|
||||||
|
file.write(text)
|
||||||
|
file.close()
|
||||||
|
|
||||||
|
doCompile(filename, basename)
|
||||||
|
os.unlink(filename)
|
||||||
|
os.unlink(basename + self.objectExtension)
|
||||||
|
|
||||||
|
return target
|
||||||
|
|
||||||
|
def compileExe(self, filename, basename):
|
||||||
|
compile = self.compileObj % {
|
||||||
|
'pythonIPath' : PythonIPath,
|
||||||
|
'filename' : filename,
|
||||||
|
'basename' : basename,
|
||||||
|
}
|
||||||
|
print >> sys.stderr, compile
|
||||||
|
if os.system(compile) != 0:
|
||||||
|
raise StandardError
|
||||||
|
|
||||||
|
link = self.linkExe % {
|
||||||
|
'filename' : filename,
|
||||||
|
'basename' : basename,
|
||||||
|
}
|
||||||
|
print >> sys.stderr, link
|
||||||
|
if os.system(link) != 0:
|
||||||
|
raise StandardError
|
||||||
|
|
||||||
|
def compileDll(self, filename, basename):
|
||||||
|
compile = self.compileObj % {
|
||||||
|
'pythonIPath' : PythonIPath,
|
||||||
|
'filename' : filename,
|
||||||
|
'basename' : basename,
|
||||||
|
}
|
||||||
|
print >> sys.stderr, compile
|
||||||
|
if os.system(compile) != 0:
|
||||||
|
raise StandardError
|
||||||
|
|
||||||
|
link = self.linkDll % {
|
||||||
|
'filename' : filename,
|
||||||
|
'basename' : basename,
|
||||||
|
}
|
||||||
|
print >> sys.stderr, link
|
||||||
|
if os.system(link) != 0:
|
||||||
|
raise StandardError
|
||||||
|
|
||||||
|
def makeModuleDef(self, mangledName, code):
|
||||||
|
result = ''
|
||||||
|
result += 'static unsigned char %s[] = {' % (mangledName)
|
||||||
|
for i in range(0, len(code), 16):
|
||||||
|
result += '\n '
|
||||||
|
for c in code[i:i+16]:
|
||||||
|
result += ('%d,' % ord(c))
|
||||||
|
result += '\n};\n'
|
||||||
|
return result
|
||||||
|
|
||||||
|
def makeModuleListEntry(self, mangledName, code, moduleName, module):
|
||||||
|
size = len(code)
|
||||||
|
if getattr(module, "__path__", None):
|
||||||
|
# Indicate package by negative size
|
||||||
|
size = -size
|
||||||
|
return ' {"%s", %s, %s},' % (moduleName, mangledName, size)
|
||||||
|
|
||||||
|
def makeForbiddenModuleListEntry(self, moduleName):
|
||||||
|
return ' {"%s", NULL, 0},' % (moduleName)
|
81
direct/src/showutil/pfreeze.py
Executable file
81
direct/src/showutil/pfreeze.py
Executable file
@ -0,0 +1,81 @@
|
|||||||
|
#! /usr/bin/env python
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
This script can be used to produce a standalone executable from
|
||||||
|
arbitrary Python code. You supply the name of the starting Python
|
||||||
|
file to import, and this script attempts to generate an executable
|
||||||
|
that will produce the same results as "python startfile.py".
|
||||||
|
|
||||||
|
This script is actually a wrapper around Panda's FreezeTool.py, which
|
||||||
|
is itself a tool to use Python's built-in "freeze" utility to compile
|
||||||
|
Python code into a standalone executable. It also uses Python's
|
||||||
|
built-in modulefinder module, which it uses to find all of the modules
|
||||||
|
imported directly or indirectly by the original startfile.py.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
pfreeze.py [opts] startfile
|
||||||
|
|
||||||
|
Options:
|
||||||
|
|
||||||
|
-o output
|
||||||
|
Specifies the name of the resulting executable file to produce.
|
||||||
|
|
||||||
|
-x module[,module...]
|
||||||
|
Specifies a comma-separated list of Python modules to exclude from
|
||||||
|
the resulting file, even if they appear to be referenced. You
|
||||||
|
may also repeat the -x command for each module.
|
||||||
|
|
||||||
|
-i module[,module...]
|
||||||
|
Specifies a comma-separated list of Python modules to include in
|
||||||
|
the resulting file, even if they do not appear to be referenced.
|
||||||
|
You may also repeat the -i command for each module.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import getopt
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from direct.showutil import FreezeTool
|
||||||
|
|
||||||
|
def usage(code, msg = ''):
|
||||||
|
print >> sys.stderr, __doc__
|
||||||
|
print >> sys.stderr, msg
|
||||||
|
sys.exit(code)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
freezer = FreezeTool.Freezer()
|
||||||
|
|
||||||
|
basename = None
|
||||||
|
|
||||||
|
try:
|
||||||
|
opts, args = getopt.getopt(sys.argv[1:], 'o:i:x:h')
|
||||||
|
except getopt.error, msg:
|
||||||
|
usage(1, msg)
|
||||||
|
|
||||||
|
for opt, arg in opts:
|
||||||
|
if opt == '-o':
|
||||||
|
basename = arg
|
||||||
|
elif opt == '-i':
|
||||||
|
for module in arg.split(','):
|
||||||
|
freezer.addModule(module)
|
||||||
|
elif opt == '-x':
|
||||||
|
for module in arg.split(','):
|
||||||
|
freezer.excludeModule(module)
|
||||||
|
elif opt == '-h':
|
||||||
|
usage(0)
|
||||||
|
|
||||||
|
if not args:
|
||||||
|
usage(0)
|
||||||
|
|
||||||
|
if not basename:
|
||||||
|
usage(1, 'You did not specify an output file.')
|
||||||
|
|
||||||
|
if len(args) != 1:
|
||||||
|
usage(1, 'Only one main file may be specified.')
|
||||||
|
|
||||||
|
freezer.setMain(args[0])
|
||||||
|
freezer.done()
|
||||||
|
|
||||||
|
freezer.generateCode(basename)
|
Loading…
x
Reference in New Issue
Block a user