mips: cleanup ISA-specific code
[gem5.git] / SConstruct
old mode 100644 (file)
new mode 100755 (executable)
index 0b417ea..eee1c78
@@ -1,5 +1,7 @@
 # -*- mode:python -*-
 
 # -*- mode:python -*-
 
+# Copyright (c) 2011 Advanced Micro Devices, Inc.
+# Copyright (c) 2009 The Hewlett-Packard Development Company
 # Copyright (c) 2004-2005 The Regents of The University of Michigan
 # All rights reserved.
 #
 # Copyright (c) 2004-2005 The Regents of The University of Michigan
 # All rights reserved.
 #
@@ -95,6 +97,7 @@ For more details, see:
 """
     raise
 
 """
     raise
 
+# Global Python includes
 import os
 import re
 import subprocess
 import os
 import re
 import subprocess
@@ -105,72 +108,97 @@ from os.path import abspath, basename, dirname, expanduser, normpath
 from os.path import exists,  isdir, isfile
 from os.path import join as joinpath, split as splitpath
 
 from os.path import exists,  isdir, isfile
 from os.path import join as joinpath, split as splitpath
 
+# SCons includes
 import SCons
 import SCons
+import SCons.Node
 
 
-def read_command(cmd):
-    """run the command cmd, read the results and return them
-    this is sorta like `cmd` in shell"""
-    from subprocess import Popen, PIPE, STDOUT
-    subp = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, close_fds=True)
-    return subp.communicate()[0]
-
-# helper function: compare arrays or strings of version numbers.
-# E.g., compare_version((1,3,25), (1,4,1)')
-# returns -1, 0, 1 if v1 is <, ==, > v2
-def compare_versions(v1, v2):
-    def make_version_list(v):
-        if isinstance(v, (list,tuple)):
-            return v
-        elif isinstance(v, str):
-            return map(lambda x: int(re.match('\d+', x).group()), v.split('.'))
+extra_python_paths = [
+    Dir('src/python').srcnode().abspath, # M5 includes
+    Dir('ext/ply').srcnode().abspath, # ply is used by several files
+    ]
+    
+sys.path[1:1] = extra_python_paths
+
+from m5.util import compareVersions, readCommand
+
+help_texts = {
+    "options" : "",
+    "global_vars" : "",
+    "local_vars" : ""
+}
+
+Export("help_texts")
+
+def AddM5Option(*args, **kwargs):
+    col_width = 30
+
+    help = "  " + ", ".join(args)
+    if "help" in kwargs:
+        length = len(help)
+        if length >= col_width:
+            help += "\n" + " " * col_width
         else:
         else:
-            raise TypeError
+            help += " " * (col_width - length)
+        help += kwargs["help"]
+    help_texts["options"] += help + "\n"
+
+    AddOption(*args, **kwargs)
+
+AddM5Option('--colors', dest='use_colors', action='store_true',
+            help="Add color to abbreviated scons output")
+AddM5Option('--no-colors', dest='use_colors', action='store_false',
+            help="Don't add color to abbreviated scons output")
+AddM5Option('--default', dest='default', type='string', action='store',
+            help='Override which build_opts file to use for defaults')
+AddM5Option('--ignore-style', dest='ignore_style', action='store_true',
+            help='Disable style checking hooks')
+AddM5Option('--update-ref', dest='update_ref', action='store_true',
+            help='Update test reference outputs')
+AddM5Option('--verbose', dest='verbose', action='store_true',
+            help='Print full tool command lines')
+
+use_colors = GetOption('use_colors')
+if use_colors:
+    from m5.util.terminal import termcap
+elif use_colors is None:
+    # option unspecified; default behavior is to use colors iff isatty
+    from m5.util.terminal import tty_termcap as termcap
+else:
+    from m5.util.terminal import no_termcap as termcap
 
 
-    v1 = make_version_list(v1)
-    v2 = make_version_list(v2)
-    # Compare corresponding elements of lists
-    for n1,n2 in zip(v1, v2):
-        if n1 < n2: return -1
-        if n1 > n2: return  1
-    # all corresponding values are equal... see if one has extra values
-    if len(v1) < len(v2): return -1
-    if len(v1) > len(v2): return  1
-    return 0
+########################################################################
+#
+# Set up the main build environment.
+#
+########################################################################
+use_vars = set([ 'AS', 'AR', 'CC', 'CXX', 'HOME', 'LD_LIBRARY_PATH', 'PATH',
+                 'PYTHONPATH', 'RANLIB' ])
 
 
-# The absolute path to the current directory (where this file lives).
-ROOT = Dir('.').abspath
+use_env = {}
+for key,val in os.environ.iteritems():
+    if key in use_vars or key.startswith("M5"):
+        use_env[key] = val
 
 
-# Path to the M5 source tree.
-SRCDIR = joinpath(ROOT, 'src')
+main = Environment(ENV=use_env)
+main.root = Dir(".")         # The current directory (where this file lives).
+main.srcdir = Dir("src")     # The source directory
 
 
-# tell python where to find m5 python code
-sys.path.append(joinpath(ROOT, 'src/python'))
+# add useful python code PYTHONPATH so it can be used by subprocesses
+# as well
+main.AppendENVPath('PYTHONPATH', extra_python_paths)
 
 
-###################################################
+########################################################################
+#
 # Mercurial Stuff.
 # Mercurial Stuff.
