mirror of https://github.com/python/cpython
Totally new "freeze" program.
This commit is contained in:
parent
dbaf332107
commit
00ff4336a9
|
@ -0,0 +1,128 @@
|
||||||
|
# Determine the names and filenames of the modules imported by a
|
||||||
|
# script, recursively. This is done by scanning for lines containing
|
||||||
|
# import statements. (The scanning has only superficial knowledge of
|
||||||
|
# Python syntax and no knowledge of semantics, so in theory the result
|
||||||
|
# may be incorrect -- however this is quite unlikely if you don't
|
||||||
|
# intentionally obscure your Python code.)
|
||||||
|
|
||||||
|
import os
|
||||||
|
import regex
|
||||||
|
import string
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
# Top-level interface.
|
||||||
|
# First argument is the main program (script).
|
||||||
|
# Second optional argument is list of modules to be searched as well.
|
||||||
|
|
||||||
|
def findmodules(scriptfile, modules = [], path = sys.path):
|
||||||
|
todo = {}
|
||||||
|
todo['__main__'] = scriptfile
|
||||||
|
for name in modules:
|
||||||
|
mod = os.path.basename(name)
|
||||||
|
if mod[-3:] == '.py': mod = mod[:-3]
|
||||||
|
todo[mod] = name
|
||||||
|
done = closure(todo)
|
||||||
|
return done
|
||||||
|
|
||||||
|
|
||||||
|
# Compute the closure of scanfile() and findmodule().
|
||||||
|
# Return a dictionary mapping module names to filenames.
|
||||||
|
# Writes to stderr if a file can't be or read.
|
||||||
|
|
||||||
|
def closure(todo):
|
||||||
|
done = {}
|
||||||
|
while todo:
|
||||||
|
newtodo = {}
|
||||||
|
for modname in todo.keys():
|
||||||
|
if not done.has_key(modname):
|
||||||
|
filename = todo[modname]
|
||||||
|
if filename is None:
|
||||||
|
filename = findmodule(modname)
|
||||||
|
done[modname] = filename
|
||||||
|
if filename in ('<builtin>', '<unknown>'):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
modules = scanfile(filename)
|
||||||
|
except IOError, msg:
|
||||||
|
sys.stderr.write("%s: %s\n" %
|
||||||
|
(filename, str(msg)))
|
||||||
|
continue
|
||||||
|
for m in modules:
|
||||||
|
if not done.has_key(m):
|
||||||
|
newtodo[m] = None
|
||||||
|
todo = newtodo
|
||||||
|
return done
|
||||||
|
|
||||||
|
|
||||||
|
# Scan a file looking for import statements.
|
||||||
|
# Return list of module names.
|
||||||
|
# Can raise IOError.
|
||||||
|
|
||||||
|
importstr = '\(^\|:\)[ \t]*import[ \t]+\([a-zA-Z0-9_, \t]+\)'
|
||||||
|
fromstr = '\(^\|:\)[ \t]*from[ \t]+\([a-zA-Z0-9_]+\)[ \t]+import[ \t]+'
|
||||||
|
isimport = regex.compile(importstr)
|
||||||
|
isfrom = regex.compile(fromstr)
|
||||||
|
|
||||||
|
def scanfile(filename):
|
||||||
|
allmodules = {}
|
||||||
|
f = open(filename, 'r')
|
||||||
|
try:
|
||||||
|
while 1:
|
||||||
|
line = f.readline()
|
||||||
|
if not line: break # EOF
|
||||||
|
while line[-2:] == '\\\n': # Continuation line
|
||||||
|
line = line[:-2] + ' '
|
||||||
|
line = line + f.readline()
|
||||||
|
if isimport.search(line) >= 0:
|
||||||
|
rawmodules = isimport.group(2)
|
||||||
|
modules = string.splitfields(rawmodules, ',')
|
||||||
|
for i in range(len(modules)):
|
||||||
|
modules[i] = string.strip(modules[i])
|
||||||
|
elif isfrom.search(line) >= 0:
|
||||||
|
modules = [isfrom.group(2)]
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
for mod in modules:
|
||||||
|
allmodules[mod] = None
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
return allmodules.keys()
|
||||||
|
|
||||||
|
|
||||||
|
# Find the file containing a module, given its name.
|
||||||
|
# Return filename, or '<builtin>', or '<unknown>'.
|
||||||
|
|
||||||
|
builtins = sys.builtin_module_names
|
||||||
|
if 'sys' not in builtins: builtins.append('sys')
|
||||||
|
# XXX this table may have to be changed depending on your platform:
|
||||||
|
tails = ['.so', 'module.so', '.py', '.pyc']
|
||||||
|
|
||||||
|
def findmodule(modname, path = sys.path):
|
||||||
|
if modname in builtins: return '<builtin>'
|
||||||
|
for dirname in path:
|
||||||
|
for tail in tails:
|
||||||
|
fullname = os.path.join(dirname, modname + tail)
|
||||||
|
try:
|
||||||
|
f = open(fullname, 'r')
|
||||||
|
except IOError:
|
||||||
|
continue
|
||||||
|
f.close()
|
||||||
|
return fullname
|
||||||
|
return '<unknown>'
|
||||||
|
|
||||||
|
|
||||||
|
# Test the above functions.
|
||||||
|
|
||||||
|
def test():
|
||||||
|
if not sys.argv[1:]:
|
||||||
|
print 'usage: python findmodules.py scriptfile [morefiles ...]'
|
||||||
|
sys.exit(2)
|
||||||
|
done = findmodules(sys.argv[1], sys.argv[2:])
|
||||||
|
items = done.items()
|
||||||
|
items.sort()
|
||||||
|
for mod, file in [('Module', 'File')] + items:
|
||||||
|
print "%-15s %s" % (mod, file)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test()
|
|
@ -1,576 +1,176 @@
|
||||||
#! /usr/local/bin/python
|
#! /usr/local/bin/python
|
||||||
|
|
||||||
# Given a Python script, create a binary that runs the script.
|
# "Freeze" a Python script into a binary.
|
||||||
# The binary is 100% independent of Python libraries and binaries.
|
# Usage: see first function below (before the imports!)
|
||||||
# It will not contain any Python source code -- only "compiled" Python
|
|
||||||
# (as initialized static variables containing marshalled code objects).
|
# HINTS:
|
||||||
# It even does the right thing for dynamically loaded modules!
|
# - Edit the line at XXX below before running!
|
||||||
# The module search path of the binary is set to the current directory.
|
# - You must have done "make inclinstall libainstall" in the Python
|
||||||
#
|
# build directory.
|
||||||
# Some problems remain:
|
# - The script should not use dynamically loaded modules
|
||||||
# - You need to have the Python source tree lying around as well as
|
# (*.so on most systems).
|
||||||
# the various libraries used to generate the Python binary.
|
|
||||||
# - For scripts that use many modules it generates absurdly large
|
|
||||||
# files (frozen.c and config.o as well as the final binary),
|
# XXX Change the following line to point to your Demo/freeze directory!
|
||||||
# and is consequently rather slow.
|
pack = '/ufs/guido/src/python/Demo/freeze'
|
||||||
#
|
|
||||||
# Caveats:
|
|
||||||
# - The search for modules sometimes finds modules that are never
|
# Print usage message and exit
|
||||||
# actually imported since the code importing them is never executed.
|
|
||||||
# - If an imported module isn't found, you get a warning but the
|
def usage(msg = None):
|
||||||
# process of freezing continues. The binary will fail if it
|
if msg:
|
||||||
# actually tries to import one of these modules.
|
sys.stderr.write(str(msg) + '\n')
|
||||||
# - This often happens with the module 'mac', which module 'os' tries
|
sys.stderr.write('usage: freeze [-p prefix] script [module] ...\n')
|
||||||
# to import (to determine whether it is running on a Macintosh).
|
sys.exit(2)
|
||||||
# You can ignore the warning about this.
|
|
||||||
# - If the program dynamically reads or generates Python code and
|
|
||||||
# executes it, this code may reference built-in or library modules
|
# Import standard modules
|
||||||
# that aren't present in the frozen binary, and this will fail.
|
|
||||||
# - Your program may be using external data files, e.g. compiled
|
|
||||||
# forms definitions (*.fd). These aren't incorporated. By default,
|
|
||||||
# the sys.path in the resulting binary is only '.' (but you can override
|
|
||||||
# that with the -P option).
|
|
||||||
#
|
|
||||||
# Usage hints:
|
|
||||||
# - If you have a bunch of scripts that you want to freeze, instead
|
|
||||||
# of freezing each of them separately, you might consider writing
|
|
||||||
# a tiny main script that looks at sys.argv[0] and then imports
|
|
||||||
# the corresponding module. You can then make links to the
|
|
||||||
# frozen binary named after the various scripts you support.
|
|
||||||
# Pass the additional scripts as arguments after the main script.
|
|
||||||
# A minimal script to do this is the following.
|
|
||||||
# import sys, posixpath
|
|
||||||
# exec('import ' + posixpath.basename(sys.argv[0]) + '\n')
|
|
||||||
#
|
|
||||||
# Mods by Jack, August 94:
|
|
||||||
# - Removed all static configuration stuff. Now, Setup and Makefile files
|
|
||||||
# are parsed to obtain the linking info for the libraries. You have to
|
|
||||||
# supply the -B option, though.
|
|
||||||
# - Added -P (set sys.path) and -I/-D/-L/-l options (passed on to cc and
|
|
||||||
# ld).
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import regex
|
|
||||||
import getopt
|
import getopt
|
||||||
import regsub
|
import os
|
||||||
import string
|
import string
|
||||||
import marshal
|
import sys
|
||||||
|
import addpack
|
||||||
|
|
||||||
# Exception used when scanfile fails
|
|
||||||
NoSuchFile = 'NoSuchFile'
|
|
||||||
|
|
||||||
# Global options
|
# Set the directory to look for the freeze-private modules
|
||||||
builddir = '' # -B dir
|
|
||||||
quiet = 0 # -q
|
|
||||||
verbose = 0 # -v
|
|
||||||
noexec = 0 # -n
|
|
||||||
nowrite = 0 # -N
|
|
||||||
ofile = 'a.out' # -o file
|
|
||||||
path = '\'"."\'' # -P path
|
|
||||||
|
|
||||||
cc_options = [] # Collects cc options
|
dir = os.path.dirname(sys.argv[0])
|
||||||
ld_options = [] # Collects ld options
|
if dir:
|
||||||
module_libraries = {} # ld options for each module
|
pack = dir
|
||||||
global_libraries = [] # Libraries we always need
|
addpack.addpack(pack)
|
||||||
include_path = '' # Include path, from Makefile
|
|
||||||
lib_path = '' # and lib path, ditto
|
|
||||||
compiler = 'cc' # and compiler
|
# Import the freeze-private modules
|
||||||
|
|
||||||
|
import findmodules
|
||||||
|
import makeconfig
|
||||||
|
import makefreeze
|
||||||
|
import makemakefile
|
||||||
|
import parsesetup
|
||||||
|
|
||||||
|
hint = """
|
||||||
|
Use the '-p prefix' command line option to specify the prefix used
|
||||||
|
when you ran 'Make inclinstall libainstall' in the Python build directory.
|
||||||
|
(Please specify an absolute path.)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
# Main program
|
||||||
|
|
||||||
# Main program -- argument parsing etc.
|
|
||||||
def main():
|
def main():
|
||||||
global quiet, verbose, noexec, nowrite, ofile, builddir, path
|
# overridable context
|
||||||
|
prefix = '/usr/local' # settable with -p option
|
||||||
|
path = sys.path
|
||||||
|
|
||||||
|
# output files
|
||||||
|
frozen_c = 'frozen.c'
|
||||||
|
config_c = 'config.c'
|
||||||
|
target = 'a.out' # normally derived from script name
|
||||||
|
makefile = 'Makefile'
|
||||||
|
|
||||||
|
# parse command line
|
||||||
try:
|
try:
|
||||||
opts, args = getopt.getopt(sys.argv[1:], 'B:nNo:P:qvI:D:L:l:')
|
opts, args = getopt.getopt(sys.argv[1:], 'p:')
|
||||||
|
if not args:
|
||||||
|
raise getopt.error, 'not enough arguments'
|
||||||
except getopt.error, msg:
|
except getopt.error, msg:
|
||||||
usage(str(msg))
|
usage('getopt error: ' + str(msg))
|
||||||
sys.exit(2)
|
|
||||||
|
# proces option arguments
|
||||||
for o, a in opts:
|
for o, a in opts:
|
||||||
if o == '-B': builddir = a
|
if o == '-p':
|
||||||
if o == '-n': noexec = 1
|
prefix = a
|
||||||
if o == '-N': nowrite = 1
|
|
||||||
if o == '-o': ofile = a
|
|
||||||
if o == '-P':
|
|
||||||
if '"' in a:
|
|
||||||
usage('sorry, cannot have " in -P option')
|
|
||||||
sys.exit(2)
|
|
||||||
path = `'"' + a + '"'`
|
|
||||||
if o == '-q': verbose = 0; quiet = 1
|
|
||||||
if o == '-v': verbose = verbose + 1; quiet = 0
|
|
||||||
if o in ('-I', '-D'): cc_options.append(o+a)
|
|
||||||
if o in ('-L', '-l'): ld_options.append(o+a)
|
|
||||||
if not builddir:
|
|
||||||
usage('sorry, you have to pass a -B option')
|
|
||||||
sys.exit(2)
|
|
||||||
if len(args) < 1:
|
|
||||||
usage('please pass at least one file argument')
|
|
||||||
sys.exit(2)
|
|
||||||
process(args[0], args[1:])
|
|
||||||
|
|
||||||
# Print usage message to stderr
|
# locations derived from options
|
||||||
def usage(*msgs):
|
binlib = os.path.join(prefix, 'lib/python/lib')
|
||||||
sys.stdout = sys.stderr
|
incldir = os.path.join(prefix, 'include/Py')
|
||||||
for msg in msgs: print msg
|
config_c_in = os.path.join(binlib, 'config.c.in')
|
||||||
print 'Usage: freeze [options] scriptfile [modulefile ...]'
|
frozenmain_c = os.path.join(binlib, 'frozenmain.c')
|
||||||
print '-B dir : name of python build dir (no default)'
|
makefile_in = os.path.join(binlib, 'Makefile')
|
||||||
print '-n : generate the files but don\'t compile and link'
|
defines = ['-DHAVE_CONFIG_H', '-DUSE_FROZEN', '-DNO_MAIN',
|
||||||
print '-N : don\'t write frozen.c (do compile unless -n given)'
|
'-DPTHONPATH=\\"$(PYTHONPATH)\\"']
|
||||||
print '-o file : binary output file (default a.out)'
|
includes = ['-I' + incldir, '-I' + binlib]
|
||||||
print '-P path : set sys.path for program (default ".")'
|
|
||||||
print '-q : quiet (no messages at all except errors)'
|
|
||||||
print '-v : verbose (lots of extra messages)'
|
|
||||||
print '-D and -I options are passed to cc, -L and -l to ld'
|
|
||||||
|
|
||||||
# Process the script file
|
# sanity check of locations
|
||||||
def process(filename, addmodules):
|
for dir in prefix, binlib, incldir:
|
||||||
global noexec
|
if not os.path.exists(dir):
|
||||||
#
|
usage('needed directory %s not found' % dir + hint)
|
||||||
if not quiet: print 'Computing needed modules ...'
|
if not os.path.isdir(dir):
|
||||||
todo = {}
|
usage('%s: not a directory' % dir)
|
||||||
todo['__main__'] = filename
|
for file in config_c_in, makefile_in, frozenmain_c:
|
||||||
for name in addmodules:
|
if not os.path.exists(file):
|
||||||
mod = os.path.basename(name)
|
usage('needed file %s not found' % file)
|
||||||
if mod[-3:] == '.py': mod = mod[:-3]
|
if not os.path.isfile(file):
|
||||||
todo[mod] = name
|
usage('%s: not a plain file' % file)
|
||||||
try:
|
|
||||||
dict = closure(todo)
|
# check that file arguments exist
|
||||||
except NoSuchFile, filename:
|
for arg in args:
|
||||||
sys.stderr.write('Can\'t open file %s\n' % filename)
|
if not os.path.exists(arg):
|
||||||
sys.exit(1)
|
usage('argument %s not found' % arg)
|
||||||
#
|
if not os.path.isfile(arg):
|
||||||
mods = dict.keys()
|
usage('%s: not a plain file' % arg)
|
||||||
mods.sort()
|
|
||||||
#
|
# process non-option arguments
|
||||||
if verbose:
|
scriptfile = args[0]
|
||||||
print '%-15s %s' % ('Module', 'Filename')
|
modules = args[1:]
|
||||||
for mod in mods:
|
|
||||||
print '%-15s %s' % (`mod`, dict[mod])
|
# derive target name from script name
|
||||||
#
|
base = os.path.basename(scriptfile)
|
||||||
if not quiet: print 'Looking for dynamically linked modules ...'
|
base, ext = os.path.splitext(base)
|
||||||
dlmodules = []
|
if base:
|
||||||
objs = []
|
if base != scriptfile:
|
||||||
libs = []
|
target = base
|
||||||
for mod in mods:
|
|
||||||
if dict[mod][-2:] == '.o':
|
|
||||||
if verbose: print 'Found', mod, dict[mod]
|
|
||||||
dlmodules.append(mod)
|
|
||||||
objs.append(dict[mod])
|
|
||||||
libsname = dict[mod][:-2] + '.libs'
|
|
||||||
try:
|
|
||||||
f = open(libsname, 'r')
|
|
||||||
except IOError:
|
|
||||||
f = None
|
|
||||||
if f:
|
|
||||||
libtext = f.read()
|
|
||||||
f.close()
|
|
||||||
for lib in string.split(libtext):
|
|
||||||
if lib in libs: libs.remove(lib)
|
|
||||||
libs.append(lib)
|
|
||||||
#
|
|
||||||
if not nowrite:
|
|
||||||
if not quiet: print 'Writing frozen.c ...'
|
|
||||||
writefrozen('frozen.c', dict)
|
|
||||||
else:
|
|
||||||
if not quiet: print 'NOT writing frozen.c ...'
|
|
||||||
#
|
|
||||||
if not quiet:
|
|
||||||
print 'Deducing compile/link options from', builddir
|
|
||||||
#
|
|
||||||
# Parse the config info
|
|
||||||
#
|
|
||||||
parse(builddir)
|
|
||||||
CONFIG_IN = lib_path + '/config.c.in'
|
|
||||||
FMAIN = lib_path + '/frozenmain.c'
|
|
||||||
CC = compiler
|
|
||||||
#
|
|
||||||
## if not dlmodules:
|
|
||||||
if 0:
|
|
||||||
config = CONFIG
|
|
||||||
if not quiet: print 'Using existing', config, '...'
|
|
||||||
else:
|
|
||||||
config = 'tmpconfig.c'
|
|
||||||
if nowrite:
|
|
||||||
if not quiet: print 'NOT writing config.c ...'
|
|
||||||
else:
|
else:
|
||||||
if not quiet:
|
target = base + '.bin'
|
||||||
print 'Writing config.c with dl modules ...'
|
|
||||||
f = open(CONFIG_IN, 'r')
|
# Actual work starts here...
|
||||||
g = open(config, 'w')
|
|
||||||
m1 = regex.compile('-- ADDMODULE MARKER 1 --')
|
dict = findmodules.findmodules(scriptfile, modules, path)
|
||||||
m2 = regex.compile('-- ADDMODULE MARKER 2 --')
|
|
||||||
builtinmodules = []
|
builtins = []
|
||||||
stdmodules = ('sys', '__main__', '__builtin__',
|
|
||||||
'marshal')
|
|
||||||
for mod in dict.keys():
|
|
||||||
if dict[mod] == '<builtin>' and \
|
|
||||||
mod not in stdmodules:
|
|
||||||
builtinmodules.append(mod)
|
|
||||||
todomodules = builtinmodules + dlmodules
|
|
||||||
while 1:
|
|
||||||
line = f.readline()
|
|
||||||
if not line: break
|
|
||||||
g.write(line)
|
|
||||||
if m1.search(line) >= 0:
|
|
||||||
if verbose: print 'Marker 1 ...'
|
|
||||||
for mod in todomodules:
|
|
||||||
g.write('extern void init' + \
|
|
||||||
mod + '();\n')
|
|
||||||
if m2.search(line) >= 0:
|
|
||||||
if verbose: print 'Marker 2 ...'
|
|
||||||
for mod in todomodules:
|
|
||||||
g.write('{"' + mod + \
|
|
||||||
'", init' + mod + '},\n')
|
|
||||||
g.close()
|
|
||||||
#
|
|
||||||
if not quiet:
|
|
||||||
if noexec: print 'Generating compilation commands ...'
|
|
||||||
else: print 'Starting compilation ...'
|
|
||||||
defs = ['-DNO_MAIN', '-DUSE_FROZEN']
|
|
||||||
defs.append('-DPYTHONPATH='+path)
|
|
||||||
#
|
|
||||||
incs = ['-I.', '-I' + include_path]
|
|
||||||
# if dict.has_key('stdwin'):
|
|
||||||
# incs.append('-I' + j(STDWIN, 'H'))
|
|
||||||
#
|
|
||||||
srcs = [config, FMAIN]
|
|
||||||
#
|
|
||||||
modlibs = module_libraries
|
|
||||||
|
|
||||||
for mod in dict.keys():
|
for mod in dict.keys():
|
||||||
if modlibs.has_key(mod):
|
if dict[mod] == '<builtin>':
|
||||||
libs = libs + modlibs[mod]
|
builtins.append(mod)
|
||||||
|
|
||||||
libs = libs + global_libraries
|
outfp = open(frozen_c, 'w')
|
||||||
#
|
|
||||||
# remove dups:
|
|
||||||
# XXXX Not well tested...
|
|
||||||
nskip = 0
|
|
||||||
newlibs = []
|
|
||||||
while libs:
|
|
||||||
l = libs[0]
|
|
||||||
del libs[0]
|
|
||||||
if l[:2] == '-L' and l in newlibs:
|
|
||||||
nskip = nskip + 1
|
|
||||||
continue
|
|
||||||
if (l[:2] == '-l' or l[-2:] == '.a') and l in libs:
|
|
||||||
nskip = nskip + 1
|
|
||||||
continue
|
|
||||||
newlibs.append(l)
|
|
||||||
libs = newlibs
|
|
||||||
if nskip and not quiet:
|
|
||||||
print 'Removed %d duplicate libraries'%nskip
|
|
||||||
#
|
|
||||||
sts = 0
|
|
||||||
#
|
|
||||||
cmd = CC + ' -c'
|
|
||||||
if cc_options:
|
|
||||||
cmd = cmd + ' ' + string.join(cc_options)
|
|
||||||
cmd = cmd + ' ' + string.join(defs)
|
|
||||||
cmd = cmd + ' ' + string.join(incs)
|
|
||||||
cmd = cmd + ' ' + string.join(srcs)
|
|
||||||
print cmd
|
|
||||||
#
|
|
||||||
if not noexec:
|
|
||||||
sts = os.system(cmd)
|
|
||||||
if sts:
|
|
||||||
print 'Exit status', sts, '-- turning on -n'
|
|
||||||
noexec = 1
|
|
||||||
#
|
|
||||||
for s in srcs:
|
|
||||||
s = os.path.basename(s)
|
|
||||||
if s[-2:] == '.c': s = s[:-2]
|
|
||||||
o = s + '.o'
|
|
||||||
objs.insert(0, o)
|
|
||||||
#
|
|
||||||
cmd = CC
|
|
||||||
cmd = cmd + ' ' + string.join(objs)
|
|
||||||
cmd = cmd + ' ' + string.join(libs)
|
|
||||||
if ld_options:
|
|
||||||
cmd = cmd + ' ' + string.join(ld_options)
|
|
||||||
cmd = cmd + ' -o ' + ofile
|
|
||||||
print cmd
|
|
||||||
#
|
|
||||||
if not noexec:
|
|
||||||
sts = os.system(cmd)
|
|
||||||
if sts:
|
|
||||||
print 'Exit status', sts
|
|
||||||
else:
|
|
||||||
print 'Done.'
|
|
||||||
#
|
|
||||||
if not quiet and not noexec and sts == 0:
|
|
||||||
print 'Note: consider this:'; print '\tstrip', ofile
|
|
||||||
#
|
|
||||||
sys.exit(sts)
|
|
||||||
|
|
||||||
|
|
||||||
# Generate code for a given module
|
|
||||||
def makecode(filename):
|
|
||||||
if filename[-2:] == '.o':
|
|
||||||
return None
|
|
||||||
try:
|
try:
|
||||||
f = open(filename, 'r')
|
makefreeze.makefreeze(outfp, dict)
|
||||||
except IOError:
|
|
||||||
return None
|
|
||||||
if verbose: print 'Making code from', filename, '...'
|
|
||||||
text = f.read()
|
|
||||||
code = compile(text, filename, 'exec')
|
|
||||||
f.close()
|
|
||||||
return marshal.dumps(code)
|
|
||||||
|
|
||||||
|
|
||||||
# Write the C source file containing the frozen Python code
|
|
||||||
def writefrozen(filename, dict):
|
|
||||||
f = open(filename, 'w')
|
|
||||||
codelist = []
|
|
||||||
for mod in dict.keys():
|
|
||||||
codestring = makecode(dict[mod])
|
|
||||||
if codestring is not None:
|
|
||||||
codelist.append((mod, codestring))
|
|
||||||
write = sys.stdout.write
|
|
||||||
save_stdout = sys.stdout
|
|
||||||
try:
|
|
||||||
sys.stdout = f
|
|
||||||
for mod, codestring in codelist:
|
|
||||||
if verbose:
|
|
||||||
write('Writing initializer for %s\n'%mod)
|
|
||||||
print 'static unsigned char M_' + mod + '[' + \
|
|
||||||
str(len(codestring)) + '+1] = {'
|
|
||||||
for i in range(0, len(codestring), 16):
|
|
||||||
for c in codestring[i:i+16]:
|
|
||||||
print str(ord(c)) + ',',
|
|
||||||
print
|
|
||||||
print '};'
|
|
||||||
print 'struct frozen {'
|
|
||||||
print ' char *name;'
|
|
||||||
print ' unsigned char *code;'
|
|
||||||
print ' int size;'
|
|
||||||
print '} frozen_modules[] = {'
|
|
||||||
for mod, codestring in codelist:
|
|
||||||
print ' {"' + mod + '",',
|
|
||||||
print 'M_' + mod + ',',
|
|
||||||
print str(len(codestring)) + '},'
|
|
||||||
print ' {0, 0, 0} /* sentinel */'
|
|
||||||
print '};'
|
|
||||||
finally:
|
finally:
|
||||||
sys.stdout = save_stdout
|
outfp.close()
|
||||||
f.close()
|
|
||||||
|
|
||||||
|
infp = open(config_c_in)
|
||||||
# Determine the names and filenames of the modules imported by the
|
outfp = open(config_c, 'w')
|
||||||
# script, recursively. This is done by scanning for lines containing
|
|
||||||
# import statements. (The scanning has only superficial knowledge of
|
|
||||||
# Python syntax and no knowledge of semantics, so in theory the result
|
|
||||||
# may be incorrect -- however this is quite unlikely if you don't
|
|
||||||
# intentionally obscure your Python code.)
|
|
||||||
|
|
||||||
# Compute the closure of scanfile() -- special first file because of script
|
|
||||||
def closure(todo):
|
|
||||||
done = {}
|
|
||||||
while todo:
|
|
||||||
newtodo = {}
|
|
||||||
for modname in todo.keys():
|
|
||||||
if not done.has_key(modname):
|
|
||||||
filename = todo[modname]
|
|
||||||
if filename is None:
|
|
||||||
filename = findmodule(modname)
|
|
||||||
done[modname] = filename
|
|
||||||
if filename in ('<builtin>', '<unknown>'):
|
|
||||||
continue
|
|
||||||
modules = scanfile(filename)
|
|
||||||
for m in modules:
|
|
||||||
if not done.has_key(m):
|
|
||||||
newtodo[m] = None
|
|
||||||
todo = newtodo
|
|
||||||
return done
|
|
||||||
|
|
||||||
# Scan a file looking for import statements
|
|
||||||
importstr = '\(^\|:\)[ \t]*import[ \t]+\([a-zA-Z0-9_, \t]+\)'
|
|
||||||
fromstr = '\(^\|:\)[ \t]*from[ \t]+\([a-zA-Z0-9_]+\)[ \t]+import[ \t]+'
|
|
||||||
isimport = regex.compile(importstr)
|
|
||||||
isfrom = regex.compile(fromstr)
|
|
||||||
def scanfile(filename):
|
|
||||||
allmodules = {}
|
|
||||||
try:
|
try:
|
||||||
f = open(filename, 'r')
|
makeconfig.makeconfig(infp, outfp, builtins)
|
||||||
except IOError, msg:
|
finally:
|
||||||
raise NoSuchFile, filename
|
outfp.close()
|
||||||
while 1:
|
infp.close()
|
||||||
line = f.readline()
|
|
||||||
if not line: break # EOF
|
|
||||||
while line[-2:] == '\\\n': # Continuation line
|
|
||||||
line = line[:-2] + ' '
|
|
||||||
line = line + f.readline()
|
|
||||||
if isimport.search(line) >= 0:
|
|
||||||
rawmodules = isimport.group(2)
|
|
||||||
modules = string.splitfields(rawmodules, ',')
|
|
||||||
for i in range(len(modules)):
|
|
||||||
modules[i] = string.strip(modules[i])
|
|
||||||
elif isfrom.search(line) >= 0:
|
|
||||||
modules = [isfrom.group(2)]
|
|
||||||
else:
|
|
||||||
continue
|
|
||||||
for mod in modules:
|
|
||||||
allmodules[mod] = None
|
|
||||||
f.close()
|
|
||||||
return allmodules.keys()
|
|
||||||
|
|
||||||
# Find the file containing a module, given its name; None if not found
|
cflags = defines + includes + ['$(OPT)']
|
||||||
builtins = sys.builtin_module_names + ['sys']
|
libs = []
|
||||||
def findmodule(modname):
|
for n in 'Modules', 'Python', 'Objects', 'Parser':
|
||||||
if modname in builtins: return '<builtin>'
|
n = 'lib%s.a' % n
|
||||||
for dirname in sys.path:
|
n = os.path.join(binlib, n)
|
||||||
dlfullname = os.path.join(dirname, modname + 'module.o')
|
libs.append(n)
|
||||||
try:
|
|
||||||
f = open(dlfullname, 'r')
|
|
||||||
except IOError:
|
|
||||||
f = None
|
|
||||||
if f:
|
|
||||||
f.close()
|
|
||||||
return dlfullname
|
|
||||||
fullname = os.path.join(dirname, modname + '.py')
|
|
||||||
try:
|
|
||||||
f = open(fullname, 'r')
|
|
||||||
except IOError:
|
|
||||||
continue
|
|
||||||
f.close()
|
|
||||||
return fullname
|
|
||||||
if not quiet:
|
|
||||||
sys.stderr.write('Warning: module %s not found\n' % modname)
|
|
||||||
return '<unknown>'
|
|
||||||
#
|
|
||||||
# Parse a setup file. Returns two dictionaries, one containing variables
|
|
||||||
# defined with their values and one containing module definitions
|
|
||||||
#
|
|
||||||
def parse_setup(fp):
|
|
||||||
modules = {}
|
|
||||||
variables = {}
|
|
||||||
for line in fp.readlines():
|
|
||||||
if '#' in line: # Strip comments
|
|
||||||
line = string.splitfields(line, '#')[0]
|
|
||||||
line = string.strip(line[:-1]) # Strip whitespace
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
words = string.split(line)
|
|
||||||
if '=' in words[0]:
|
|
||||||
#
|
|
||||||
# equal sign before first space. Definition
|
|
||||||
#
|
|
||||||
pos = string.index(line, '=')
|
|
||||||
name = line[:pos]
|
|
||||||
value = string.strip(line[pos+1:])
|
|
||||||
variables[name] = value
|
|
||||||
else:
|
|
||||||
modules[words[0]] = words[1:]
|
|
||||||
return modules, variables
|
|
||||||
#
|
|
||||||
# Parse a makefile. Returns a list of the variables defined.
|
|
||||||
#
|
|
||||||
def parse_makefile(fp):
|
|
||||||
variables = {}
|
|
||||||
for line in fp.readlines():
|
|
||||||
if '#' in line: # Strip comments
|
|
||||||
line = string.splitfields(line, '#')[0]
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
if line[0] in string.whitespace:
|
|
||||||
continue
|
|
||||||
line = string.strip(line[:-1]) # Strip whitespace
|
|
||||||
if not line:
|
|
||||||
continue
|
|
||||||
if '=' in string.splitfields(line, ':')[0]:
|
|
||||||
#
|
|
||||||
# equal sign before first colon. Definition
|
|
||||||
#
|
|
||||||
pos = string.index(line, '=')
|
|
||||||
name = line[:pos]
|
|
||||||
value = string.strip(line[pos+1:])
|
|
||||||
variables[name] = value
|
|
||||||
return variables
|
|
||||||
|
|
||||||
#
|
makevars = parsesetup.getmakevars(makefile_in)
|
||||||
# Recursively add loader options from Setup files in extension
|
somevars = {}
|
||||||
# directories.
|
for key in makevars.keys():
|
||||||
#
|
somevars[key] = makevars[key]
|
||||||
def add_extension_directory(name, isinstalldir):
|
|
||||||
if verbose:
|
somevars['CFLAGS'] = string.join(cflags) # override
|
||||||
print 'Adding extension directory', name
|
files = ['$(OPT)', config_c, frozenmain_c] + libs + \
|
||||||
fp = open(name + '/Setup', 'r')
|
['$(MODLIBS)', '$(LIBS)', '$(SYSLIBS)']
|
||||||
modules, variables = parse_setup(fp)
|
|
||||||
#
|
outfp = open(makefile, 'w')
|
||||||
# Locate all new modules and remember the ld flags needed for them
|
try:
|
||||||
#
|
makemakefile.makemakefile(outfp, somevars, files, target)
|
||||||
for m in modules.keys():
|
finally:
|
||||||
if module_libraries.has_key(m):
|
outfp.close()
|
||||||
continue
|
|
||||||
options = modules[m]
|
# Done!
|
||||||
if isinstalldir:
|
|
||||||
ld_options = []
|
print 'Now run make to build the target:', target
|
||||||
else:
|
|
||||||
ld_options = [name + '/lib.a']
|
|
||||||
for o in options:
|
|
||||||
# ld options are all capital except DUIC and l
|
|
||||||
if o[:-2] == '.a':
|
|
||||||
ld_options.append(o)
|
|
||||||
elif o[0] == '-':
|
|
||||||
if o[1] == 'l':
|
|
||||||
ld_options.append(o)
|
|
||||||
elif o[1] in string.uppercase and not o[1] in 'DUIC':
|
|
||||||
ld_options.append(o)
|
|
||||||
module_libraries[m] = ld_options
|
|
||||||
#
|
|
||||||
# See if we have to bother with base setups
|
|
||||||
#
|
|
||||||
if variables.has_key('BASESETUP'):
|
|
||||||
if isinstalldir:
|
|
||||||
raise 'installdir has base setup'
|
|
||||||
setupfiles = string.split(variables['BASESETUP'])
|
|
||||||
for s in setupfiles:
|
|
||||||
if s[-6:] <> '/Setup':
|
|
||||||
raise 'Incorrect BASESETUP', s
|
|
||||||
s = s[:-6]
|
|
||||||
if s[0] <> '/':
|
|
||||||
s = name + '/' + s
|
|
||||||
s = os.path.normpath(s)
|
|
||||||
add_extension_directory(s, 0)
|
|
||||||
#
|
|
||||||
# Main routine for this module: given a build directory, get all
|
|
||||||
# information needed for the linker.
|
|
||||||
#
|
|
||||||
def parse(dir):
|
|
||||||
global include_path
|
|
||||||
global lib_path
|
|
||||||
global compiler
|
|
||||||
|
|
||||||
fp = open(dir + '/Makefile', 'r')
|
|
||||||
#
|
|
||||||
# First find the global libraries and the base python
|
|
||||||
#
|
|
||||||
vars = parse_makefile(fp)
|
|
||||||
if vars.has_key('CC'):
|
|
||||||
compiler = vars['CC']
|
|
||||||
if not vars.has_key('installdir'):
|
|
||||||
raise 'No $installdir in Makefile'
|
|
||||||
include_path = vars['installdir'] + '/include/Py'
|
|
||||||
lib_path = vars['installdir'] + '/lib/python/lib'
|
|
||||||
global_libraries.append('-L' + lib_path)
|
|
||||||
global_libraries.append('-lPython')
|
|
||||||
global_libraries.append('-lParser')
|
|
||||||
global_libraries.append('-lObjects')
|
|
||||||
global_libraries.append('-lModules')
|
|
||||||
for name in ('LIBS', 'LIBM', 'LIBC'):
|
|
||||||
if not vars.has_key(name):
|
|
||||||
raise 'Missing required def in Makefile', name
|
|
||||||
for lib in string.split(vars[name]):
|
|
||||||
global_libraries.append(lib)
|
|
||||||
#
|
|
||||||
# Next, parse the modules from the base python
|
|
||||||
#
|
|
||||||
add_extension_directory(lib_path, 1)
|
|
||||||
#
|
|
||||||
# Finally, parse the modules from the extension python
|
|
||||||
#
|
|
||||||
if dir <> lib_path:
|
|
||||||
add_extension_directory(dir, 0)
|
|
||||||
|
|
||||||
# Call the main program
|
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import regex
|
||||||
|
|
||||||
|
|
||||||
|
# Write the config.c file
|
||||||
|
|
||||||
|
never = ['marshal', '__main__', '__builtin__', 'sys']
|
||||||
|
|
||||||
|
def makeconfig(infp, outfp, modules):
|
||||||
|
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
|
||||||
|
outfp.write('extern void init%s();\n' % mod)
|
||||||
|
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()
|
|
@ -0,0 +1,91 @@
|
||||||
|
import marshal
|
||||||
|
|
||||||
|
|
||||||
|
# Write a file containing frozen code for the modules in the dictionary.
|
||||||
|
|
||||||
|
header = """
|
||||||
|
struct frozen {
|
||||||
|
char *name;
|
||||||
|
unsigned char *code;
|
||||||
|
int size;
|
||||||
|
} frozen_modules[] = {
|
||||||
|
"""
|
||||||
|
trailer = """\
|
||||||
|
{0, 0, 0} /* sentinel */
|
||||||
|
};
|
||||||
|
"""
|
||||||
|
|
||||||
|
def makefreeze(outfp, dict):
|
||||||
|
done = []
|
||||||
|
mods = dict.keys()
|
||||||
|
mods.sort()
|
||||||
|
for mod in mods:
|
||||||
|
modfn = dict[mod]
|
||||||
|
try:
|
||||||
|
str = makecode(modfn)
|
||||||
|
except IOError, msg:
|
||||||
|
sys.stderr.write("%s: %s\n" % (modfn, str(msg)))
|
||||||
|
continue
|
||||||
|
if str:
|
||||||
|
done.append(mod, len(str))
|
||||||
|
writecode(outfp, mod, str)
|
||||||
|
outfp.write(header)
|
||||||
|
for mod, size in done:
|
||||||
|
outfp.write('\t{"%s", M_%s, %d},\n' % (mod, mod, size))
|
||||||
|
outfp.write(trailer)
|
||||||
|
|
||||||
|
|
||||||
|
# Return code string for a given module -- either a .py or a .pyc
|
||||||
|
# file. Return either a string or None (if it's not Python code).
|
||||||
|
# May raise IOError.
|
||||||
|
|
||||||
|
def makecode(filename):
|
||||||
|
if filename[-3:] == '.py':
|
||||||
|
f = open(filename, 'r')
|
||||||
|
try:
|
||||||
|
text = f.read()
|
||||||
|
code = compile(text, filename, 'exec')
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
return marshal.dumps(code)
|
||||||
|
if filename[-4:] == '.pyc':
|
||||||
|
f = open(filename, 'rb')
|
||||||
|
try:
|
||||||
|
f.seek(8)
|
||||||
|
str = f.read()
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
return str
|
||||||
|
# Can't generate code for this extension
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# 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('static 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')
|
||||||
|
|
||||||
|
|
||||||
|
# Test for the above functions.
|
||||||
|
|
||||||
|
def test():
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
if not sys.argv[1:]:
|
||||||
|
print 'usage: python freezepython.py file.py(c) ...'
|
||||||
|
sys.exit(2)
|
||||||
|
dict = {}
|
||||||
|
for arg in sys.argv[1:]:
|
||||||
|
base = os.path.basename(arg)
|
||||||
|
mod, ext = os.path.splitext(base)
|
||||||
|
dict[mod] = arg
|
||||||
|
makefreeze(sys.stdout, dict)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
test()
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Write the actual Makefile.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import string
|
||||||
|
|
||||||
|
def makemakefile(outfp, makevars, files, target):
|
||||||
|
outfp.write("# Makefile generated by freeze.py script\n\n")
|
||||||
|
|
||||||
|
keys = makevars.keys()
|
||||||
|
keys.sort()
|
||||||
|
for key in keys:
|
||||||
|
outfp.write("%s=%s\n" % (key, makevars[key]))
|
||||||
|
outfp.write("\nall: %s\n" % target)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
outfp.write("\n%s: %s\n" % (target, string.join(deps)))
|
||||||
|
outfp.write("\t$(CC) %s -o %s\n" % (string.join(files), target))
|
|
@ -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()
|
Loading…
Reference in New Issue