""" Base class for freezing scripts into executables. """ import datetime import distutils.sysconfig import imp import marshal import os import shutil import socket import stat import struct import sys import time import zipfile import cx_Freeze import cx_Freeze.util __all__ = [ "ConfigError", "ConstantsModule", "Executable", "Freezer" ] if sys.platform == "win32": pythonDll = "python%s%s.dll" % sys.version_info[:2] GLOBAL_BIN_PATH_EXCLUDES = [cx_Freeze.util.GetSystemDir()] GLOBAL_BIN_INCLUDES = [ pythonDll, "gdiplus.dll", "mfc71.dll", "msvcp71.dll", "msvcr71.dll" ] GLOBAL_BIN_EXCLUDES = [ "comctl32.dll", "oci.dll", "cx_Logging.pyd" ] else: extension = distutils.sysconfig.get_config_var("SO") pythonSharedLib = "libpython%s.%s%s" % \ (sys.version_info[:2] + (extension,)) GLOBAL_BIN_INCLUDES = [pythonSharedLib] GLOBAL_BIN_EXCLUDES = [ "libclntsh.so", "libwtc9.so" ] GLOBAL_BIN_PATH_EXCLUDES = ["/lib", "/lib32", "/lib64", "/usr/lib", "/usr/lib32", "/usr/lib64"] # NOTE: the try: except: block in this code is not necessary under Python 2.4 # and higher and can be removed once support for Python 2.3 is no longer needed EXTENSION_LOADER_SOURCE = \ """ import imp, os, sys found = False for p in sys.path: if not os.path.isdir(p): continue f = os.path.join(p, "%s") if not os.path.exists(f): continue try: m = imp.load_dynamic(__name__, f) except ImportError: del sys.modules[__name__] raise sys.modules[__name__] = m found = True break if not found: del sys.modules[__name__] raise ImportError, "No module named %%s" %% __name__ """ class Freezer(object): def __init__(self, executables, constantsModules = [], includes = [], excludes = [], packages = [], replacePaths = [], compress = None, optimizeFlag = 0, copyDependentFiles = None, initScript = None, base = None, path = None, createLibraryZip = None, appendScriptToExe = None, appendScriptToLibrary = None, targetDir = None, binIncludes = [], binExcludes = [], binPathIncludes = [], binPathExcludes = [], icon = None, includeFiles = []): self.executables = executables self.constantsModules = constantsModules self.includes = includes self.excludes = excludes self.packages = packages self.replacePaths = replacePaths self.compress = compress self.optimizeFlag = optimizeFlag self.copyDependentFiles = copyDependentFiles self.initScript = initScript self.base = base self.path = path self.createLibraryZip = createLibraryZip self.appendScriptToExe = appendScriptToExe self.appendScriptToLibrary = appendScriptToLibrary self.targetDir = targetDir self.binIncludes = [os.path.normcase(n) \ for n in GLOBAL_BIN_INCLUDES + binIncludes] self.binExcludes = [os.path.normcase(n) \ for n in GLOBAL_BIN_EXCLUDES + binExcludes] self.binPathIncludes = [os.path.normcase(n) for n in binPathIncludes] self.binPathExcludes = [os.path.normcase(n) \ for n in GLOBAL_BIN_PATH_EXCLUDES + binPathExcludes] self.icon = icon self.includeFiles = includeFiles self._VerifyConfiguration() def _CopyFile(self, source, target, copyDependentFiles, includeMode = False): normalizedSource = os.path.normcase(os.path.normpath(source)) normalizedTarget = os.path.normcase(os.path.normpath(target)) if normalizedTarget in self.filesCopied: return if normalizedSource == normalizedTarget: return self._RemoveFile(target) targetDir = os.path.dirname(target) self._CreateDirectory(targetDir) print "copying", source, "->", target shutil.copyfile(source, target) if includeMode: shutil.copymode(source, target) self.filesCopied[normalizedTarget] = None if copyDependentFiles: for source in self._GetDependentFiles(source): target = os.path.join(targetDir, os.path.basename(source)) self._CopyFile(source, target, copyDependentFiles) def _CreateDirectory(self, path): if not os.path.isdir(path): print "creating directory", path os.makedirs(path) def _FreezeExecutable(self, exe): if self.createLibraryZip: finder = self.finder else: finder = self._GetModuleFinder(exe) if exe.script is None: scriptModule = None else: scriptModule = finder.IncludeFile(exe.script, exe.moduleName) self._CopyFile(exe.base, exe.targetName, exe.copyDependentFiles, includeMode = True) if exe.icon is not None: if sys.platform == "win32": cx_Freeze.util.AddIcon(exe.targetName, exe.icon) else: targetName = os.path.join(os.path.dirname(exe.targetName), os.path.basename(exe.icon)) self._CopyFile(exe.icon, targetName, copyDependentFiles = False) if not os.access(exe.targetName, os.W_OK): mode = os.stat(exe.targetName).st_mode os.chmod(exe.targetName, mode | stat.S_IWUSR) if not exe.appendScriptToLibrary: if exe.appendScriptToExe: fileName = exe.targetName else: baseFileName, ext = os.path.splitext(exe.targetName) fileName = baseFileName + ".zip" self._RemoveFile(fileName) if not self.createLibraryZip and exe.copyDependentFiles: scriptModule = None self._WriteModules(fileName, exe.initScript, finder, exe.compress, exe.copyDependentFiles, scriptModule) def _GetBaseFileName(self, argsSource = None): if argsSource is None: argsSource = self name = argsSource.base if name is None: if argsSource.copyDependentFiles: name = "Console" else: name = "ConsoleKeepPath" argsSource.base = self._GetFileName("bases", name) if argsSource.base is None: raise ConfigError("no base named %s", name) def _GetDependentFiles(self, path): dependentFiles = self.dependentFiles.get(path) if dependentFiles is None: if sys.platform == "win32": origPath = os.environ["PATH"] os.environ["PATH"] = origPath + os.pathsep + \ os.pathsep.join(sys.path) dependentFiles = cx_Freeze.util.GetDependentFiles(path) os.environ["PATH"] = origPath else: dependentFiles = [] for line in os.popen('ldd "%s"' % path): parts = line.strip().split(" => ") if len(parts) != 2: continue dependentFile = parts[1] if dependentFile == "not found": print "WARNING: cannot find", parts[0] continue pos = dependentFile.find(" (") if pos >= 0: dependentFile = dependentFile[:pos].strip() if dependentFile: dependentFiles.append(dependentFile) dependentFiles = self.dependentFiles[path] = \ [f for f in dependentFiles if self._ShouldCopyFile(f)] return dependentFiles def _GetFileName(self, dir, name): if os.path.isabs(name): return name name = os.path.normcase(name) fullDir = os.path.join(os.path.dirname(cx_Freeze.__file__), dir) if os.path.isdir(fullDir): for fileName in os.listdir(fullDir): if name == os.path.splitext(os.path.normcase(fileName))[0]: return os.path.join(fullDir, fileName) def _GetInitScriptFileName(self, argsSource = None): if argsSource is None: argsSource = self name = argsSource.initScript if name is None: if argsSource.copyDependentFiles: name = "Console" else: name = "ConsoleKeepPath" argsSource.initScript = self._GetFileName("initscripts", name) if argsSource.initScript is None: raise ConfigError("no initscript named %s", name) def _GetModuleFinder(self, argsSource = None): if argsSource is None: argsSource = self finder = cx_Freeze.ModuleFinder(self.includeFiles, argsSource.excludes, argsSource.path, argsSource.replacePaths) if argsSource.copyDependentFiles: finder.IncludeModule("imp") finder.IncludeModule("os") finder.IncludeModule("sys") if argsSource.compress: finder.IncludeModule("zlib") for name in argsSource.includes: finder.IncludeModule(name) for name in argsSource.packages: finder.IncludePackage(name) return finder def _PrintReport(self, fileName, modules): print "writing zip file", fileName print print " %-25s %s" % ("Name", "File") print " %-25s %s" % ("----", "----") for module in modules: if module.path: print "P", else: print "m", print "%-25s" % module.name, module.file or "" print def _RemoveFile(self, path): if os.path.exists(path): os.chmod(path, 0777) os.remove(path) def _ShouldCopyFile(self, path): dir, name = os.path.split(os.path.normcase(path)) parts = name.split(".") tweaked = False while True: if not parts[-1].isdigit(): break parts.pop(-1) tweaked = True if tweaked: name = ".".join(parts) if name in self.binIncludes: return True if name in self.binExcludes: return False for path in self.binPathIncludes: if dir.startswith(path): return True for path in self.binPathExcludes: if dir.startswith(path): return False return True def _VerifyCanAppendToLibrary(self): if not self.createLibraryZip: raise ConfigError("script cannot be appended to library zip if " "one is not being created") def _VerifyConfiguration(self): if self.compress is None: self.compress = True if self.copyDependentFiles is None: self.copyDependentFiles = True if self.createLibraryZip is None: self.createLibraryZip = True if self.appendScriptToExe is None: self.appendScriptToExe = False if self.appendScriptToLibrary is None: self.appendScriptToLibrary = \ self.createLibraryZip and not self.appendScriptToExe if self.targetDir is None: self.targetDir = os.path.abspath("dist") self._GetInitScriptFileName() self._GetBaseFileName() if self.path is None: self.path = sys.path if self.appendScriptToLibrary: self._VerifyCanAppendToLibrary() for sourceFileName, targetFileName in self.includeFiles: if not os.path.exists(sourceFileName): raise ConfigError("cannot find file/directory named %s", sourceFileName) if os.path.isabs(targetFileName): raise ConfigError("target file/directory cannot be absolute") for executable in self.executables: executable._VerifyConfiguration(self) def _WriteModules(self, fileName, initScript, finder, compress, copyDependentFiles, scriptModule = None): initModule = finder.IncludeFile(initScript, "cx_Freeze__init__") if scriptModule is None: for module in self.constantsModules: module.Create(finder) modules = [m for m in finder.modules \ if m.name not in self.excludeModules] else: modules = [initModule, scriptModule] self.excludeModules[initModule.name] = None self.excludeModules[scriptModule.name] = None itemsToSort = [(m.name, m) for m in modules] itemsToSort.sort() modules = [m for n, m in itemsToSort] self._PrintReport(fileName, modules) if scriptModule is None: finder.ReportMissingModules() targetDir = os.path.dirname(fileName) self._CreateDirectory(targetDir) filesToCopy = [] if os.path.exists(fileName): mode = "a" else: mode = "w" outFile = zipfile.PyZipFile(fileName, mode, zipfile.ZIP_DEFLATED) for module in modules: if module.code is None and module.file is not None: fileName = os.path.basename(module.file) baseFileName, ext = os.path.splitext(fileName) if baseFileName != module.name and module.name != "zlib": if "." in module.name: fileName = module.name + ext generatedFileName = "ExtensionLoader_%s.py" % \ module.name.replace(".", "_") module.code = compile(EXTENSION_LOADER_SOURCE % fileName, generatedFileName, "exec") target = os.path.join(targetDir, fileName) filesToCopy.append((module, target)) if module.code is None: continue fileName = "/".join(module.name.split(".")) if module.path: fileName += "/__init__" if module.file is not None and os.path.exists(module.file): mtime = os.stat(module.file).st_mtime else: mtime = time.time() zipTime = time.localtime(mtime)[:6] data = imp.get_magic() + struct.pack("" % self.script def _VerifyConfiguration(self, freezer): if self.path is None: self.path = freezer.path if self.targetDir is None: self.targetDir = freezer.targetDir if self.includes is None: self.includes = freezer.includes if self.excludes is None: self.excludes = freezer.excludes if self.packages is None: self.packages = freezer.packages if self.replacePaths is None: self.replacePaths = freezer.replacePaths if self.compress is None: self.compress = freezer.compress if self.copyDependentFiles is None: self.copyDependentFiles = freezer.copyDependentFiles if self.appendScriptToExe is None: self.appendScriptToExe = freezer.appendScriptToExe if self.appendScriptToLibrary is None: self.appendScriptToLibrary = freezer.appendScriptToLibrary if self.initScript is None: self.initScript = freezer.initScript else: freezer._GetInitScriptFileName(self) if self.base is None: self.base = freezer.base else: freezer._GetBaseFileName(self) if self.appendScriptToLibrary: freezer._VerifyCanAppendToLibrary() if self.icon is None: self.icon = freezer.icon if self.script is not None: name, ext = os.path.splitext(os.path.basename(self.script)) if self.appendScriptToLibrary: self.moduleName = "%s__main__" % os.path.normcase(name) else: self.moduleName = "__main__" if self.targetName is None: baseName, ext = os.path.splitext(self.base) self.targetName = name + ext self.targetName = os.path.join(self.targetDir, self.targetName) class ConstantsModule(object): def __init__(self, releaseString = None, copyright = None, moduleName = "BUILD_CONSTANTS", timeFormat = "%B %d, %Y %H:%M:%S"): self.moduleName = moduleName self.timeFormat = timeFormat self.values = {} self.values["BUILD_RELEASE_STRING"] = releaseString self.values["BUILD_COPYRIGHT"] = copyright def Create(self, finder): """Create the module which consists of declaration statements for each of the values.""" today = datetime.datetime.today() sourceTimestamp = 0 for module in finder.modules: if module.file is None: continue if module.inZipFile: continue if not os.path.exists(module.file): raise ConfigError("no file named %s", module.file) timestamp = os.stat(module.file).st_mtime sourceTimestamp = max(sourceTimestamp, timestamp) sourceTimestamp = datetime.datetime.fromtimestamp(sourceTimestamp) self.values["BUILD_TIMESTAMP"] = today.strftime(self.timeFormat) self.values["BUILD_HOST"] = socket.gethostname().split(".")[0] self.values["SOURCE_TIMESTAMP"] = \ sourceTimestamp.strftime(self.timeFormat) module = finder._AddModule(self.moduleName) sourceParts = [] names = self.values.keys() names.sort() for name in names: value = self.values[name] sourceParts.append("%s = %r" % (name, value)) source = "\n".join(sourceParts) module.code = compile(source, "%s.py" % self.moduleName, "exec")