diff options
Diffstat (limited to 'intern/python/freeze')
-rw-r--r-- | intern/python/freeze/Makefile | 14 | ||||
-rw-r--r-- | intern/python/freeze/README | 173 | ||||
-rw-r--r-- | intern/python/freeze/README.NaN | 12 | ||||
-rw-r--r-- | intern/python/freeze/bkfile.py | 47 | ||||
-rw-r--r-- | intern/python/freeze/checkextensions.py | 91 | ||||
-rw-r--r-- | intern/python/freeze/checkextensions_win32.py | 190 | ||||
-rwxr-xr-x | intern/python/freeze/freeze.py | 461 | ||||
-rw-r--r-- | intern/python/freeze/hello.py | 1 | ||||
-rw-r--r-- | intern/python/freeze/makeconfig.py | 61 | ||||
-rw-r--r-- | intern/python/freeze/makefreeze.py | 115 | ||||
-rw-r--r-- | intern/python/freeze/makemakefile.py | 57 | ||||
-rw-r--r-- | intern/python/freeze/modulefinder.py | 444 | ||||
-rw-r--r-- | intern/python/freeze/parsesetup.py | 98 | ||||
-rw-r--r-- | intern/python/freeze/winmakemakefile.py | 146 |
14 files changed, 1910 insertions, 0 deletions
diff --git a/intern/python/freeze/Makefile b/intern/python/freeze/Makefile new file mode 100644 index 00000000000..3c344029b3e --- /dev/null +++ b/intern/python/freeze/Makefile @@ -0,0 +1,14 @@ +# $Id$ +# This is the makefile for the bytecode freezing of all modules which +# the main file depends on (last argument in importer rule) + +SRCDIR = ../modules + +TARGETDIR = ../../../source/blender/bpython/frozen + +PYFLAGS = -S -O + +default: importer + +importer: + python $(PYFLAGS) freeze.py -d -x os -x pprint -x Blender -I $(SRCDIR) -o $(TARGETDIR) $(SRCDIR)/VRMLmain.py diff --git a/intern/python/freeze/README b/intern/python/freeze/README new file mode 100644 index 00000000000..fcdd67029a5 --- /dev/null +++ b/intern/python/freeze/README @@ -0,0 +1,173 @@ +THE FREEZE SCRIPT +================= + +(Directions for Windows are at the end of this file.) + + +What is Freeze? +--------------- + +Freeze make it possible to ship arbitrary Python programs to people +who don't have Python. The shipped file (called a "frozen" version of +your Python program) is an executable, so this only works if your +platform is compatible with that on the receiving end (this is usually +a matter of having the same major operating system revision and CPU +type). + +The shipped file contains a Python interpreter and large portions of +the Python run-time. Some measures have been taken to avoid linking +unneeded modules, but the resulting binary is usually not small. + +The Python source code of your program (and of the library modules +written in Python that it uses) is not included in the binary -- +instead, the compiled byte-code (the instruction stream used +internally by the interpreter) is incorporated. This gives some +protection of your Python source code, though not much -- a +disassembler for Python byte-code is available in the standard Python +library. At least someone running "strings" on your binary won't see +the source. + + +How does Freeze know which modules to include? +---------------------------------------------- + +Previous versions of Freeze used a pretty simple-minded algorithm to +find the modules that your program uses, essentially searching for +lines starting with the word "import". It was pretty easy to trick it +into making mistakes, either missing valid import statements, or +mistaking string literals (e.g. doc strings) for import statements. + +This has been remedied: Freeze now uses the regular Python parser to +parse the program (and all its modules) and scans the generated byte +code for IMPORT instructions. It may still be confused -- it will not +know about calls to the __import__ built-in function, or about import +statements constructed on the fly and executed using the 'exec' +statement, and it will consider import statements even when they are +unreachable (e.g. "if 0: import foobar"). + +This new version of Freeze also knows about Python's new package +import mechanism, and uses exactly the same rules to find imported +modules and packages. One exception: if you write 'from package +import *', Python will look into the __all__ variable of the package +to determine which modules are to be imported, while Freeze will do a +directory listing. + +One tricky issue: Freeze assumes that the Python interpreter and +environment you're using to run Freeze is the same one that would be +used to run your program, which should also be the same whose sources +and installed files you will learn about in the next section. In +particular, your PYTHONPATH setting should be the same as for running +your program locally. (Tip: if the program doesn't run when you type +"python hello.py" there's little chance of getting the frozen version +to run.) + + +How do I use Freeze? +-------------------- + +Normally, you should be able to use it as follows: + + python freeze.py hello.py + +where hello.py is your program and freeze.py is the main file of +Freeze (in actuality, you'll probably specify an absolute pathname +such as /usr/joe/python/Tools/freeze/freeze.py). + + +What do I do next? +------------------ + +Freeze creates a number of files: frozen.c, config.c and Makefile, +plus one file for each Python module that gets included named +M_<module>.c. To produce the frozen version of your program, you can +simply type "make". This should produce a binary file. If the +filename argument to Freeze was "hello.py", the binary will be called +"hello". + +Note: you can use the -o option to freeze to specify an alternative +directory where these files are created. This makes it easier to +clean up after you've shipped the frozen binary. You should invoke +"make" in the given directory. + + +Freezing Tkinter programs +------------------------- + +Unfortunately, it is currently not possible to freeze programs that +use Tkinter. It *seems* to work, but when you ship the frozen program +to a site without a Tcl/Tk installation, it will fail with a complaint +about missing Tcl/Tk initialization files. + +A workaround would be possible, in which the Tcl/Tk library files are +incorporated in a frozen Python module as string literals and written +to a temporary location when the program runs; this is currently left +as an exercise for the reader. (If you implement this, please post to +the Python newsgroup!) + +Of course, you can also simply require that Tcl/Tk is required on the +target installation. + + +A warning against shared library modules +---------------------------------------- + +When your Python installation uses shared library modules, these will +not be incorporated in the frozen program. Again, the frozen program +will work when you test it, but it won't work when you ship it to a +site without a Python installation. + +Freeze prints a warning when this is the case at the end of the +freezing process: + + Warning: unknown modules remain: ... + +When this occurs, the best thing to do is usually to rebuild Python +using static linking only. + + +Troubleshooting +--------------- + +If you have trouble using Freeze for a large program, it's probably +best to start playing with a really simple program first (like the file +hello.py). If you can't get that to work there's something +fundamentally wrong -- perhaps you haven't installed Python. To do a +proper install, you should do "make install" in the Python root +directory. + + +Usage under Windows 95 or NT +---------------------------- + +Under Windows 95 or NT, you *must* use the -p option and point it to +the top of the Python source tree. + +WARNING: the resulting executable is not self-contained; it requires +the Python DLL, currently PYTHON20.DLL (it does not require the +standard library of .py files though). It may also require one or +more extension modules loaded from .DLL or .PYD files; the module +names are printed in the warning message about remaining unknown +modules. + +The driver script generates a Makefile that works with the Microsoft +command line C compiler (CL). To compile, run "nmake"; this will +build a target "hello.exe" if the source was "hello.py". Only the +files frozenmain.c and frozen.c are used; no config.c is generated or +used, since the standard DLL is used. + +In order for this to work, you must have built Python using the VC++ +(Developer Studio) 5.0 compiler. The provided project builds +python20.lib in the subdirectory pcbuild\Release of thje Python source +tree, and this is where the generated Makefile expects it to be. If +this is not the case, you can edit the Makefile or (probably better) +winmakemakefile.py (e.g., if you are using the 4.2 compiler, the +python20.lib file is generated in the subdirectory vc40 of the Python +source tree). + +You can freeze programs that use Tkinter, but Tcl/Tk must be installed +on the target system. + +It is possible to create frozen programs that don't have a console +window, by specifying the option '-s windows'. + +--Guido van Rossum (home page: http://www.python.org/~guido/) diff --git a/intern/python/freeze/README.NaN b/intern/python/freeze/README.NaN new file mode 100644 index 00000000000..93892543c4f --- /dev/null +++ b/intern/python/freeze/README.NaN @@ -0,0 +1,12 @@ +$Id$ + +This is a modification of the freeze.py script used to freeze python +modules as byte code in Blender. + +To create this byte code, simply type 'make'. Freeze will then generate +the C source files in the TARGETDIR (specified in the Makefile), provided +that you have a valid python installation. + +Be warned: testing of the module dependencies is needed, as these are +resolved AT RUNTIME! + diff --git a/intern/python/freeze/bkfile.py b/intern/python/freeze/bkfile.py new file mode 100644 index 00000000000..d29716a93c9 --- /dev/null +++ b/intern/python/freeze/bkfile.py @@ -0,0 +1,47 @@ +_orig_open = open + +class _BkFile: + def __init__(self, file, mode, bufsize): + import os + self.__filename = file + self.__backup = file + '~' + try: + os.unlink(self.__backup) + except os.error: + pass + try: + os.rename(file, self.__backup) + except os.error: + self.__backup = None + self.__file = _orig_open(file, mode, bufsize) + self.closed = self.__file.closed + self.fileno = self.__file.fileno + self.flush = self.__file.flush + self.isatty = self.__file.isatty + self.mode = self.__file.mode + self.name = self.__file.name + self.read = self.__file.read + self.readinto = self.__file.readinto + self.readline = self.__file.readline + self.readlines = self.__file.readlines + self.seek = self.__file.seek + self.softspace = self.__file.softspace + self.tell = self.__file.tell + self.truncate = self.__file.truncate + self.write = self.__file.write + self.writelines = self.__file.writelines + + def close(self): + self.__file.close() + if self.__backup is None: + return + import filecmp + if filecmp.cmp(self.__backup, self.__filename, shallow = 0): + import os + os.unlink(self.__filename) + os.rename(self.__backup, self.__filename) + +def open(file, mode = 'r', bufsize = -1): + if 'w' not in mode: + return _orig_open(file, mode, bufsize) + return _BkFile(file, mode, bufsize) diff --git a/intern/python/freeze/checkextensions.py b/intern/python/freeze/checkextensions.py new file mode 100644 index 00000000000..8d597bfae96 --- /dev/null +++ b/intern/python/freeze/checkextensions.py @@ -0,0 +1,91 @@ +# Check for a module in a set of extension directories. +# An extension directory should contain a Setup file +# and one or more .o files or a lib.a file. + +import os +import string +import parsesetup + +def checkextensions(unknown, extensions): + files = [] + modules = [] + edict = {} + for e in extensions: + setup = os.path.join(e, 'Setup') + liba = os.path.join(e, 'lib.a') + if not os.path.isfile(liba): + liba = None + edict[e] = parsesetup.getsetupinfo(setup), liba + for mod in unknown: + for e in extensions: + (mods, vars), liba = edict[e] + if not mods.has_key(mod): + continue + modules.append(mod) + if liba: + # If we find a lib.a, use it, ignore the + # .o files, and use *all* libraries for + # *all* modules in the Setup file + if liba in files: + break + files.append(liba) + for m in mods.keys(): + files = files + select(e, mods, vars, + m, 1) + break + files = files + select(e, mods, vars, mod, 0) + break + return files, modules + +def select(e, mods, vars, mod, skipofiles): + files = [] + for w in mods[mod]: + w = treatword(w) + if not w: + continue + w = expandvars(w, vars) + for w in string.split(w): + if skipofiles and w[-2:] == '.o': + continue + # Assume $var expands to absolute pathname + if w[0] not in ('-', '$') and w[-2:] in ('.o', '.a'): + w = os.path.join(e, w) + if w[:2] in ('-L', '-R') and w[2:3] != '$': + w = w[:2] + os.path.join(e, w[2:]) + files.append(w) + return files + +cc_flags = ['-I', '-D', '-U'] +cc_exts = ['.c', '.C', '.cc', '.c++'] + +def treatword(w): + if w[:2] in cc_flags: + return None + if w[:1] == '-': + return w # Assume loader flag + head, tail = os.path.split(w) + base, ext = os.path.splitext(tail) + if ext in cc_exts: + tail = base + '.o' + w = os.path.join(head, tail) + return w + +def expandvars(str, vars): + i = 0 + while i < len(str): + i = k = string.find(str, '$', i) + if i < 0: + break + i = i+1 + var = str[i:i+1] + i = i+1 + if var == '(': + j = string.find(str, ')', i) + if j < 0: + break + var = str[i:j] + i = j+1 + if vars.has_key(var): + str = str[:k] + vars[var] + str[i:] + i = k + return str diff --git a/intern/python/freeze/checkextensions_win32.py b/intern/python/freeze/checkextensions_win32.py new file mode 100644 index 00000000000..85c3a3c0cdd --- /dev/null +++ b/intern/python/freeze/checkextensions_win32.py @@ -0,0 +1,190 @@ +"""Extension management for Windows. + +Under Windows it is unlikely the .obj files are of use, as special compiler options +are needed (primarily to toggle the behavior of "public" symbols. + +I dont consider it worth parsing the MSVC makefiles for compiler options. Even if +we get it just right, a specific freeze application may have specific compiler +options anyway (eg, to enable or disable specific functionality) + +So my basic stragtegy is: + +* Have some Windows INI files which "describe" one or more extension modules. + (Freeze comes with a default one for all known modules - but you can specify + your own). +* This description can include: + - The MSVC .dsp file for the extension. The .c source file names + are extraced from there. + - Specific compiler/linker options + - Flag to indicate if Unicode compilation is expected. + +At the moment the name and location of this INI file is hardcoded, +but an obvious enhancement would be to provide command line options. +""" + +import os, string, sys +try: + import win32api +except ImportError: + win32api = None # User has already been warned + +class CExtension: + """An abstraction of an extension implemented in C/C++ + """ + def __init__(self, name, sourceFiles): + self.name = name + # A list of strings defining additional compiler options. + self.sourceFiles = sourceFiles + # A list of special compiler options to be applied to + # all source modules in this extension. + self.compilerOptions = [] + # A list of .lib files the final .EXE will need. + self.linkerLibs = [] + + def GetSourceFiles(self): + return self.sourceFiles + + def AddCompilerOption(self, option): + self.compilerOptions.append(option) + def GetCompilerOptions(self): + return self.compilerOptions + + def AddLinkerLib(self, lib): + self.linkerLibs.append(lib) + def GetLinkerLibs(self): + return self.linkerLibs + +def checkextensions(unknown, extra_inis, prefix): + # Create a table of frozen extensions + + defaultMapName = os.path.join( os.path.split(sys.argv[0])[0], "extensions_win32.ini") + if not os.path.isfile(defaultMapName): + sys.stderr.write("WARNING: %s can not be found - standard extensions may not be found" % mapFileName) + else: + # must go on end, so other inis can override. + extra_inis.append(defaultMapName) + + ret = [] + for mod in unknown: + for ini in extra_inis: +# print "Looking for", mod, "in", win32api.GetFullPathName(ini),"...", + defn = get_extension_defn( mod, ini, prefix ) + if defn is not None: +# print "Yay - found it!" + ret.append( defn ) + break +# print "Nope!" + else: # For not broken! + sys.stderr.write("No definition of module %s in any specified map file.\n" % (mod)) + + return ret + +def get_extension_defn(moduleName, mapFileName, prefix): + if win32api is None: return None + os.environ['PYTHONPREFIX'] = prefix + dsp = win32api.GetProfileVal(moduleName, "dsp", "", mapFileName) + if dsp=="": + return None + + # We allow environment variables in the file name + dsp = win32api.ExpandEnvironmentStrings(dsp) + # If the path to the .DSP file is not absolute, assume it is relative + # to the description file. + if not os.path.isabs(dsp): + dsp = os.path.join( os.path.split(mapFileName)[0], dsp) + # Parse it to extract the source files. + sourceFiles = parse_dsp(dsp) + if sourceFiles is None: + return None + + module = CExtension(moduleName, sourceFiles) + # Put the path to the DSP into the environment so entries can reference it. + os.environ['dsp_path'] = os.path.split(dsp)[0] + os.environ['ini_path'] = os.path.split(mapFileName)[0] + + cl_options = win32api.GetProfileVal(moduleName, "cl", "", mapFileName) + if cl_options: + module.AddCompilerOption(win32api.ExpandEnvironmentStrings(cl_options)) + + exclude = win32api.GetProfileVal(moduleName, "exclude", "", mapFileName) + exclude = string.split(exclude) + + if win32api.GetProfileVal(moduleName, "Unicode", 0, mapFileName): + module.AddCompilerOption('/D UNICODE /D _UNICODE') + + libs = string.split(win32api.GetProfileVal(moduleName, "libs", "", mapFileName)) + for lib in libs: + module.AddLinkerLib(win32api.ExpandEnvironmentStrings(lib)) + + for exc in exclude: + if exc in module.sourceFiles: + modules.sourceFiles.remove(exc) + + return module + +# Given an MSVC DSP file, locate C source files it uses +# returns a list of source files. +def parse_dsp(dsp): +# print "Processing", dsp + # For now, only support + ret = [] + dsp_path, dsp_name = os.path.split(dsp) + try: + lines = open(dsp, "r").readlines() + except IOError, msg: + sys.stderr.write("%s: %s\n" % (dsp, msg)) + return None + for line in lines: + fields = string.split(string.strip(line), "=", 2) + if fields[0]=="SOURCE": + if string.lower(os.path.splitext(fields[1])[1]) in ['.cpp', '.c']: + ret.append( win32api.GetFullPathName(os.path.join(dsp_path, fields[1] ) ) ) + return ret + +def write_extension_table(fname, modules): + fp = open(fname, "w") + try: + fp.write (ext_src_header) + # Write fn protos + for module in modules: + # bit of a hack for .pyd's as part of packages. + name = string.split(module.name,'.')[-1] + fp.write('extern void init%s(void);\n' % (name) ) + # Write the table + fp.write (ext_tab_header) + for module in modules: + name = string.split(module.name,'.')[-1] + fp.write('\t{"%s", init%s},\n' % (name, name) ) + + fp.write (ext_tab_footer) + fp.write(ext_src_footer) + finally: + fp.close() + + +ext_src_header = """\ +#include "Python.h" +""" + +ext_tab_header = """\ + +static struct _inittab extensions[] = { +""" + +ext_tab_footer = """\ + /* Sentinel */ + {0, 0} +}; +""" + +ext_src_footer = """\ +extern DL_IMPORT(int) PyImport_ExtendInittab(struct _inittab *newtab); + +int PyInitFrozenExtensions() +{ + return PyImport_ExtendInittab(extensions); +} + +""" + + diff --git a/intern/python/freeze/freeze.py b/intern/python/freeze/freeze.py new file mode 100755 index 00000000000..e6f4ead15d5 --- /dev/null +++ b/intern/python/freeze/freeze.py @@ -0,0 +1,461 @@ +#! /usr/bin/env python + +# changes made by strubi@blender.nl + +"""Freeze a Python script into a binary. + +usage: freeze [options...] script [module]... + +Options: +-p prefix: This is the prefix used when you ran ``make install'' + in the Python build directory. + (If you never ran this, freeze won't work.) + The default is whatever sys.prefix evaluates to. + It can also be the top directory of the Python source + tree; then -P must point to the build tree. + +-P exec_prefix: Like -p but this is the 'exec_prefix', used to + install objects etc. The default is whatever sys.exec_prefix + evaluates to, or the -p argument if given. + If -p points to the Python source tree, -P must point + to the build tree, if different. + +-e extension: A directory containing additional .o files that + may be used to resolve modules. This directory + should also have a Setup file describing the .o files. + On Windows, the name of a .INI file describing one + or more extensions is passed. + More than one -e option may be given. + +-o dir: Directory where the output files are created; default '.'. + +-m: Additional arguments are module names instead of filenames. + +-a package=dir: Additional directories to be added to the package's + __path__. Used to simulate directories added by the + package at runtime (eg, by OpenGL and win32com). + More than one -a option may be given for each package. + +-l file: Pass the file to the linker (windows only) + +-d: Debugging mode for the module finder. + +-q: Make the module finder totally quiet. + +-h: Print this help message. + +-x module Exclude the specified module. + +-i filename: Include a file with additional command line options. Used + to prevent command lines growing beyond the capabilities of + the shell/OS. All arguments specified in filename + are read and the -i option replaced with the parsed + params (note - quoting args in this file is NOT supported) + +-s subsystem: Specify the subsystem (For Windows only.); + 'console' (default), 'windows', 'service' or 'com_dll' + +-w: Toggle Windows (NT or 95) behavior. + (For debugging only -- on a win32 platform, win32 behavior + is automatic.) + +Arguments: + +script: The Python script to be executed by the resulting binary. + +module ...: Additional Python modules (referenced by pathname) + that will be included in the resulting binary. These + may be .py or .pyc files. If -m is specified, these are + module names that are search in the path instead. + +NOTES: + +In order to use freeze successfully, you must have built Python and +installed it ("make install"). + +The script should not use modules provided only as shared libraries; +if it does, the resulting binary is not self-contained. +""" + + +# Import standard modules + +import getopt +import os +import string +import sys + + +# Import the freeze-private modules + +import checkextensions +import modulefinder +import makeconfig +import makefreeze +import makemakefile +import parsesetup +import bkfile + + +# Main program + +def main(): + # overridable context + prefix = None # settable with -p option + exec_prefix = None # settable with -P option + extensions = [] + exclude = [] # settable with -x option + addn_link = [] # settable with -l, but only honored under Windows. + path = sys.path[:] + modargs = 0 + debug = 1 + odir = '' + win = sys.platform[:3] == 'win' + + # default the exclude list for each platform + if win: exclude = exclude + [ + 'dos', 'dospath', 'mac', 'macpath', 'macfs', 'MACFS', 'posix', 'os2', 'ce'] + + # modules that are imported by the Python runtime + #implicits = ["site", "exceptions"] + implicits = ["exceptions"] + + # output files + frozen_c = 'frozen.c' + config_c = 'config.c' + target = 'a.out' # normally derived from script name + makefile = 'Makefile.freeze' + subsystem = 'console' + + # parse command line by first replacing any "-i" options with the file contents. + pos = 1 + while pos < len(sys.argv)-1: # last option can not be "-i", so this ensures "pos+1" is in range! + if sys.argv[pos] == '-i': + try: + options = string.split(open(sys.argv[pos+1]).read()) + except IOError, why: + usage("File name '%s' specified with the -i option can not be read - %s" % (sys.argv[pos+1], why) ) + # Replace the '-i' and the filename with the read params. + sys.argv[pos:pos+2] = options + pos = pos + len(options) - 1 # Skip the name and the included args. + pos = pos + 1 + + # Now parse the command line with the extras inserted. + try: + opts, args = getopt.getopt(sys.argv[1:], 'a:de:hmo:p:P:I:qs:wx:l:') + except getopt.error, msg: + usage('getopt error: ' + str(msg)) + + # proces option arguments + for o, a in opts: + if o == '-h': + print __doc__ + return + if o == '-d': + debug = debug + 1 + if o == '-e': + extensions.append(a) + if o == '-I': # include path + path.append(a) + if o == '-m': + modargs = 1 + if o == '-o': + odir = a + if o == '-p': + prefix = a + if o == '-P': + exec_prefix = a + if o == '-q': + debug = 0 + if o == '-w': + win = not win + if o == '-s': + if not win: + usage("-s subsystem option only on Windows") + subsystem = a + if o == '-x': + exclude.append(a) + if o == '-l': + addn_link.append(a) + if o == '-a': + apply(modulefinder.AddPackagePath, tuple(string.split(a,"=", 2))) + + # default prefix and exec_prefix + if not exec_prefix: + if prefix: + exec_prefix = prefix + else: + exec_prefix = sys.exec_prefix + if not prefix: + prefix = sys.prefix + + # determine whether -p points to the Python source tree + ishome = os.path.exists(os.path.join(prefix, 'Python', 'ceval.c')) + + # locations derived from options + version = sys.version[:3] + if win: + extensions_c = 'frozen_extensions.c' + if ishome: + print "(Using Python source directory)" + binlib = exec_prefix + incldir = os.path.join(prefix, 'Include') + config_h_dir = exec_prefix + config_c_in = os.path.join(prefix, 'Modules', 'config.c.in') + frozenmain_c = os.path.join(prefix, 'Python', 'frozenmain.c') + makefile_in = os.path.join(exec_prefix, 'Modules', 'Makefile') + if win: + frozendllmain_c = os.path.join(exec_prefix, 'Pc\\frozen_dllmain.c') + else: + binlib = os.path.join(exec_prefix, + 'lib', 'python%s' % version, 'config') + incldir = os.path.join(prefix, 'include', 'python%s' % version) + config_h_dir = os.path.join(exec_prefix, 'include', + 'python%s' % version) + config_c_in = os.path.join(binlib, 'config.c.in') + frozenmain_c = os.path.join(binlib, 'frozenmain.c') + makefile_in = os.path.join(binlib, 'Makefile') + frozendllmain_c = os.path.join(binlib, 'frozen_dllmain.c') + supp_sources = [] + defines = [] + includes = ['-I' + incldir, '-I' + config_h_dir] + + # sanity check of directories and files + check_dirs = [prefix, exec_prefix, binlib, incldir] + if not win: check_dirs = check_dirs + extensions # These are not directories on Windows. + for dir in check_dirs: + if not os.path.exists(dir): + usage('needed directory %s not found' % dir) + if not os.path.isdir(dir): + usage('%s: not a directory' % dir) + if win: + files = supp_sources + extensions # extensions are files on Windows. + else: + files = [config_c_in, makefile_in] + supp_sources + for file in supp_sources: + if not os.path.exists(file): + usage('needed file %s not found' % file) + if not os.path.isfile(file): + usage('%s: not a plain file' % file) + if not win: + for dir in extensions: + setup = os.path.join(dir, 'Setup') + if not os.path.exists(setup): + usage('needed file %s not found' % setup) + if not os.path.isfile(setup): + usage('%s: not a plain file' % setup) + + # check that enough arguments are passed + if not args: + usage('at least one filename argument required') + + # check that file arguments exist + for arg in args: + if arg == '-m': + break + # if user specified -m on the command line before _any_ + # file names, then nothing should be checked (as the + # very first file should be a module name) + if modargs: + break + if not os.path.exists(arg): + usage('argument %s not found' % arg) + if not os.path.isfile(arg): + usage('%s: not a plain file' % arg) + + # process non-option arguments + scriptfile = args[0] + modules = args[1:] + + # derive target name from script name + base = os.path.basename(scriptfile) + base, ext = os.path.splitext(base) + if base: + if base != scriptfile: + target = base + else: + target = base + '.bin' + + # handle -o option + base_frozen_c = frozen_c + base_config_c = config_c + base_target = target + if odir and not os.path.isdir(odir): + try: + os.mkdir(odir) + print "Created output directory", odir + except os.error, msg: + usage('%s: mkdir failed (%s)' % (odir, str(msg))) + base = '' + if odir: + base = os.path.join(odir, '') + frozen_c = os.path.join(odir, frozen_c) + config_c = os.path.join(odir, config_c) + target = os.path.join(odir, target) + makefile = os.path.join(odir, makefile) + if win: extensions_c = os.path.join(odir, extensions_c) + + # Handle special entry point requirements + # (on Windows, some frozen programs do not use __main__, but + # import the module directly. Eg, DLLs, Services, etc + custom_entry_point = None # Currently only used on Windows + python_entry_is_main = 1 # Is the entry point called __main__? + # handle -s option on Windows + if win: + import winmakemakefile + try: + custom_entry_point, python_entry_is_main = \ + winmakemakefile.get_custom_entry_point(subsystem) + except ValueError, why: + usage(why) + + + # Actual work starts here... + + # collect all modules of the program + dir = os.path.dirname(scriptfile) + path[0] = dir + mf = modulefinder.ModuleFinder(path, debug, exclude) + + if win and subsystem=='service': + # If a Windows service, then add the "built-in" module. + mod = mf.add_module("servicemanager") + mod.__file__="dummy.pyd" # really built-in to the resulting EXE + + for mod in implicits: + mf.import_hook(mod) + for mod in modules: + if mod == '-m': + modargs = 1 + continue + if modargs: + if mod[-2:] == '.*': + mf.import_hook(mod[:-2], None, ["*"]) + else: + mf.import_hook(mod) + else: + mf.load_file(mod) + + # Add the main script as either __main__, or the actual module name. + if python_entry_is_main: + mf.run_script(scriptfile) + else: + mf.load_file(scriptfile) + + if debug > 0: + mf.report() + print + dict = mf.modules + + # generate output for frozen modules + files = makefreeze.makefreeze(base, dict, debug, custom_entry_point, 1) + # look for unfrozen modules (builtin and of unknown origin) + builtins = [] + unknown = [] + mods = dict.keys() + mods.sort() + for mod in mods: + if dict[mod].__code__: + continue + if not dict[mod].__file__: + builtins.append(mod) + else: + unknown.append(mod) + + # search for unknown modules in extensions directories (not on Windows) + addfiles = [] + frozen_extensions = [] # Windows list of modules. + if unknown or (not win and builtins): + if not win: + addfiles, addmods = \ + checkextensions.checkextensions(unknown+builtins, + extensions) + for mod in addmods: + if mod in unknown: + unknown.remove(mod) + builtins.append(mod) + else: + # Do the windows thang... + import checkextensions_win32 + # Get a list of CExtension instances, each describing a module + # (including its source files) + frozen_extensions = checkextensions_win32.checkextensions( + unknown, extensions, prefix) + for mod in frozen_extensions: + unknown.remove(mod.name) + + # report unknown modules + if unknown: + sys.stderr.write('Warning: unknown modules remain: %s\n' % + string.join(unknown)) + + # windows gets different treatment + if win: + # Taking a shortcut here... + import winmakemakefile, checkextensions_win32 + checkextensions_win32.write_extension_table(extensions_c, + frozen_extensions) + # Create a module definition for the bootstrap C code. + xtras = [frozenmain_c, os.path.basename(frozen_c), + frozendllmain_c, os.path.basename(extensions_c)] + files + maindefn = checkextensions_win32.CExtension( '__main__', xtras ) + frozen_extensions.append( maindefn ) + outfp = open(makefile, 'w') + try: + winmakemakefile.makemakefile(outfp, + locals(), + frozen_extensions, + os.path.basename(target)) + finally: + outfp.close() + return + + # generate config.c and Makefile + builtins.sort() + infp = open(config_c_in) + outfp = bkfile.open(config_c, 'w') + try: + makeconfig.makeconfig(infp, outfp, builtins) + finally: + outfp.close() + infp.close() + + cflags = defines + includes + ['$(OPT)'] + libs = [os.path.join(binlib, 'libpython$(VERSION).a')] + + somevars = {} + if os.path.exists(makefile_in): + makevars = parsesetup.getmakevars(makefile_in) + for key in makevars.keys(): + somevars[key] = makevars[key] + + somevars['CFLAGS'] = string.join(cflags) # override + files = ['$(OPT)', '$(LDFLAGS)', base_config_c, base_frozen_c] + \ + files + supp_sources + addfiles + libs + \ + ['$(MODLIBS)', '$(LIBS)', '$(SYSLIBS)'] + + outfp = bkfile.open(makefile, 'w') + try: + makemakefile.makemakefile(outfp, somevars, files, base_target) + finally: + outfp.close() + + # Done! + + if odir: + print 'Now run "make" in', odir, + print 'to build the target:', base_target + else: + print 'Now run "make" to build the target:', base_target + + +# Print usage message and exit + +def usage(msg): + sys.stdout = sys.stderr + print "Error:", msg + print "Use ``%s -h'' for help" % sys.argv[0] + sys.exit(2) + + +main() diff --git a/intern/python/freeze/hello.py b/intern/python/freeze/hello.py new file mode 100644 index 00000000000..f978acc883c --- /dev/null +++ b/intern/python/freeze/hello.py @@ -0,0 +1 @@ +print 'Hello world...' diff --git a/intern/python/freeze/makeconfig.py b/intern/python/freeze/makeconfig.py new file mode 100644 index 00000000000..5d1ba2c333c --- /dev/null +++ b/intern/python/freeze/makeconfig.py @@ -0,0 +1,61 @@ +import regex + + +# Write the config.c file + +never = ['marshal', '__main__', '__builtin__', 'sys', 'exceptions'] + +def makeconfig(infp, outfp, modules, with_ifdef=0): + m1 = regex.compile('-- ADDMODULE MARKER 1 --') + m2 = regex.compile('-- ADDMODULE MARKER 2 --') + while 1: + line = infp.readline() + if not line: break + outfp.write(line) + if m1 and m1.search(line) >= 0: + m1 = None + for mod in modules: + if mod in never: + continue + if with_ifdef: + outfp.write("#ifndef init%s\n"%mod) + outfp.write('extern void init%s();\n' % mod) + if with_ifdef: + outfp.write("#endif\n") + elif m2 and m2.search(line) >= 0: + m2 = None + for mod in modules: + if mod in never: + continue + outfp.write('\t{"%s", init%s},\n' % + (mod, mod)) + if m1: + sys.stderr.write('MARKER 1 never found\n') + elif m2: + sys.stderr.write('MARKER 2 never found\n') + + +# Test program. + +def test(): + import sys + if not sys.argv[3:]: + print 'usage: python makeconfig.py config.c.in outputfile', + print 'modulename ...' + sys.exit(2) + if sys.argv[1] == '-': + infp = sys.stdin + else: + infp = open(sys.argv[1]) + if sys.argv[2] == '-': + outfp = sys.stdout + else: + outfp = open(sys.argv[2], 'w') + makeconfig(infp, outfp, sys.argv[3:]) + if outfp != sys.stdout: + outfp.close() + if infp != sys.stdin: + infp.close() + +if __name__ == '__main__': + test() diff --git a/intern/python/freeze/makefreeze.py b/intern/python/freeze/makefreeze.py new file mode 100644 index 00000000000..8bd9ce1a2e2 --- /dev/null +++ b/intern/python/freeze/makefreeze.py @@ -0,0 +1,115 @@ +## +## +## Customized makefreeze for NaN +## +## +## 1.11.2001, strubi@blender.nl +## +## +import marshal +import string +import bkfile + + +# Write a file containing frozen code for the modules in the dictionary. + +header = """ +#include "Python.h" + +static struct _frozen _PyImport_FrozenModules[] = { +""" +trailer = """\ + {0, 0, 0} /* sentinel */ +}; +""" + +# if __debug__ == 0 (i.e. -O option given), set Py_OptimizeFlag in frozen app. +main_entry_point = """ +int +main(argc, argv) + int argc; + char **argv; +{ + extern int Py_FrozenMain(int, char **); + init_frozen_modules(); + return Py_FrozenMain(argc, argv); +} + +""" + +default_entry_point = """ +void +init_frozenmodules(void) +{ +""" + ((not __debug__ and """ + Py_OptimizeFlag++; +""") or "") + """ + PyImport_FrozenModules = _PyImport_FrozenModules; +} +""" + +HEADER = """ +/* This is a generated file, containing frozen bytecode. + * Check $(HOME)/develop/intern/python/freeze/README for more information. + */ + +""" + +def makefreeze(base, dict, debug=0, entry_point = None, exclude_main = 0): + if entry_point is None: entry_point = default_entry_point + done = [] + files = [] + mods = dict.keys() + if exclude_main: + mods.remove("__main__") + mods.sort() + for mod in mods: + m = dict[mod] + mangled = string.join(string.split(mod, "."), "__") + if m.__code__: + file = 'M_' + mangled + '.c' + outfp = bkfile.open(base + file, 'w') + outfp.write(HEADER) + files.append(file) + if debug: + print "freezing", mod, "..." + str = marshal.dumps(m.__code__) + size = len(str) + if m.__path__: + # Indicate package by negative size + size = -size + done.append((mod, mangled, size)) + writecode(outfp, mangled, str) + outfp.close() + if debug: + print "generating table of frozen modules" + outfp = bkfile.open(base + 'frozen.c', 'w') + for mod, mangled, size in done: + outfp.write('extern unsigned char M_%s[];\n' % mangled) + outfp.write(header) + for mod, mangled, size in done: + outfp.write('\t{"%s", M_%s, %d},\n' % (mod, mangled, size)) + outfp.write(trailer) + outfp.write(entry_point) + outfp.close() + #outfp = bkfile.open(base + 'main.c', 'w') + #outfp.write(main_entry_point) + #outfp.close() + return files + + + +# Write a C initializer for a module containing the frozen python code. +# The array is called M_<mod>. + +def writecode(outfp, mod, str): + outfp.write('unsigned char M_%s[] = {' % mod) + for i in range(0, len(str), 16): + outfp.write('\n\t') + for c in str[i:i+16]: + outfp.write('%d,' % ord(c)) + outfp.write('\n};\n') + +## def writecode(outfp, mod, str): +## outfp.write('unsigned char M_%s[%d] = "%s";\n' % (mod, len(str), +## string.join(map(lambda s: `s`[1:-1], string.split(str, '"')), '\\"'))) diff --git a/intern/python/freeze/makemakefile.py b/intern/python/freeze/makemakefile.py new file mode 100644 index 00000000000..bb2ed43430d --- /dev/null +++ b/intern/python/freeze/makemakefile.py @@ -0,0 +1,57 @@ +# Write the actual Makefile. +## +## +## Customized makemakefile for NaN +## +## +## 1.11.2001, strubi@blender.nl +## +## + + +import os +import string + +def makemakefile(outfp, makevars, files, target): + outfp.write("# Makefile generated by freeze.py script\n\n") + + target = "frozen" + libtarget = "lib" + target + targetlib = libtarget + ".a" + #targetlib = "libpyfrozen.a" + + keys = makevars.keys() + keys.sort() + for key in keys: + outfp.write("%s=%s\n" % (key, makevars[key])) + outfp.write("\nall: %s\n\n" % libtarget) + + deps = [] + for i in range(len(files)): + file = files[i] + if file[-2:] == '.c': + base = os.path.basename(file) + dest = base[:-2] + '.o' + # outfp.write("%s: %s\n" % (dest, file)) + # outfp.write("\t$(CC) $(CFLAGS) -c %s\n" % file) + files[i] = dest + deps.append(dest) + + mainfile = 'M___main__.o' + + try: + deps.remove(mainfile) + except: + pass + outfp.write("OBJS = %s\n" % string.join(deps)) + +# libfiles.remove('M___main__.o') # don't link with __main__ + + outfp.write("\n%s: $(OBJS)\n" % (libtarget)) + outfp.write("\t$(AR) ruv %s $(OBJS)\n" % (targetlib)) + + outfp.write("\n%s: %s $(OBJS)\n" % (target, mainfile)) + outfp.write("\t$(CC) %s %s -o %s $(LDLAST)\n" % + (mainfile, " ".join(deps), target)) + + outfp.write("\nclean:\n\t-rm -f *.o *.a %s\n" % target) diff --git a/intern/python/freeze/modulefinder.py b/intern/python/freeze/modulefinder.py new file mode 100644 index 00000000000..e84a54128f6 --- /dev/null +++ b/intern/python/freeze/modulefinder.py @@ -0,0 +1,444 @@ +"""Find modules used by a script, using introspection.""" + +import dis +import imp +import marshal +import os +import re +import string +import sys + +if sys.platform=="win32": + # On Windows, we can locate modules in the registry with + # the help of the win32api package. + try: + import win32api + except ImportError: + print "The win32api module is not available - modules listed" + print "in the registry will not be found." + win32api = None + + +IMPORT_NAME = dis.opname.index('IMPORT_NAME') +IMPORT_FROM = dis.opname.index('IMPORT_FROM') +STORE_NAME = dis.opname.index('STORE_NAME') +STORE_FAST = dis.opname.index('STORE_FAST') +STORE_GLOBAL = dis.opname.index('STORE_GLOBAL') +STORE_OPS = [STORE_NAME, STORE_FAST, STORE_GLOBAL] + +# Modulefinder does a good job at simulating Python's, but it can not +# handle __path__ modifications packages make at runtime. Therefore there +# is a mechanism whereby you can register extra paths in this map for a +# package, and it will be honored. + +# Note this is a mapping is lists of paths. +packagePathMap = {} + +# A Public interface +def AddPackagePath(packagename, path): + paths = packagePathMap.get(packagename, []) + paths.append(path) + packagePathMap[packagename] = paths + +class Module: + + def __init__(self, name, file=None, path=None): + self.__name__ = name + self.__file__ = file + self.__path__ = path + self.__code__ = None + + def __repr__(self): + s = "Module(%s" % `self.__name__` + if self.__file__ is not None: + s = s + ", %s" % `self.__file__` + if self.__path__ is not None: + s = s + ", %s" % `self.__path__` + s = s + ")" + return s + + +class ModuleFinder: + + def __init__(self, path=None, debug=0, excludes = []): + if path is None: + path = sys.path + self.path = path + self.modules = {} + self.badmodules = {} + self.debug = debug + self.indent = 0 + self.excludes = excludes + + def msg(self, level, str, *args): + if level <= self.debug: + for i in range(self.indent): + print " ", + print str, + for arg in args: + print repr(arg), + print + + def msgin(self, *args): + level = args[0] + if level <= self.debug: + self.indent = self.indent + 1 + apply(self.msg, args) + + def msgout(self, *args): + level = args[0] + if level <= self.debug: + self.indent = self.indent - 1 + apply(self.msg, args) + + def run_script(self, pathname): + self.msg(2, "run_script", pathname) + fp = open(pathname) + stuff = ("", "r", imp.PY_SOURCE) + self.load_module('__main__', fp, pathname, stuff) + + def load_file(self, pathname): + dir, name = os.path.split(pathname) + name, ext = os.path.splitext(name) + fp = open(pathname) + stuff = (ext, "r", imp.PY_SOURCE) + self.load_module(name, fp, pathname, stuff) + + def import_hook(self, name, caller=None, fromlist=None): + self.msg(3, "import_hook", name, caller, fromlist) + parent = self.determine_parent(caller) + q, tail = self.find_head_package(parent, name) + m = self.load_tail(q, tail) + if not fromlist: + return q + if m.__path__: + self.ensure_fromlist(m, fromlist) + + def determine_parent(self, caller): + self.msgin(4, "determine_parent", caller) + if not caller: + self.msgout(4, "determine_parent -> None") + return None + pname = caller.__name__ + if caller.__path__: + parent = self.modules[pname] + assert caller is parent + self.msgout(4, "determine_parent ->", parent) + return parent + if '.' in pname: + i = string.rfind(pname, '.') + pname = pname[:i] + parent = self.modules[pname] + assert parent.__name__ == pname + self.msgout(4, "determine_parent ->", parent) + return parent + self.msgout(4, "determine_parent -> None") + return None + + def find_head_package(self, parent, name): + self.msgin(4, "find_head_package", parent, name) + if '.' in name: + i = string.find(name, '.') + head = name[:i] + tail = name[i+1:] + else: + head = name + tail = "" + if parent: + qname = "%s.%s" % (parent.__name__, head) + else: + qname = head + q = self.import_module(head, qname, parent) + if q: + self.msgout(4, "find_head_package ->", (q, tail)) + return q, tail + if parent: + qname = head + parent = None + q = self.import_module(head, qname, parent) + if q: + self.msgout(4, "find_head_package ->", (q, tail)) + return q, tail + self.msgout(4, "raise ImportError: No module named", qname) + raise ImportError, "No module named " + qname + + def load_tail(self, q, tail): + self.msgin(4, "load_tail", q, tail) + m = q + while tail: + i = string.find(tail, '.') + if i < 0: i = len(tail) + head, tail = tail[:i], tail[i+1:] + mname = "%s.%s" % (m.__name__, head) + m = self.import_module(head, mname, m) + if not m: + self.msgout(4, "raise ImportError: No module named", mname) + raise ImportError, "No module named " + mname + self.msgout(4, "load_tail ->", m) + return m + + def ensure_fromlist(self, m, fromlist, recursive=0): + self.msg(4, "ensure_fromlist", m, fromlist, recursive) + for sub in fromlist: + if sub == "*": + if not recursive: + all = self.find_all_submodules(m) + if all: + self.ensure_fromlist(m, all, 1) + elif not hasattr(m, sub): + subname = "%s.%s" % (m.__name__, sub) + submod = self.import_module(sub, subname, m) + if not submod: + raise ImportError, "No module named " + subname + + def find_all_submodules(self, m): + if not m.__path__: + return + modules = {} + suffixes = [".py", ".pyc", ".pyo"] + for dir in m.__path__: + try: + names = os.listdir(dir) + except os.error: + self.msg(2, "can't list directory", dir) + continue + for name in names: + mod = None + for suff in suffixes: + n = len(suff) + if name[-n:] == suff: + mod = name[:-n] + break + if mod and mod != "__init__": + modules[mod] = mod + return modules.keys() + + def import_module(self, partname, fqname, parent): + self.msgin(3, "import_module", partname, fqname, parent) + try: + m = self.modules[fqname] + except KeyError: + pass + else: + self.msgout(3, "import_module ->", m) + return m + if self.badmodules.has_key(fqname): + self.msgout(3, "import_module -> None") + if parent: + self.badmodules[fqname][parent.__name__] = None + return None + try: + fp, pathname, stuff = self.find_module(partname, + parent and parent.__path__) + except ImportError: + self.msgout(3, "import_module ->", None) + return None + try: + m = self.load_module(fqname, fp, pathname, stuff) + finally: + if fp: fp.close() + if parent: + setattr(parent, partname, m) + self.msgout(3, "import_module ->", m) + return m + + def load_module(self, fqname, fp, pathname, (suffix, mode, type)): + self.msgin(2, "load_module", fqname, fp and "fp", pathname) + if type == imp.PKG_DIRECTORY: + m = self.load_package(fqname, pathname) + self.msgout(2, "load_module ->", m) + return m + if type == imp.PY_SOURCE: + co = compile(fp.read()+'\n', pathname, 'exec') + elif type == imp.PY_COMPILED: + if fp.read(4) != imp.get_magic(): + self.msgout(2, "raise ImportError: Bad magic number", pathname) + raise ImportError, "Bad magic number in %s" % pathname + fp.read(4) + co = marshal.load(fp) + else: + co = None + m = self.add_module(fqname) + m.__file__ = pathname + if co: + m.__code__ = co + self.scan_code(co, m) + self.msgout(2, "load_module ->", m) + return m + + def scan_code(self, co, m): + code = co.co_code + n = len(code) + i = 0 + lastname = None + while i < n: + c = code[i] + i = i+1 + op = ord(c) + if op >= dis.HAVE_ARGUMENT: + oparg = ord(code[i]) + ord(code[i+1])*256 + i = i+2 + if op == IMPORT_NAME: + name = lastname = co.co_names[oparg] + if not self.badmodules.has_key(lastname): + try: + self.import_hook(name, m) + except ImportError, msg: + self.msg(2, "ImportError:", str(msg)) + if not self.badmodules.has_key(name): + self.badmodules[name] = {} + self.badmodules[name][m.__name__] = None + elif op == IMPORT_FROM: + name = co.co_names[oparg] + assert lastname is not None + if not self.badmodules.has_key(lastname): + try: + self.import_hook(lastname, m, [name]) + except ImportError, msg: + self.msg(2, "ImportError:", str(msg)) + fullname = lastname + "." + name + if not self.badmodules.has_key(fullname): + self.badmodules[fullname] = {} + self.badmodules[fullname][m.__name__] = None + elif op in STORE_OPS: + # Skip; each IMPORT_FROM is followed by a STORE_* opcode + pass + else: + lastname = None + for c in co.co_consts: + if isinstance(c, type(co)): + self.scan_code(c, m) + + def load_package(self, fqname, pathname): + self.msgin(2, "load_package", fqname, pathname) + m = self.add_module(fqname) + m.__file__ = pathname + m.__path__ = [pathname] + + # As per comment at top of file, simulate runtime __path__ additions. + m.__path__ = m.__path__ + packagePathMap.get(fqname, []) + + fp, buf, stuff = self.find_module("__init__", m.__path__) + self.load_module(fqname, fp, buf, stuff) + self.msgout(2, "load_package ->", m) + return m + + def add_module(self, fqname): + if self.modules.has_key(fqname): + return self.modules[fqname] + self.modules[fqname] = m = Module(fqname) + return m + + def find_module(self, name, path): + if name in self.excludes: + self.msgout(3, "find_module -> Excluded") + raise ImportError, name + + if path is None: + if name in sys.builtin_module_names: + return (None, None, ("", "", imp.C_BUILTIN)) + + # Emulate the Registered Module support on Windows. + if sys.platform=="win32" and win32api is not None: + HKEY_LOCAL_MACHINE = 0x80000002 + try: + pathname = win32api.RegQueryValue(HKEY_LOCAL_MACHINE, "Software\\Python\\PythonCore\\%s\\Modules\\%s" % (sys.winver, name)) + fp = open(pathname, "rb") + # XXX - To do - remove the hard code of C_EXTENSION. + stuff = "", "rb", imp.C_EXTENSION + return fp, pathname, stuff + except win32api.error: + pass + + path = self.path + return imp.find_module(name, path) + + def report(self): + print + print " %-25s %s" % ("Name", "File") + print " %-25s %s" % ("----", "----") + # Print modules found + keys = self.modules.keys() + keys.sort() + for key in keys: + m = self.modules[key] + if m.__path__: + print "P", + else: + print "m", + print "%-25s" % key, m.__file__ or "" + + # Print missing modules + keys = self.badmodules.keys() + keys.sort() + for key in keys: + # ... but not if they were explicitly excluded. + if key not in self.excludes: + mods = self.badmodules[key].keys() + mods.sort() + print "?", key, "from", string.join(mods, ', ') + + +def test(): + # Parse command line + import getopt + try: + opts, args = getopt.getopt(sys.argv[1:], "dmp:qx:") + except getopt.error, msg: + print msg + return + + # Process options + debug = 1 + domods = 0 + addpath = [] + exclude = [] + for o, a in opts: + if o == '-d': + debug = debug + 1 + if o == '-m': + domods = 1 + if o == '-p': + addpath = addpath + string.split(a, os.pathsep) + if o == '-q': + debug = 0 + if o == '-x': + exclude.append(a) + + # Provide default arguments + if not args: + script = "hello.py" + else: + script = args[0] + + # Set the path based on sys.path and the script directory + path = sys.path[:] + path[0] = os.path.dirname(script) + path = addpath + path + if debug > 1: + print "path:" + for item in path: + print " ", `item` + + # Create the module finder and turn its crank + mf = ModuleFinder(path, debug, exclude) + for arg in args[1:]: + if arg == '-m': + domods = 1 + continue + if domods: + if arg[-2:] == '.*': + mf.import_hook(arg[:-2], None, ["*"]) + else: + mf.import_hook(arg) + else: + mf.load_file(arg) + mf.run_script(script) + mf.report() + + +if __name__ == '__main__': + try: + test() + except KeyboardInterrupt: + print "\n[interrupt]" diff --git a/intern/python/freeze/parsesetup.py b/intern/python/freeze/parsesetup.py new file mode 100644 index 00000000000..1795671e946 --- /dev/null +++ b/intern/python/freeze/parsesetup.py @@ -0,0 +1,98 @@ +# Parse Makefiles and Python Setup(.in) files. + +import regex +import string + + +# Extract variable definitions from a Makefile. +# Return a dictionary mapping names to values. +# May raise IOError. + +makevardef = regex.compile('^\([a-zA-Z0-9_]+\)[ \t]*=\(.*\)') + +def getmakevars(filename): + variables = {} + fp = open(filename) + try: + while 1: + line = fp.readline() + if not line: + break + if makevardef.match(line) < 0: + continue + name, value = makevardef.group(1, 2) + # Strip trailing comment + i = string.find(value, '#') + if i >= 0: + value = value[:i] + value = string.strip(value) + variables[name] = value + finally: + fp.close() + return variables + + +# Parse a Python Setup(.in) file. +# Return two dictionaries, the first mapping modules to their +# definitions, the second mapping variable names to their values. +# May raise IOError. + +setupvardef = regex.compile('^\([a-zA-Z0-9_]+\)=\(.*\)') + +def getsetupinfo(filename): + modules = {} + variables = {} + fp = open(filename) + try: + while 1: + line = fp.readline() + if not line: + break + # Strip comments + i = string.find(line, '#') + if i >= 0: + line = line[:i] + if setupvardef.match(line) >= 0: + name, value = setupvardef.group(1, 2) + variables[name] = string.strip(value) + else: + words = string.split(line) + if words: + modules[words[0]] = words[1:] + finally: + fp.close() + return modules, variables + + +# Test the above functions. + +def test(): + import sys + import os + if not sys.argv[1:]: + print 'usage: python parsesetup.py Makefile*|Setup* ...' + sys.exit(2) + for arg in sys.argv[1:]: + base = os.path.basename(arg) + if base[:8] == 'Makefile': + print 'Make style parsing:', arg + v = getmakevars(arg) + prdict(v) + elif base[:5] == 'Setup': + print 'Setup style parsing:', arg + m, v = getsetupinfo(arg) + prdict(m) + prdict(v) + else: + print arg, 'is neither a Makefile nor a Setup file' + print '(name must begin with "Makefile" or "Setup")' + +def prdict(d): + keys = d.keys() + keys.sort() + for key in keys: + value = d[key] + print "%-15s" % key, str(value) + +if __name__ == '__main__': + test() diff --git a/intern/python/freeze/winmakemakefile.py b/intern/python/freeze/winmakemakefile.py new file mode 100644 index 00000000000..d668a6e8273 --- /dev/null +++ b/intern/python/freeze/winmakemakefile.py @@ -0,0 +1,146 @@ +import sys, os, string + +# Template used then the program is a GUI program +WINMAINTEMPLATE = """ +#include <windows.h> + +int WINAPI WinMain( + HINSTANCE hInstance, // handle to current instance + HINSTANCE hPrevInstance, // handle to previous instance + LPSTR lpCmdLine, // pointer to command line + int nCmdShow // show state of window + ) +{ + extern int Py_FrozenMain(int, char **); + PyImport_FrozenModules = _PyImport_FrozenModules; + return Py_FrozenMain(__argc, __argv); +} +""" + +SERVICETEMPLATE = """ +extern int PythonService_main(int, char **); + +int main( int argc, char **argv) +{ + PyImport_FrozenModules = _PyImport_FrozenModules; + return PythonService_main(argc, argv); +} +""" + +subsystem_details = { + # -s flag : (C entry point template), (is it __main__?), (is it a DLL?) + 'console' : (None, 1, 0), + 'windows' : (WINMAINTEMPLATE, 1, 0), + 'service' : (SERVICETEMPLATE, 0, 0), + 'com_dll' : ("", 0, 1), +} + +def get_custom_entry_point(subsystem): + try: + return subsystem_details[subsystem][:2] + except KeyError: + raise ValueError, "The subsystem %s is not known" % subsystem + + +def makemakefile(outfp, vars, files, target): + save = sys.stdout + try: + sys.stdout = outfp + realwork(vars, files, target) + finally: + sys.stdout = save + +def realwork(vars, moddefns, target): + version_suffix = `sys.version_info[0]`+`sys.version_info[1]` + print "# Makefile for Microsoft Visual C++ generated by freeze.py script" + print + print 'target = %s' % target + print 'pythonhome = %s' % vars['prefix'] + print + print 'DEBUG=0 # Set to 1 to use the _d versions of Python.' + print '!IF $(DEBUG)' + print 'debug_suffix=_d' + print 'c_debug=/Zi /Od /DDEBUG /D_DEBUG' + print 'l_debug=/DEBUG' + print 'temp_dir=Build\\Debug' + print '!ELSE' + print 'debug_suffix=' + print 'c_debug=/Ox' + print 'l_debug=' + print 'temp_dir=Build\\Release' + print '!ENDIF' + print + + print '# The following line assumes you have built Python using the standard instructions' + print '# Otherwise fix the following line to point to the library.' + print 'pythonlib = "$(pythonhome)/pcbuild/python%s$(debug_suffix).lib"' % version_suffix + print + + # We only ever write one "entry point" symbol - either + # "main" or "WinMain". Therefore, there is no need to + # pass a subsystem switch to the linker as it works it + # out all by itself. However, the subsystem _does_ determine + # the file extension and additional linker flags. + target_link_flags = "" + target_ext = ".exe" + if subsystem_details[vars['subsystem']][2]: + target_link_flags = "-dll" + target_ext = ".dll" + + + print "# As the target uses Python%s.dll, we must use this compiler option!" % version_suffix + print "cdl = /MD" + print + print "all: $(target)$(debug_suffix)%s" % (target_ext) + print + + print '$(temp_dir):' + print ' if not exist $(temp_dir)\. mkdir $(temp_dir)' + print + + objects = [] + libs = ["shell32.lib", "comdlg32.lib", "wsock32.lib", "user32.lib", "oleaut32.lib"] + for moddefn in moddefns: + print "# Module", moddefn.name + for file in moddefn.sourceFiles: + base = os.path.basename(file) + base, ext = os.path.splitext(base) + objects.append(base + ".obj") + print '$(temp_dir)\%s.obj: "%s"' % (base, file) + print "\t@$(CC) -c -nologo /Fo$* $(cdl) $(c_debug) /D BUILD_FREEZE", + print '"-I$(pythonhome)/Include" "-I$(pythonhome)/PC" \\' + print "\t\t$(cflags) $(cdebug) $(cinclude) \\" + extra = moddefn.GetCompilerOptions() + if extra: + print "\t\t%s \\" % (string.join(extra),) + print '\t\t"%s"' % file + print + + # Add .lib files this module needs + for modlib in moddefn.GetLinkerLibs(): + if modlib not in libs: + libs.append(modlib) + + print "ADDN_LINK_FILES=", + for addn in vars['addn_link']: print '"%s"' % (addn), + print ; print + + print "OBJS=", + for obj in objects: print '"$(temp_dir)\%s"' % (obj), + print ; print + + print "LIBS=", + for lib in libs: print '"%s"' % (lib), + print ; print + + print "$(target)$(debug_suffix)%s: $(temp_dir) $(OBJS)" % (target_ext) + print "\tlink -out:$(target)$(debug_suffix)%s %s" % (target_ext, target_link_flags), + print "\t$(OBJS) \\" + print "\t$(LIBS) \\" + print "\t$(ADDN_LINK_FILES) \\" + print "\t$(pythonlib) $(lcustom) $(l_debug)\\" + print "\t$(resources)" + print + print "clean:" + print "\t-rm -f *.obj" + print "\t-rm -f $(target).exe" |