Link in Python interpreter.

Use embedded zip archive to carry Python code instead
of homegrown embedded string/file mechanism.
Do argument parsing in Python instead of C++.

SConstruct:
    Add Python interpreter include path & library.
    Define two new simple builders which copy &
    concatenate files, respectively, for use by
    the Python embedded zipfile code.
src/SConscript:
    Encapsulate environment creation in a function.
    Add code to append Python zip archive to final executable.
    Eliminate references to obsolete files.
src/python/SConscript:
    Rewrite to generate embedded zip archive of Python code
    (replacing old "embedded string" mechanism).
src/python/m5/__init__.py:
    Move main arg-parsing loop here (out of C++ main()).
src/python/m5/config.py:
    Minor fix (version incompatibility?).
src/sim/main.cc:
    Invoke embedded Python interpreter to parse args
    and generate config.ini, replacing C++ arg parsing code.

--HG--
extra : convert_revision : 72d21236b2bee139ff39ba4cf031a4a1f8560029
This commit is contained in:
Steve Reinhardt
2006-05-30 13:11:34 -04:00
parent d308055afc
commit 0337db3388
6 changed files with 254 additions and 369 deletions

View File

@@ -27,126 +27,55 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import os, os.path, re, sys
from zipfile import PyZipFile
# handy function for path joins
def join(*args):
return os.path.normpath(os.path.join(*args))
Import('env')
import scons_helper
# This SConscript is in charge of collecting .py files and generating a zip archive that is appended to the m5 binary.
def WriteEmbeddedPyFile(target, source, path, name, ext, filename):
if isinstance(source, str):
source = file(source, 'r')
# Copy .py source files here (relative to src/python in the build
# directory).
pyzip_root = 'zip'
if isinstance(target, str):
target = file(target, 'w')
# List of files & directories to include in the zip file. To include
# a package, list only the root directory of the package, not any
# internal .py files (else they will get the path stripped off when
# they are imported into the zip file).
pyzip_files = []
print >>target, "AddModule(%s, %s, %s, %s, '''\\" % \
(`path`, `name`, `ext`, `filename`)
# List of additional files on which the zip archive depends, but which
# are not included in pyzip_files... i.e. individual .py files within
# a package.
pyzip_dep_files = []
for line in source:
line = line
# escape existing backslashes
line = line.replace('\\', '\\\\')
# escape existing triple quotes
line = line.replace("'''", r"\'\'\'")
# Add the specified package to the zip archive. Adds the directory to
# pyzip_files and all included .py files to pyzip_dep_files.
def addPkg(pkgdir):
pyzip_files.append(join(pyzip_root, pkgdir))
origdir = os.getcwd()
srcdir = join(Dir('.').srcnode().abspath, pkgdir)
os.chdir(srcdir)
for path, dirs, files in os.walk('.'):
for i,dir in enumerate(dirs):
if dir == 'SCCS':
del dirs[i]
break
print >>target, line,
for f in files:
if f.endswith('.py'):
source = join(pkgdir, path, f)
target = join(pyzip_root, source)
pyzip_dep_files.append(target)
env.CopyFile(target, source)
print >>target, "''')"
print >>target
def WriteCFile(target, source, name):
if isinstance(source, str):
source = file(source, 'r')
if isinstance(target, str):
target = file(target, 'w')
print >>target, 'const char %s_string[] = {' % name
count = 0
from array import array
try:
while True:
foo = array('B')
foo.fromfile(source, 10000)
l = [ str(i) for i in foo.tolist() ]
count += len(l)
for i in xrange(0,9999,20):
print >>target, ','.join(l[i:i+20]) + ','
except EOFError:
l = [ str(i) for i in foo.tolist() ]
count += len(l)
for i in xrange(0,len(l),20):
print >>target, ','.join(l[i:i+20]) + ','
print >>target, ','.join(l[i:]) + ','
print >>target, '};'
print >>target, 'const int %s_length = %d;' % (name, count)
print >>target
def splitpath(path):
dir,file = os.path.split(path)
path = []
assert(file)
while dir:
dir,base = os.path.split(dir)
path.insert(0, base)
return path, file
def MakeEmbeddedPyFile(target, source, env):
target = file(str(target[0]), 'w')
tree = {}
for src in source:
src = str(src)
path,pyfile = splitpath(src)
node = tree
for dir in path:
if not node.has_key(dir):
node[dir] = { }
node = node[dir]
name,ext = pyfile.split('.')
if name == '__init__':
node['.hasinit'] = True
node[pyfile] = (src,name,ext,src)
done = False
while not done:
done = True
for name,entry in tree.items():
if not isinstance(entry, dict): continue
if entry.has_key('.hasinit'): continue
done = False
del tree[name]
for key,val in entry.iteritems():
if tree.has_key(key):
raise NameError, \
"dir already has %s can't add it again" % key
tree[key] = val
files = []
def populate(node, path = []):
names = node.keys()
names.sort()
for name in names:
if name == '.hasinit':
continue
entry = node[name]
if isinstance(entry, dict):
if not entry.has_key('.hasinit'):
raise NameError, 'package directory missing __init__.py'
populate(entry, path + [ name ])
else:
pyfile,name,ext,filename = entry
files.append((pyfile, path, name, ext, filename))
populate(tree)
for pyfile, path, name, ext, filename in files:
WriteEmbeddedPyFile(target, pyfile, path, name, ext, filename)
os.chdir(origdir)
# Generate Python file that contains a dict specifying the current
# build_env flags.
def MakeDefinesPyFile(target, source, env):
f = file(str(target[0]), 'w')
print >>f, "import __main__"
@@ -154,54 +83,21 @@ def MakeDefinesPyFile(target, source, env):
print >>f, source[0]
f.close()
CFileCounter = 0
def MakePythonCFile(target, source, env):
global CFileCounter
target = file(str(target[0]), 'w')
print >>target, '''\
#include "base/embedfile.hh"
namespace {
'''
for src in source:
src = str(src)
fname = os.path.basename(src)
name = 'embedded_file%d' % CFileCounter
CFileCounter += 1
WriteCFile(target, src, name)
print >>target, '''\
EmbedMap %(name)s("%(fname)s",
%(name)s_string, %(name)s_length);
''' % locals()
print >>target, '''\
/* namespace */ }
'''
# base list of .py files to embed
embedded_py_files = [ os.path.join(env['ROOT'], 'util/pbs/jobfile.py') ]
# add all .py files in python/m5
objpath = os.path.join(env['SRCDIR'], 'python', 'm5')
for root, dirs, files in os.walk(objpath, topdown=True):
for i,dir in enumerate(dirs):
if dir == 'SCCS':
del dirs[i]
break
assert(root.startswith(objpath))
for f in files:
if f.endswith('.py'):
embedded_py_files.append(os.path.join(root, f))
embedfile_hh = os.path.join(env['SRCDIR'], 'base/embedfile.hh')
optionDict = dict([(opt, env[opt]) for opt in env.ExportOptions])
env.Command('defines.py', Value(optionDict), MakeDefinesPyFile)
env.Command('embedded_py.py', embedded_py_files, MakeEmbeddedPyFile)
env.Depends('embedded_py.cc', embedfile_hh)
env.Command('embedded_py.cc',
['string_importer.py', 'defines.py', 'embedded_py.py'],
MakePythonCFile)
# Now specify the packages & files for the zip archive.
addPkg('m5')
pyzip_files.append('defines.py')
pyzip_files.append(join(env['ROOT'], 'util/pbs/jobfile.py'))
# Action function to build the zip archive. Uses the PyZipFile module
# included in the standard Python library.
def buildPyZip(target, source, env):
pzf = PyZipFile(str(target[0]), 'w')
for s in source:
pzf.writepy(str(s))
# Add the zip file target to the environment.
env.Command('m5py.zip', pyzip_files, buildPyZip)
env.Depends('m5py.zip', pyzip_dep_files)