-# 1) Grab repository revision if we know it.
-# 2) Ensure that the style hook is in place.
-###################################################
+#
+# If the M5 directory is a mercurial repository, we should do some
+# extra things.
+#
+########################################################################
 
 
-hg_info = "Unknown"
-try:
-    if not exists(ROOT) or not isdir(ROOT) or \
-           not exists(joinpath(ROOT, ".hg")):
-        raise ValueError(".hg directory not found")
-    hg_info = read_command("cd %s; hg id -n -i -t -b" % ROOT).strip()
-except ImportError, e:
-    print "Mercurial not found"
-except ValueError, e:
-    print e
-except Exception, e:
-    print "Other mercurial exception: %s" % e
-
-def check_style_hook(ui):
-    ui.readconfig(joinpath(ROOT, '.hg', 'hgrc'))
-    style_hook = ui.config('hooks', 'pretxncommit.style', None)
-
-    if not style_hook:
-        print """\
+hgdir = main.root.Dir(".hg")
+
+mercurial_style_message = """
 You're missing the M5 style hook.
 Please install the hook so we can ensure that all code fits a common style.
 
 You're missing the M5 style hook.
 Please install the hook so we can ensure that all code fits a common style.
 
@@ -182,17 +210,42 @@ or your personal .hgrc
 style = %s/util/style.py
 
 [hooks]
 style = %s/util/style.py
 
 [hooks]
-pretxncommit.style = python:style.check_whitespace
-""" % (ROOT)
-        sys.exit(1)
+pretxncommit.style = python:style.check_style
+pre-qrefresh.style = python:style.check_style
+""" % (main.root)
+
+mercurial_bin_not_found = """
+Mercurial binary cannot be found, unfortunately this means that we
+cannot easily determine the version of M5 that you are running and
+this makes error messages more difficult to collect.  Please consider
+installing mercurial if you choose to post an error message
+"""
+
+mercurial_lib_not_found = """
+Mercurial libraries cannot be found, ignoring style hook
+If you are actually a M5 developer, please fix this and
+run the style hook. It is important.
+"""
 
 
-if ARGUMENTS.get('IGNORE_STYLE') != 'True' and isdir(joinpath(ROOT, '.hg')):
+if hgdir.exists():
+    # Ensure that the style hook is in place.
     try:
     try:
-        from mercurial import ui
-        check_style_hook(ui.ui())
+        ui = None
+        if not GetOption('ignore_style'):
+            from mercurial import ui
+            ui = ui.ui()
     except ImportError:
     except ImportError:
-        pass
+        print mercurial_lib_not_found
 
 
+    if ui is not None:
+        ui.readconfig(hgdir.File('hgrc').abspath)
+        style_hook = ui.config('hooks', 'pretxncommit.style', None)
+
+        if not style_hook:
+            print mercurial_style_message
+            sys.exit(1)
+else:
+    print ".hg directory not found"
 
 ###################################################
 #
 
 ###################################################
 #
@@ -226,7 +279,7 @@ if COMMAND_LINE_TARGETS:
                     COMMAND_LINE_TARGETS]
 else:
     # Default targets are relative to root of tree
                     COMMAND_LINE_TARGETS]
 else:
     # Default targets are relative to root of tree
-    abs_targets = [ normpath(joinpath(ROOT, str(x))) for x in \
+    abs_targets = [ normpath(joinpath(main.root.abspath, str(x))) for x in \
                     DEFAULT_TARGETS]
 
 
                     DEFAULT_TARGETS]
 
 
@@ -256,28 +309,17 @@ for t in abs_targets:
 # Make sure build_root exists (might not if this is the first build there)
 if not isdir(build_root):
     mkdir(build_root)
 # Make sure build_root exists (might not if this is the first build there)
 if not isdir(build_root):
     mkdir(build_root)
+main['BUILDROOT'] = build_root
 
 
-###################################################
-#
-# Set up the default build environment.  This environment is copied
-# and modified according to each selected configuration.
-#
-###################################################
-
-env = Environment(ENV = environ,  # inherit user's environment vars
-                  ROOT = ROOT,
-                  SRCDIR = SRCDIR,
-                  HG_INFO = hg_info)
+Export('main')
 
 
-Export('env')
-
-env.SConsignFile(joinpath(build_root, "sconsign"))
+main.SConsignFile(joinpath(build_root, "sconsign"))
 
 # Default duplicate option is to use hard links, but this messes up
 # when you use emacs to edit a file in the target dir, as emacs moves
 # file to file~ then copies to file, breaking the link.  Symbolic
 # (soft) links work better.
 
 # Default duplicate option is to use hard links, but this messes up
 # when you use emacs to edit a file in the target dir, as emacs moves
 # file to file~ then copies to file, breaking the link.  Symbolic
 # (soft) links work better.
-env.SetOption('duplicate', 'soft-copy')
+main.SetOption('duplicate', 'soft-copy')
 
 #
 # Set up global sticky variables... these are common to an entire build
 
 #
 # Set up global sticky variables... these are common to an entire build
@@ -299,96 +341,203 @@ def PathListAllExist(key, val, env):
         if not isdir(path):
             raise SCons.Errors.UserError("Path does not exist: '%s'" % path)
 
         if not isdir(path):
             raise SCons.Errors.UserError("Path does not exist: '%s'" % path)
 
-global_sticky_vars_file = joinpath(build_root, 'variables.global')
+global_vars_file = joinpath(build_root, 'variables.global')
 
 
-global_sticky_vars = Variables(global_sticky_vars_file, args=ARGUMENTS)
+global_vars = Variables(global_vars_file, args=ARGUMENTS)
 
 
-global_sticky_vars.AddVariables(
-    ('CC', 'C compiler', environ.get('CC', env['CC'])),
-    ('CXX', 'C++ compiler', environ.get('CXX', env['CXX'])),
+global_vars.AddVariables(
+    ('CC', 'C compiler', environ.get('CC', main['CC'])),
+    ('CXX', 'C++ compiler', environ.get('CXX', main['CXX'])),
     ('BATCH', 'Use batch pool for build and tests', False),
     ('BATCH_CMD', 'Batch pool submission command name', 'qdo'),
     ('BATCH', 'Use batch pool for build and tests', False),
     ('BATCH_CMD', 'Batch pool submission command name', 'qdo'),
-    ('EXTRAS', 'Add Extra directories to the compilation', '',
-     PathListAllExist, PathListMakeAbsolute)
-    )    
-
-# base help text
-help_text = '''
-Usage: scons [scons options] [build options] [target(s)]
-
-Global sticky options:
-'''
-
-help_text += global_sticky_vars.GenerateHelpText(env)
+    ('M5_BUILD_CACHE', 'Cache built objects in this directory', False),
+    ('EXTRAS', 'Add extra directories to the compilation', '',
+     PathListAllExist, PathListMakeAbsolute),
+    )
 
 
-# Update env with values from ARGUMENTS & file global_sticky_vars_file
-global_sticky_vars.Update(env)
+# Update main environment with values from ARGUMENTS & global_vars_file
+global_vars.Update(main)
+help_texts["global_vars"] += global_vars.GenerateHelpText(main)
 
 # Save sticky variable settings back to current variables file
 
 # Save sticky variable settings back to current variables file
