diff --git a/direct/src/showutil/FreezeTool.py b/direct/src/showutil/FreezeTool.py index c734c56101..b0c1af3d00 100644 --- a/direct/src/showutil/FreezeTool.py +++ b/direct/src/showutil/FreezeTool.py @@ -7,6 +7,7 @@ import os import marshal import imp import platform +import struct from io import StringIO import distutils.sysconfig as sysconf @@ -1549,6 +1550,93 @@ class Freezer: return target + def generateRuntimeFromStub(self, basename): + def make_module_list_entry(code, offset, modulename, module): + size = len(code) + if getattr(module, "__path__", None): + # Indicate package by negative size + size = -size + return struct.pack('<256sIi', bytes(modulename, 'ascii'), offset, size) + + def make_forbidden_module_list_entry(modulename): + return struct.pack('<256sIi', bytes(modulename, 'ascii'), 0, 0) + + # We must have a __main__ module to make an exe file. + if not self.__writingModule('__main__'): + message = "Can't generate an executable without a __main__ module." + raise Exception(message) + + if self.platform.startswith('win'): + target = basename + '.exe' + else: + target = basename + + + # Generate export table. + moduleBlob = bytes() + codeOffset = 0 + moduleList = [] + + for moduleName, mdef in self.getModuleDefs(): + origName = mdef.moduleName + if mdef.forbid: + # Explicitly disallow importing this module. + moduleList.append(make_forbidden_module_list_entry(moduleName)) + continue + + assert not mdef.exclude + # Allow importing this module. + module = self.mf.modules.get(origName, None) + code = getattr(module, "__code__", None) + if code: + code = marshal.dumps(code) + moduleList.append(make_module_list_entry(code, codeOffset, moduleName, module)) + moduleBlob += code + codeOffset += len(code) + continue + + # This is a module with no associated Python code. It is either + # an extension module or a builtin module. Get the filename, if + # it is the former. + extensionFilename = getattr(module, '__file__', None) + + if extensionFilename or self.linkExtensionModules: + self.extras.append((moduleName, extensionFilename)) + + # If it is a submodule of a frozen module, Python will have + # trouble importing it as a builtin module. Synthesize a frozen + # module that loads it as builtin. + if '.' in moduleName and self.linkExtensionModules: + code = compile('import sys;del sys.modules["%s"];import imp;imp.init_builtin("%s")' % (moduleName, moduleName), moduleName, 'exec') + code = marshal.dumps(code) + moduleList.append(make_module_list_entry(code, codeOffset, moduleName, module)) + moduleBlob += code + codeOffset += len(code) + elif '.' in moduleName: + # Nothing we can do about this case except warn the user they + # are in for some trouble. + print('WARNING: Python cannot import extension modules under ' + 'frozen Python packages; %s will be inaccessible. ' + 'passing either -l to link in extension modules or use ' + '-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') + with open(stub_path, 'rb') as f: + stubbin = f.read() + + with open(target, 'wb') as f: + f.write(stubbin) + listoffset = f.tell() + for mod in moduleList: + f.write(mod) + modsoffset = f.tell() + f.write(moduleBlob) + f.write(struct.pack('