View File

@@ -24,7 +24,53 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import sys, os
import sys, os, time
import __main__
briefCopyright = '''
Copyright (c) 2001-2006
The Regents of The University of Michigan
All Rights Reserved
'''
fullCopyright = '''
Copyright (c) 2001-2006
The Regents of The University of Michigan
All Rights Reserved
Permission is granted to use, copy, create derivative works and
redistribute this software and such derivative works for any purpose,
so long as the copyright notice above, this grant of permission, and
the disclaimer below appear in all copies made; and so long as the
name of The University of Michigan is not used in any advertising or
publicity pertaining to the use or distribution of this software
without specific, written prior authorization.
THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION FROM THE
UNIVERSITY OF MICHIGAN AS TO ITS FITNESS FOR ANY PURPOSE, AND WITHOUT
WARRANTY BY THE UNIVERSITY OF MICHIGAN OF ANY KIND, EITHER EXPRESS OR
IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE REGENTS OF
THE UNIVERSITY OF MICHIGAN SHALL NOT BE LIABLE FOR ANY DAMAGES,
INCLUDING DIRECT, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT OF OR IN CONNECTION
WITH THE USE OF THE SOFTWARE, EVEN IF IT HAS BEEN OR IS HEREAFTER
ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
'''
def sayHello(f):
print >> f, "M5 Simulator System"
print >> f, briefCopyright
print >> f, "M5 compiled on", __main__.compileDate
hostname = os.environ.get('HOSTNAME')
if not hostname:
hostname = os.environ.get('HOST')
if hostname:
print >> f, "M5 executing on", hostname
print >> f, "M5 simulation started", time.ctime()
sayHello(sys.stderr)
# define this here so we can use it right away if necessary
def panic(string):
@@ -72,3 +118,63 @@ from config import *
# import the built-in object definitions
from objects import *
args_left = sys.argv[1:]
configfile_found = False
while args_left:
arg = args_left.pop(0)
if arg.startswith('--'):
# if arg starts with '--', parse as a special python option
# of the format --<python var>=<string value>
try:
(var, val) = arg.split('=', 1)
except ValueError:
panic("Could not parse configuration argument '%s'\n"
"Expecting --<variable>=<value>\n" % arg);
eval("%s = %s" % (var, repr(val)))
elif arg.startswith('-'):
# if the arg starts with '-', it should be a simulator option
# with a format similar to getopt.
optchar = arg[1]
if len(arg) > 2:
args_left.insert(0, arg[2:])
if optchar == 'd':
outdir = args_left.pop(0)
elif optchar == 'h':
showBriefHelp(sys.stderr)
sys.exit(1)
elif optchar == 'E':
env_str = args_left.pop(0)
split_result = env_str.split('=', 1)
var = split_result[0]
if len(split_result == 2):
val = split_result[1]
else:
val = True
env[var] = val
elif optchar == 'I':
AddToPath(args_left.pop(0))
elif optchar == 'P':
eval(args_left.pop(0))
else:
showBriefHelp(sys.stderr)
panic("invalid argument '%s'\n" % arg_str)
else:
# In any other case, treat the option as a configuration file
# name and load it.
if not arg.endswith('.py'):
panic("Config file '%s' must end in '.py'\n" % arg)
configfile_found = True
m5execfile(arg, globals())
if not configfile_found:
panic("no configuration file specified!")
if globals().has_key('root') and isinstance(root, Root):
sys.stdout = file('config.ini', 'w')
instantiate(root)
else:
print 'Instantiation skipped: no root object found.'

View File

@@ -794,7 +794,7 @@ class ParamFactory(object):
# E.g., Param.Int(5, "number of widgets")
def __call__(self, *args, **kwargs):
caller_frame = inspect.stack()[1][0]
caller_frame = inspect.currentframe().f_back
ptype = None
try:
ptype = eval(self.ptype_str,