-global_sticky_vars.Save(global_sticky_vars_file, env)
+global_vars.Save(global_vars_file, main)
 
 # Parse EXTRAS variable to build list of all directories where we're
 # look for sources etc.  This list is exported as base_dir_list.
 
 # Parse EXTRAS variable to build list of all directories where we're
 # look for sources etc.  This list is exported as base_dir_list.
-base_dir = joinpath(ROOT, 'src')
-if env['EXTRAS']:
-    extras_dir_list = env['EXTRAS'].split(':')
+base_dir = main.srcdir.abspath
+if main['EXTRAS']:
+    extras_dir_list = main['EXTRAS'].split(':')
 else:
     extras_dir_list = []
 
 Export('base_dir')
 Export('extras_dir_list')
 
 else:
     extras_dir_list = []
 
 Export('base_dir')
 Export('extras_dir_list')
 
-# M5_PLY is used by isa_parser.py to find the PLY package.
-env.Append(ENV = { 'M5_PLY' : str(Dir('ext/ply')) })
-env['GCC'] = read_command(env['CXX'] + ' --version').find('g++') >= 0
-env['SUNCC'] = read_command(env['CXX'] + ' -V').find('Sun C++') >= 0
-env['ICC'] = read_command(env['CXX'] + ' -V').find('Intel') >= 0
-if env['GCC'] + env['SUNCC'] + env['ICC'] > 1:
+# the ext directory should be on the #includes path
+main.Append(CPPPATH=[Dir('ext')])
+
+def strip_build_path(path, env):
+    path = str(path)
+    variant_base = env['BUILDROOT'] + os.path.sep
+    if path.startswith(variant_base):
+        path = path[len(variant_base):]
+    elif path.startswith('build/'):
+        path = path[6:]
+    return path
+
+# Generate a string of the form:
+#   common/path/prefix/src1, src2 -> tgt1, tgt2
+# to print while building.
+class Transform(object):
+    # all specific color settings should be here and nowhere else
+    tool_color = termcap.Normal
+    pfx_color = termcap.Yellow
+    srcs_color = termcap.Yellow + termcap.Bold
+    arrow_color = termcap.Blue + termcap.Bold
+    tgts_color = termcap.Yellow + termcap.Bold
+
+    def __init__(self, tool, max_sources=99):
+        self.format = self.tool_color + (" [%8s] " % tool) \
+                      + self.pfx_color + "%s" \
+                      + self.srcs_color + "%s" \
+                      + self.arrow_color + " -> " \
+                      + self.tgts_color + "%s" \
+                      + termcap.Normal
+        self.max_sources = max_sources
+
+    def __call__(self, target, source, env, for_signature=None):
+        # truncate source list according to max_sources param
+        source = source[0:self.max_sources]
+        def strip(f):
+            return strip_build_path(str(f), env)
+        if len(source) > 0:
+            srcs = map(strip, source)
+        else:
+            srcs = ['']
+        tgts = map(strip, target)
+        # surprisingly, os.path.commonprefix is a dumb char-by-char string
+        # operation that has nothing to do with paths.
+        com_pfx = os.path.commonprefix(srcs + tgts)
+        com_pfx_len = len(com_pfx)
+        if com_pfx:
+            # do some cleanup and sanity checking on common prefix
+            if com_pfx[-1] == ".":
+                # prefix matches all but file extension: ok
+                # back up one to change 'foo.cc -> o' to 'foo.cc -> .o'
+                com_pfx = com_pfx[0:-1]
+            elif com_pfx[-1] == "/":
+                # common prefix is directory path: OK
+                pass
+            else:
+                src0_len = len(srcs[0])
+                tgt0_len = len(tgts[0])
+                if src0_len == com_pfx_len:
+                    # source is a substring of target, OK
+                    pass
+                elif tgt0_len == com_pfx_len:
+                    # target is a substring of source, need to back up to
+                    # avoid empty string on RHS of arrow
+                    sep_idx = com_pfx.rfind(".")
+                    if sep_idx != -1:
+                        com_pfx = com_pfx[0:sep_idx]
+                    else:
+                        com_pfx = ''
+                elif src0_len > com_pfx_len and srcs[0][com_pfx_len] == ".":
+                    # still splitting at file extension: ok
+                    pass
+                else:
+                    # probably a fluke; ignore it
+                    com_pfx = ''
+        # recalculate length in case com_pfx was modified
+        com_pfx_len = len(com_pfx)
+        def fmt(files):
+            f = map(lambda s: s[com_pfx_len:], files)
+            return ', '.join(f)
+        return self.format % (com_pfx, fmt(srcs), fmt(tgts))
+
+Export('Transform')
+
+
+if GetOption('verbose'):
+    def MakeAction(action, string, *args, **kwargs):
+        return Action(action, *args, **kwargs)
+else:
+    MakeAction = Action
+    main['CCCOMSTR']        = Transform("CC")
+    main['CXXCOMSTR']       = Transform("CXX")
+    main['ASCOMSTR']        = Transform("AS")
+    main['SWIGCOMSTR']      = Transform("SWIG")
+    main['ARCOMSTR']        = Transform("AR", 0)
+    main['LINKCOMSTR']      = Transform("LINK", 0)
+    main['RANLIBCOMSTR']    = Transform("RANLIB", 0)
+    main['M4COMSTR']        = Transform("M4")
+    main['SHCCCOMSTR']      = Transform("SHCC")
+    main['SHCXXCOMSTR']     = Transform("SHCXX")
+Export('MakeAction')
+
+CXX_version = readCommand([main['CXX'],'--version'], exception=False)
+CXX_V = readCommand([main['CXX'],'-V'], exception=False)
+
+main['GCC'] = CXX_version and CXX_version.find('g++') >= 0
+main['SUNCC'] = CXX_V and CXX_V.find('Sun C++') >= 0
+main['ICC'] = CXX_V and CXX_V.find('Intel') >= 0
+if main['GCC'] + main['SUNCC'] + main['ICC'] > 1:
     print 'Error: How can we have two at the same time?'
     Exit(1)
 
 # Set up default C++ compiler flags
     print 'Error: How can we have two at the same time?'
     Exit(1)
 
 # Set up default C++ compiler flags
-if env['GCC']:
-    env.Append(CCFLAGS='-pipe')
-    env.Append(CCFLAGS='-fno-strict-aliasing')
-    env.Append(CCFLAGS=Split('-Wall -Wno-sign-compare -Werror -Wundef'))
-    env.Append(CXXFLAGS='-Wno-deprecated')
-elif env['ICC']:
+if main['GCC']:
+    main.Append(CCFLAGS=['-pipe'])
+    main.Append(CCFLAGS=['-fno-strict-aliasing'])
+    main.Append(CCFLAGS=['-Wall', '-Wno-sign-compare', '-Wundef'])
+    main.Append(CXXFLAGS=['-Wno-deprecated'])
+    # Read the GCC version to check for versions with bugs
+    # Note CCVERSION doesn't work here because it is run with the CC
+    # before we override it from the command line
+    gcc_version = readCommand([main['CXX'], '-dumpversion'], exception=False)
+    if not compareVersions(gcc_version, '4.4.1') or \
+       not compareVersions(gcc_version, '4.4.2'):
+        print 'Info: Tree vectorizer in GCC 4.4.1 & 4.4.2 is buggy, disabling.'
+        main.Append(CCFLAGS=['-fno-tree-vectorize'])
+elif main['ICC']:
     pass #Fix me... add warning flags once we clean up icc warnings
     pass #Fix me... add warning flags once we clean up icc warnings
-elif env['SUNCC']:
-    env.Append(CCFLAGS='-Qoption ccfe')
-    env.Append(CCFLAGS='-features=gcc')
-    env.Append(CCFLAGS='-features=extensions')
-    env.Append(CCFLAGS='-library=stlport4')
-    env.Append(CCFLAGS='-xar')
-#    env.Append(CCFLAGS='-instances=semiexplicit')
+elif main['SUNCC']:
+    main.Append(CCFLAGS=['-Qoption ccfe'])
+    main.Append(CCFLAGS=['-features=gcc'])
+    main.Append(CCFLAGS=['-features=extensions'])
+    main.Append(CCFLAGS=['-library=stlport4'])
+    main.Append(CCFLAGS=['-xar'])
+    #main.Append(CCFLAGS=['-instances=semiexplicit'])
 else:
     print 'Error: Don\'t know what compiler options to use for your compiler.'
     print '       Please fix SConstruct and src/SConscript and try again.'
     Exit(1)
 
 else:
     print 'Error: Don\'t know what compiler options to use for your compiler.'
     print '       Please fix SConstruct and src/SConscript and try again.'
     Exit(1)
 
+# Set up common yacc/bison flags (needed for Ruby)
+main['YACCFLAGS'] = '-d'
+main['YACCHXXFILESUFFIX'] = '.hh'
+
 # Do this after we save setting back, or else we'll tack on an
 # extra 'qdo' every time we run scons.
 # Do this after we save setting back, or else we'll tack on an
 # extra 'qdo' every time we run scons.
-if env['BATCH']:
-    env['CC']     = env['BATCH_CMD'] + ' ' + env['CC']
-    env['CXX']    = env['BATCH_CMD'] + ' ' + env['CXX']
-    env['AS']     = env['BATCH_CMD'] + ' ' + env['AS']
-    env['AR']     = env['BATCH_CMD'] + ' ' + env['AR']
-    env['RANLIB'] = env['BATCH_CMD'] + ' ' + env['RANLIB']
+if main['BATCH']:
+    main['CC']     = main['BATCH_CMD'] + ' ' + main['CC']
+    main['CXX']    = main['BATCH_CMD'] + ' ' + main['CXX']
+    main['AS']     = main['BATCH_CMD'] + ' ' + main['AS']
+    main['AR']     = main['BATCH_CMD'] + ' ' + main['AR']
+    main['RANLIB'] = main['BATCH_CMD'] + ' ' + main['RANLIB']
 
 if sys.platform == 'cygwin':
     # cygwin has some header file issues...
 
 if sys.platform == 'cygwin':
     # cygwin has some header file issues...
-    env.Append(CCFLAGS=Split("-Wno-uninitialized"))
-env.Append(CPPPATH=[Dir('ext/dnet')])
+    main.Append(CCFLAGS=["-Wno-uninitialized"])
 
 # Check for SWIG
 
 # Check for SWIG
-if not env.has_key('SWIG'):
+if not main.has_key('SWIG'):
     print 'Error: SWIG utility not found.'
     print '       Please install (see http://www.swig.org) and retry.'
     Exit(1)
 
 # Check for appropriate SWIG version
     print 'Error: SWIG utility not found.'
     print '       Please install (see http://www.swig.org) and retry.'
     Exit(1)
 
 # Check for appropriate SWIG version
-swig_version = read_command('swig -version').split()
+swig_version = readCommand(('swig', '-version'), exception='').split()
 # First 3 words should be "SWIG Version x.y.z"
 if len(swig_version) < 3 or \
         swig_version[0] != 'SWIG' or swig_version[1] != 'Version':
 # First 3 words should be "SWIG Version x.y.z"
 if len(swig_version) < 3 or \
         swig_version[0] != 'SWIG' or swig_version[1] != 'Version':
@@ -396,19 +545,19 @@ if len(swig_version) < 3 or \
     Exit(1)
 
 min_swig_version = '1.3.28'
     Exit(1)
 
 min_swig_version = '1.3.28'
-if compare_versions(swig_version[2], min_swig_version) < 0:
+if compareVersions(swig_version[2], min_swig_version) < 0:
     print 'Error: SWIG version', min_swig_version, 'or newer required.'
     print '       Installed version:', swig_version[2]
     Exit(1)
 
 # Set up SWIG flags & scanner
 swig_flags=Split('-c++ -python -modern -templatereduce $_CPPINCFLAGS')
     print 'Error: SWIG version', min_swig_version, 'or newer required.'
     print '       Installed version:', swig_version[2]
     Exit(1)
 
 # Set up SWIG flags & scanner
 swig_flags=Split('-c++ -python -modern -templatereduce $_CPPINCFLAGS')
-env.Append(SWIGFLAGS=swig_flags)
+main.Append(SWIGFLAGS=swig_flags)
 
 # filter out all existing swig scanners, they mess up the dependency
 # stuff for some reason
 scanners = []
 
 # filter out all existing swig scanners, they mess up the dependency
 # stuff for some reason
 scanners = []
-for scanner in env['SCANNERS']:
+for scanner in main['SCANNERS']:
     skeys = scanner.skeys
     if skeys == '.i':
         continue
     skeys = scanner.skeys
     if skeys == '.i':
         continue
@@ -424,7 +573,7 @@ swig_inc_re = '^[ \t]*[%,#][ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")'
 scanners.append(CPPScanner("SwigScan", [ ".i" ], "CPPPATH", swig_inc_re))
 
 # replace the scanners list that has what we want
 scanners.append(CPPScanner("SwigScan", [ ".i" ], "CPPPATH", swig_inc_re))
 
 # replace the scanners list that has what we want
-env['SCANNERS'] = scanners
+main['SCANNERS'] = scanners
 
 # Add a custom Check function to the Configure context so that we can
 # figure out if the compiler adds leading underscores to global
 
 # Add a custom Check function to the Configure context so that we can
 # figure out if the compiler adds leading underscores to global
@@ -454,7 +603,7 @@ def CheckLeading(context):
 
 # Platform-specific configuration.  Note again that we assume that all
 # builds under a given build root run on the same host platform.
 
 # Platform-specific configuration.  Note again that we assume that all
 # builds under a given build root run on the same host platform.
-conf = Configure(env,
+conf = Configure(main,
                  conf_dir = joinpath(build_root, '.scons_config'),
                  log_file = joinpath(build_root, 'scons_config.log'),
                  custom_tests = { 'CheckLeading' : CheckLeading })
                  conf_dir = joinpath(build_root, '.scons_config'),
                  log_file = joinpath(build_root, 'scons_config.log'),
                  custom_tests = { 'CheckLeading' : CheckLeading })
@@ -467,12 +616,12 @@ conf.CheckLeading()
 try:
     import platform
     uname = platform.uname()
 try:
     import platform
     uname = platform.uname()
-    if uname[0] == 'Darwin' and compare_versions(uname[2], '9.0.0') >= 0:
-        if int(read_command('sysctl -n hw.cpu64bit_capable')[0]):
-            env.Append(CCFLAGS='-arch x86_64')
-            env.Append(CFLAGS='-arch x86_64')
-            env.Append(LINKFLAGS='-arch x86_64')
-            env.Append(ASFLAGS='-arch x86_64')
+    if uname[0] == 'Darwin' and compareVersions(uname[2], '9.0.0') >= 0:
+        if int(readCommand('sysctl -n hw.cpu64bit_capable')[0]):
+            main.Append(CCFLAGS=['-arch', 'x86_64'])
+            main.Append(CFLAGS=['-arch', 'x86_64'])
+            main.Append(LINKFLAGS=['-arch', 'x86_64'])
+            main.Append(ASFLAGS=['-arch', 'x86_64'])
 except:
     pass
 
 except:
     pass
 
@@ -493,7 +642,7 @@ if not conf:
         def __getattr__(self, mname):
             return NullCheck
 
         def __getattr__(self, mname):
             return NullCheck
 
-    conf = NullConf(env)
+    conf = NullConf(main)
 
 # Find Python include and library directories for embedding the
 # interpreter.  For consistency, we will use the same Python
 
 # Find Python include and library directories for embedding the
 # interpreter.  For consistency, we will use the same Python
@@ -504,7 +653,8 @@ from distutils import sysconfig
 
 py_getvar = sysconfig.get_config_var
 
 
 py_getvar = sysconfig.get_config_var
 
-py_version = 'python' + py_getvar('VERSION')
+py_debug = getattr(sys, 'pydebug', False)
+py_version = 'python' + py_getvar('VERSION') + (py_debug and "_d" or "")
 
 py_general_include = sysconfig.get_python_inc()
 py_platform_include = sysconfig.get_python_inc(plat_specific=True)
 
 py_general_include = sysconfig.get_python_inc()
 py_platform_include = sysconfig.get_python_inc(plat_specific=True)
@@ -512,21 +662,28 @@ py_includes = [ py_general_include ]
 if py_platform_include != py_general_include:
     py_includes.append(py_platform_include)
 
 if py_platform_include != py_general_include:
     py_includes.append(py_platform_include)
 
-py_lib_path = []
+py_lib_path = [ py_getvar('LIBDIR') ]
 # add the prefix/lib/pythonX.Y/config dir, but only if there is no
 # shared library in prefix/lib/.
 if not py_getvar('Py_ENABLE_SHARED'):
 # add the prefix/lib/pythonX.Y/config dir, but only if there is no
 # shared library in prefix/lib/.
 if not py_getvar('Py_ENABLE_SHARED'):
-    py_lib_path.append('-L' + py_getvar('LIBPL'))
+    py_lib_path.append(py_getvar('LIBPL'))
 
 py_libs = []
 for lib in py_getvar('LIBS').split() + py_getvar('SYSLIBS').split():
 
 py_libs = []
 for lib in py_getvar('LIBS').split() + py_getvar('SYSLIBS').split():
+    assert lib.startswith('-l')
+    lib = lib[2:]   
     if lib not in py_libs:
         py_libs.append(lib)
     if lib not in py_libs:
         py_libs.append(lib)
-py_libs.append('-l' + py_version)
+py_libs.append(py_version)
+
+main.Append(CPPPATH=py_includes)
+main.Append(LIBPATH=py_lib_path)
+
+# Cache build files in the supplied directory.
+if main['M5_BUILD_CACHE']:
+    print 'Using build cache located at', main['M5_BUILD_CACHE']
+    CacheDir(main['M5_BUILD_CACHE'])
 
 
-env.Append(CPPPATH=py_includes)
-env.Append(LIBPATH=py_lib_path)
-#env.Append(LIBS=py_libs)
 
 # verify that this stuff works
 if not conf.CheckHeader('Python.h', '<>'):
 
 # verify that this stuff works
 if not conf.CheckHeader('Python.h', '<>'):
@@ -534,8 +691,6 @@ if not conf.CheckHeader('Python.h', '<>'):
     Exit(1)
 
 for lib in py_libs:
     Exit(1)
 
 for lib in py_libs:
-    assert lib.startswith('-l')
-    lib = lib[2:]
     if not conf.CheckLib(lib):
         print "Error: can't find library %s required by python" % lib
         Exit(1)
     if not conf.CheckLib(lib):
         print "Error: can't find library %s required by python" % lib
         Exit(1)
@@ -554,6 +709,16 @@ if not conf.CheckLibWithHeader('z', 'zlib.h', 'C++','zlibVersion();'):
     print '       Please install zlib and try again.'
     Exit(1)
 
     print '       Please install zlib and try again.'
     Exit(1)
 
+# Check for librt.
+have_posix_clock = \
+    conf.CheckLibWithHeader(None, 'time.h', 'C',
+                            'clock_nanosleep(0,0,NULL,NULL);') or \
+    conf.CheckLibWithHeader('rt', 'time.h', 'C',
+                            'clock_nanosleep(0,0,NULL,NULL);')
+
+if not have_posix_clock:
+    print "Can't find library for POSIX clocks."
+
 # Check for <fenv.h> (C99 FP environment control)
 have_fenv = conf.CheckHeader('fenv.h', '<>')
 if not have_fenv:
 # Check for <fenv.h> (C99 FP environment control)
 have_fenv = conf.CheckHeader('fenv.h', '<>')
 if not have_fenv:
@@ -569,9 +734,9 @@ have_mysql = bool(mysql_config)
 
 # Check MySQL version.
 if have_mysql:
 
 # Check MySQL version.
 if have_mysql:
-    mysql_version = read_command(mysql_config + ' --version')
+    mysql_version = readCommand(mysql_config + ' --version')
     min_mysql_version = '4.1'
     min_mysql_version = '4.1'
-    if compare_versions(mysql_version, min_mysql_version) < 0:
+    if compareVersions(mysql_version, min_mysql_version) < 0:
         print 'Warning: MySQL', min_mysql_version, 'or newer required.'
         print '         Version', mysql_version, 'detected.'
         have_mysql = False
         print 'Warning: MySQL', min_mysql_version, 'or newer required.'
         print '         Version', mysql_version, 'detected.'
         have_mysql = False
@@ -590,7 +755,7 @@ if have_mysql:
 #
 # Finish the configuration
 #
 #
 # Finish the configuration
 #
-env = conf.Finish()
+main = conf.Finish()
 
 ######################################################################
 #
 
 ######################################################################
 #
@@ -601,10 +766,34 @@ env = conf.Finish()
 all_isa_list = [ ]
 Export('all_isa_list')
 
 all_isa_list = [ ]
 Export('all_isa_list')
 
-# Define the universe of supported CPU models
-all_cpu_list = [ ]
-default_cpus = [ ]
-Export('all_cpu_list', 'default_cpus')
+class CpuModel(object):
+    '''The CpuModel class encapsulates everything the ISA parser needs to
+    know about a particular CPU model.'''
+
+    # Dict of available CPU model objects.  Accessible as CpuModel.dict.
+    dict = {}
+    list = []
+    defaults = []
+
+    # Constructor.  Automatically adds models to CpuModel.dict.
+    def __init__(self, name, filename, includes, strings, default=False):
+        self.name = name           # name of model
+        self.filename = filename   # filename for output exec code
+        self.includes = includes   # include files needed in exec file
+        # The 'strings' dict holds all the per-CPU symbols we can
+        # substitute into templates etc.
+        self.strings = strings
+
+        # This cpu is enabled by default
+        self.default = default
+
+        # Add self to dict
+        if name in CpuModel.dict:
+            raise AttributeError, "CpuModel '%s' already registered" % name
+        CpuModel.dict[name] = self
+        CpuModel.list.append(name)
+
+Export('CpuModel')
 
 # Sticky variables get saved in the variables file so they persist from
 # one invocation to the next (unless overridden, in which case the new
 
 # Sticky variables get saved in the variables file so they persist from
 # one invocation to the next (unless overridden, in which case the new
@@ -612,9 +801,9 @@ Export('all_cpu_list', 'default_cpus')
 sticky_vars = Variables(args=ARGUMENTS)
 Export('sticky_vars')
 
 sticky_vars = Variables(args=ARGUMENTS)
 Export('sticky_vars')
 
-# Non-sticky variables only apply to the current build.
-nonsticky_vars = Variables(args=ARGUMENTS)
-Export('nonsticky_vars')
+# Sticky variables that should be exported
+export_vars = []
+Export('export_vars')
 
 # Walk the tree and execute all SConsopts scripts that wil add to the
 # above variables
 
 # Walk the tree and execute all SConsopts scripts that wil add to the
 # above variables
@@ -625,16 +814,16 @@ for bdir in [ base_dir ] + extras_dir_list:
             SConscript(joinpath(root, 'SConsopts'))
 
 all_isa_list.sort()
             SConscript(joinpath(root, 'SConsopts'))
 
 all_isa_list.sort()
-all_cpu_list.sort()
-default_cpus.sort()
 
 sticky_vars.AddVariables(
     EnumVariable('TARGET_ISA', 'Target ISA', 'alpha', all_isa_list),
     BoolVariable('FULL_SYSTEM', 'Full-system support', False),
 
 sticky_vars.AddVariables(
     EnumVariable('TARGET_ISA', 'Target ISA', 'alpha', all_isa_list),
     BoolVariable('FULL_SYSTEM', 'Full-system support', False),
-    ListVariable('CPU_MODELS', 'CPU models', default_cpus, all_cpu_list),
+    ListVariable('CPU_MODELS', 'CPU models',
+                 sorted(n for n,m in CpuModel.dict.iteritems() if m.default),
+                 sorted(CpuModel.list)),
     BoolVariable('NO_FAST_ALLOC', 'Disable fast object allocator', False),
     BoolVariable('NO_FAST_ALLOC', 'Disable fast object allocator', False),
-    BoolVariable('FAST_ALLOC_DEBUG', 'Enable fast object allocator debugging',
-                 False),
+    BoolVariable('FORCE_FAST_ALLOC',
+                 'Enable fast object allocator, even for m5.debug', False),
     BoolVariable('FAST_ALLOC_STATS', 'Enable fast object allocator statistics',
                  False),
     BoolVariable('EFENCE', 'Link with Electric Fence malloc debugger',
     BoolVariable('FAST_ALLOC_STATS', 'Enable fast object allocator statistics',
                  False),
     BoolVariable('EFENCE', 'Link with Electric Fence malloc debugger',
@@ -646,19 +835,18 @@ sticky_vars.AddVariables(
                  'Compile for SSE2 (-msse2) to get IEEE FP on x86 hosts',
                  False),
     BoolVariable('USE_MYSQL', 'Use MySQL for stats output', have_mysql),
                  'Compile for SSE2 (-msse2) to get IEEE FP on x86 hosts',
                  False),
     BoolVariable('USE_MYSQL', 'Use MySQL for stats output', have_mysql),
+    BoolVariable('USE_POSIX_CLOCK', 'Use POSIX Clocks', have_posix_clock),
     BoolVariable('USE_FENV', 'Use <fenv.h> IEEE mode control', have_fenv),
     BoolVariable('USE_CHECKER', 'Use checker for detailed CPU models', False),
     BoolVariable('USE_FENV', 'Use <fenv.h> IEEE mode control', have_fenv),
     BoolVariable('USE_CHECKER', 'Use checker for detailed CPU models', False),
-    )
-
-nonsticky_vars.AddVariables(
-    BoolVariable('update_ref', 'Update test reference outputs', False)
+    BoolVariable('CP_ANNOTATE', 'Enable critical path annotation capability', False),
+    BoolVariable('RUBY', 'Build with Ruby', False),
     )
 
 # These variables get exported to #defines in config/*.hh (see src/SConscript).
     )
 
 # These variables get exported to #defines in config/*.hh (see src/SConscript).
-env.ExportVariables = ['FULL_SYSTEM', 'ALPHA_TLASER', 'USE_FENV', \
-                       'USE_MYSQL', 'NO_FAST_ALLOC', 'FAST_ALLOC_DEBUG', \
-                       'FAST_ALLOC_STATS', 'SS_COMPATIBLE_FP', \
-                       'USE_CHECKER', 'TARGET_ISA']
+export_vars += ['FULL_SYSTEM', 'USE_FENV', 'USE_MYSQL',
+                'NO_FAST_ALLOC', 'FORCE_FAST_ALLOC', 'FAST_ALLOC_STATS',
+                'SS_COMPATIBLE_FP', 'USE_CHECKER', 'TARGET_ISA', 'CP_ANNOTATE',
+                'USE_POSIX_CLOCK' ]
 
 ###################################################
 #
 
 ###################################################
 #
@@ -704,17 +892,15 @@ def config_emitter(target, source, env):
 
 config_builder = Builder(emitter = config_emitter, action = config_action)
 
 
 config_builder = Builder(emitter = config_emitter, action = config_action)
 
-env.Append(BUILDERS = { 'ConfigFile' : config_builder })
+main.Append(BUILDERS = { 'ConfigFile' : config_builder })
 
 # libelf build is shared across all configs in the build root.
 
 # libelf build is shared across all configs in the build root.
-env.SConscript('ext/libelf/SConscript',
-               variant_dir = joinpath(build_root, 'libelf'),
-               exports = 'env')
+main.SConscript('ext/libelf/SConscript',
+                variant_dir = joinpath(build_root, 'libelf'))
 
 # gzstream build is shared across all configs in the build root.
 
 # gzstream build is shared across all configs in the build root.
-env.SConscript('ext/gzstream/SConscript',
-               variant_dir = joinpath(build_root, 'gzstream'),
-               exports = 'env')
+main.SConscript('ext/gzstream/SConscript',
+                variant_dir = joinpath(build_root, 'gzstream'))
 
 ###################################################
 #
 
 ###################################################
 #
@@ -722,34 +908,23 @@ env.SConscript('ext/gzstream/SConscript',
 #
 ###################################################
 
 #
 ###################################################
 
-env['ALL_ISA_LIST'] = all_isa_list
+main['ALL_ISA_LIST'] = all_isa_list
 def make_switching_dir(dname, switch_headers, env):
     # Generate the header.  target[0] is the full path of the output
     # header to generate.  'source' is a dummy variable, since we get the
     # list of ISAs from env['ALL_ISA_LIST'].
     def gen_switch_hdr(target, source, env):
         fname = str(target[0])
 def make_switching_dir(dname, switch_headers, env):
     # Generate the header.  target[0] is the full path of the output
     # header to generate.  'source' is a dummy variable, since we get the
     # list of ISAs from env['ALL_ISA_LIST'].
     def gen_switch_hdr(target, source, env):
         fname = str(target[0])
-        bname = basename(fname)
         f = open(fname, 'w')
         f = open(fname, 'w')
-        f.write('#include "arch/isa_specific.hh"\n')
-        cond = '#if'
-        for isa in all_isa_list:
-            f.write('%s THE_ISA == %s_ISA\n#include "%s/%s/%s"\n'
-                    % (cond, isa.upper(), dname, isa, bname))
-            cond = '#elif'
-        f.write('#else\n#error "THE_ISA not set"\n#endif\n')
+        isa = env['TARGET_ISA'].lower()
+        print >>f, '#include "%s/%s/%s"' % (dname, isa, basename(fname))
         f.close()
         f.close()
-        return 0
-
-    # String to print when generating header
-    def gen_switch_hdr_string(target, source, env):
-        return "Generating switch header " + str(target[0])
 
     # Build SCons Action object. 'varlist' specifies env vars that this
     # action depends on; when env['ALL_ISA_LIST'] changes these actions
     # should get re-executed.
 
     # Build SCons Action object. 'varlist' specifies env vars that this
     # action depends on; when env['ALL_ISA_LIST'] changes these actions
     # should get re-executed.
-    switch_hdr_action = Action(gen_switch_hdr, gen_switch_hdr_string,
-                               varlist=['ALL_ISA_LIST'])
+    switch_hdr_action = MakeAction(gen_switch_hdr,
+                          Transform("GENERATE"), varlist=['ALL_ISA_LIST'])
 
     # Instantiate actions for each header
     for hdr in switch_headers:
 
     # Instantiate actions for each header
     for hdr in switch_headers:
@@ -762,14 +937,11 @@ Export('make_switching_dir')
 #
 ###################################################
 
 #
 ###################################################
 
-# rename base env
-base_env = env
-
 for variant_path in variant_paths:
     print "Building in", variant_path
 
     # Make a copy of the build-root environment to use for this config.
 for variant_path in variant_paths:
     print "Building in", variant_path
 
     # Make a copy of the build-root environment to use for this config.
-    env = base_env.Clone()
+    env = main.Clone()
     env['BUILDDIR'] = variant_path
 
     # variant_dir is the tail component of build path, and is used to
     env['BUILDDIR'] = variant_path
 
     # variant_dir is the tail component of build path, and is used to
@@ -796,8 +968,10 @@ for variant_path in variant_paths:
         # Get default build variables from source tree.  Variables are
         # normally determined by name of $VARIANT_DIR, but can be
         # overriden by 'default=' arg on command line.
         # Get default build variables from source tree.  Variables are
         # normally determined by name of $VARIANT_DIR, but can be
         # overriden by 'default=' arg on command line.
-        default_vars_file = joinpath('build_opts',
-                                     ARGUMENTS.get('default', variant_dir))
+        default = GetOption('default')
+        if not default:
+            default = variant_dir
+        default_vars_file = joinpath('build_opts', default)
         if isfile(default_vars_file):
             sticky_vars.files.append(default_vars_file)
             print "Variables file %s not found,\n  using defaults in %s" \
         if isfile(default_vars_file):
             sticky_vars.files.append(default_vars_file)
             print "Variables file %s not found,\n  using defaults in %s" \
@@ -809,12 +983,10 @@ for variant_path in variant_paths:
 
     # Apply current variable settings to env
     sticky_vars.Update(env)
 
     # Apply current variable settings to env
     sticky_vars.Update(env)
-    nonsticky_vars.Update(env)
 
 
-    help_text += "\nSticky variables for %s:\n" % variant_dir \
-                 + sticky_vars.GenerateHelpText(env) \
-                 + "\nNon-sticky variables for %s:\n" % variant_dir \
-                 + nonsticky_vars.GenerateHelpText(env)
+    help_texts["local_vars"] += \
+        "Build variables for %s:\n" % variant_dir \
+                 + sticky_vars.GenerateHelpText(env)
 
     # Process variable settings.
 
 
     # Process variable settings.
 
@@ -844,7 +1016,7 @@ for variant_path in variant_paths:
     sticky_vars.Save(current_vars_file, env)
 
     if env['USE_SSE2']:
     sticky_vars.Save(current_vars_file, env)
 
     if env['USE_SSE2']:
-        env.Append(CCFLAGS='-msse2')
+        env.Append(CCFLAGS=['-msse2'])
 
     # The src/SConscript file sets up the build rules in 'env' according
     # to the configured variables.  It returns a list of environments,
 
     # The src/SConscript file sets up the build rules in 'env' according
     # to the configured variables.  It returns a list of environments,
@@ -858,13 +1030,15 @@ for variant_path in variant_paths:
                    variant_dir = joinpath(variant_path, 'tests', e.Label),
                    exports = { 'env' : e }, duplicate = False)
 
                    variant_dir = joinpath(variant_path, 'tests', e.Label),
                    exports = { 'env' : e }, duplicate = False)
 
-Help(help_text)
+# base help text
+Help('''
+Usage: scons [scons options] [build variables] [target(s)]
 
 
+Extra scons options:
+%(options)s
 
 
-###################################################
-#
-# Let SCons do its thing.  At this point SCons will use the defined
-# build environments to build the requested targets.
-#
-###################################################
+Global build variables:
+%(global_vars)s
 
 
+%(local_vars)s
+''' % help_texts)