SConscript revision 8942
19264Sdjordje.kovacevic@arm.com# -*- mode:python -*-
29264Sdjordje.kovacevic@arm.com
39264Sdjordje.kovacevic@arm.com# Copyright (c) 2004-2005 The Regents of The University of Michigan
49264Sdjordje.kovacevic@arm.com# All rights reserved.
59264Sdjordje.kovacevic@arm.com#
69264Sdjordje.kovacevic@arm.com# Redistribution and use in source and binary forms, with or without
79264Sdjordje.kovacevic@arm.com# modification, are permitted provided that the following conditions are
89264Sdjordje.kovacevic@arm.com# met: redistributions of source code must retain the above copyright
99264Sdjordje.kovacevic@arm.com# notice, this list of conditions and the following disclaimer;
109264Sdjordje.kovacevic@arm.com# redistributions in binary form must reproduce the above copyright
119264Sdjordje.kovacevic@arm.com# notice, this list of conditions and the following disclaimer in the
129264Sdjordje.kovacevic@arm.com# documentation and/or other materials provided with the distribution;
139264Sdjordje.kovacevic@arm.com# neither the name of the copyright holders nor the names of its
149264Sdjordje.kovacevic@arm.com# contributors may be used to endorse or promote products derived from
159264Sdjordje.kovacevic@arm.com# this software without specific prior written permission.
169264Sdjordje.kovacevic@arm.com#
179264Sdjordje.kovacevic@arm.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
189264Sdjordje.kovacevic@arm.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
199264Sdjordje.kovacevic@arm.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
209264Sdjordje.kovacevic@arm.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
219264Sdjordje.kovacevic@arm.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
229264Sdjordje.kovacevic@arm.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
239264Sdjordje.kovacevic@arm.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
249264Sdjordje.kovacevic@arm.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
259264Sdjordje.kovacevic@arm.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
269264Sdjordje.kovacevic@arm.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
279264Sdjordje.kovacevic@arm.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
289264Sdjordje.kovacevic@arm.com#
299264Sdjordje.kovacevic@arm.com# Authors: Nathan Binkert
309264Sdjordje.kovacevic@arm.com
319264Sdjordje.kovacevic@arm.comimport array
329264Sdjordje.kovacevic@arm.comimport bisect
339264Sdjordje.kovacevic@arm.comimport imp
349264Sdjordje.kovacevic@arm.comimport marshal
359264Sdjordje.kovacevic@arm.comimport os
369264Sdjordje.kovacevic@arm.comimport re
379264Sdjordje.kovacevic@arm.comimport sys
389264Sdjordje.kovacevic@arm.comimport zlib
399264Sdjordje.kovacevic@arm.com
409264Sdjordje.kovacevic@arm.comfrom os.path import basename, dirname, exists, isdir, isfile, join as joinpath
419264Sdjordje.kovacevic@arm.com
429264Sdjordje.kovacevic@arm.comimport SCons
439264Sdjordje.kovacevic@arm.com
449264Sdjordje.kovacevic@arm.com# This file defines how to build a particular configuration of gem5
459264Sdjordje.kovacevic@arm.com# based on variable settings in the 'env' build environment.
469264Sdjordje.kovacevic@arm.com
479264Sdjordje.kovacevic@arm.comImport('*')
489264Sdjordje.kovacevic@arm.com
499264Sdjordje.kovacevic@arm.com# Children need to see the environment
509264Sdjordje.kovacevic@arm.comExport('env')
519264Sdjordje.kovacevic@arm.com
529264Sdjordje.kovacevic@arm.combuild_env = [(opt, env[opt]) for opt in export_vars]
539264Sdjordje.kovacevic@arm.com
5413892Sgabeblack@google.comfrom m5.util import code_formatter, compareVersions
559264Sdjordje.kovacevic@arm.com
569264Sdjordje.kovacevic@arm.com########################################################################
579264Sdjordje.kovacevic@arm.com# Code for adding source files of various types
5813892Sgabeblack@google.com#
599264Sdjordje.kovacevic@arm.com# When specifying a source file of some type, a set of guards can be
6013892Sgabeblack@google.com# specified for that file.  When get() is used to find the files, if
619264Sdjordje.kovacevic@arm.com# get specifies a set of filters, only files that match those filters
629264Sdjordje.kovacevic@arm.com# will be accepted (unspecified filters on files are assumed to be
639264Sdjordje.kovacevic@arm.com# false).  Current filters are:
649264Sdjordje.kovacevic@arm.com#     main -- specifies the gem5 main() function
659264Sdjordje.kovacevic@arm.com#     skip_lib -- do not put this file into the gem5 library
669264Sdjordje.kovacevic@arm.com#     <unittest> -- unit tests use filters based on the unit test name
679264Sdjordje.kovacevic@arm.com#
689264Sdjordje.kovacevic@arm.com# A parent can now be specified for a source file and default filter
699264Sdjordje.kovacevic@arm.com# values will be retrieved recursively from parents (children override
709264Sdjordje.kovacevic@arm.com# parents).
719264Sdjordje.kovacevic@arm.com#
729264Sdjordje.kovacevic@arm.comclass SourceMeta(type):
739264Sdjordje.kovacevic@arm.com    '''Meta class for source files that keeps track of all files of a
749264Sdjordje.kovacevic@arm.com    particular type and has a get function for finding all functions
759264Sdjordje.kovacevic@arm.com    of a certain type that match a set of guards'''
769264Sdjordje.kovacevic@arm.com    def __init__(cls, name, bases, dict):
779264Sdjordje.kovacevic@arm.com        super(SourceMeta, cls).__init__(name, bases, dict)
789264Sdjordje.kovacevic@arm.com        cls.all = []
799264Sdjordje.kovacevic@arm.com        
8013892Sgabeblack@google.com    def get(cls, **guards):
819264Sdjordje.kovacevic@arm.com        '''Find all files that match the specified guards.  If a source
829264Sdjordje.kovacevic@arm.com        file does not specify a flag, the default is False'''
839264Sdjordje.kovacevic@arm.com        for src in cls.all:
849264Sdjordje.kovacevic@arm.com            for flag,value in guards.iteritems():
859264Sdjordje.kovacevic@arm.com                # if the flag is found and has a different value, skip
869264Sdjordje.kovacevic@arm.com                # this file
879264Sdjordje.kovacevic@arm.com                if src.all_guards.get(flag, False) != value:
889264Sdjordje.kovacevic@arm.com                    break
899264Sdjordje.kovacevic@arm.com            else:
909264Sdjordje.kovacevic@arm.com                yield src
919264Sdjordje.kovacevic@arm.com
9213892Sgabeblack@google.comclass SourceFile(object):
939264Sdjordje.kovacevic@arm.com    '''Base object that encapsulates the notion of a source file.
949264Sdjordje.kovacevic@arm.com    This includes, the source node, target node, various manipulations
959264Sdjordje.kovacevic@arm.com    of those.  A source file also specifies a set of guards which
969264Sdjordje.kovacevic@arm.com    describing which builds the source file applies to.  A parent can
979264Sdjordje.kovacevic@arm.com    also be specified to get default guards from'''
989264Sdjordje.kovacevic@arm.com    __metaclass__ = SourceMeta
999264Sdjordje.kovacevic@arm.com    def __init__(self, source, parent=None, **guards):
1009264Sdjordje.kovacevic@arm.com        self.guards = guards
1019264Sdjordje.kovacevic@arm.com        self.parent = parent
1029264Sdjordje.kovacevic@arm.com
1039264Sdjordje.kovacevic@arm.com        tnode = source
1049264Sdjordje.kovacevic@arm.com        if not isinstance(source, SCons.Node.FS.File):
1059264Sdjordje.kovacevic@arm.com            tnode = File(source)
1069264Sdjordje.kovacevic@arm.com
1079264Sdjordje.kovacevic@arm.com        self.tnode = tnode
1089264Sdjordje.kovacevic@arm.com        self.snode = tnode.srcnode()
1099264Sdjordje.kovacevic@arm.com
1109264Sdjordje.kovacevic@arm.com        for base in type(self).__mro__:
1119264Sdjordje.kovacevic@arm.com            if issubclass(base, SourceFile):
1129264Sdjordje.kovacevic@arm.com                base.all.append(self)
1139264Sdjordje.kovacevic@arm.com
1149264Sdjordje.kovacevic@arm.com    @property
1159264Sdjordje.kovacevic@arm.com    def filename(self):
1169264Sdjordje.kovacevic@arm.com        return str(self.tnode)
1179264Sdjordje.kovacevic@arm.com
1189264Sdjordje.kovacevic@arm.com    @property
1199264Sdjordje.kovacevic@arm.com    def dirname(self):
1209264Sdjordje.kovacevic@arm.com        return dirname(self.filename)
1219264Sdjordje.kovacevic@arm.com
1229264Sdjordje.kovacevic@arm.com    @property
1239264Sdjordje.kovacevic@arm.com    def basename(self):
1249264Sdjordje.kovacevic@arm.com        return basename(self.filename)
1259264Sdjordje.kovacevic@arm.com
1269264Sdjordje.kovacevic@arm.com    @property
1279264Sdjordje.kovacevic@arm.com    def extname(self):
1289264Sdjordje.kovacevic@arm.com        index = self.basename.rfind('.')
1299264Sdjordje.kovacevic@arm.com        if index <= 0:
1309264Sdjordje.kovacevic@arm.com            # dot files aren't extensions
1319264Sdjordje.kovacevic@arm.com            return self.basename, None
1329264Sdjordje.kovacevic@arm.com
1339264Sdjordje.kovacevic@arm.com        return self.basename[:index], self.basename[index+1:]
1349264Sdjordje.kovacevic@arm.com
1359264Sdjordje.kovacevic@arm.com    @property
1369264Sdjordje.kovacevic@arm.com    def all_guards(self):
1379264Sdjordje.kovacevic@arm.com        '''find all guards for this object getting default values
1389264Sdjordje.kovacevic@arm.com        recursively from its parents'''
1399264Sdjordje.kovacevic@arm.com        guards = {}
1409264Sdjordje.kovacevic@arm.com        if self.parent:
1419264Sdjordje.kovacevic@arm.com            guards.update(self.parent.guards)
1429264Sdjordje.kovacevic@arm.com        guards.update(self.guards)
1439264Sdjordje.kovacevic@arm.com        return guards
1449264Sdjordje.kovacevic@arm.com
1459264Sdjordje.kovacevic@arm.com    def __lt__(self, other): return self.filename < other.filename
1469264Sdjordje.kovacevic@arm.com    def __le__(self, other): return self.filename <= other.filename
1479264Sdjordje.kovacevic@arm.com    def __gt__(self, other): return self.filename > other.filename
1489264Sdjordje.kovacevic@arm.com    def __ge__(self, other): return self.filename >= other.filename
1499264Sdjordje.kovacevic@arm.com    def __eq__(self, other): return self.filename == other.filename
1509264Sdjordje.kovacevic@arm.com    def __ne__(self, other): return self.filename != other.filename
1519264Sdjordje.kovacevic@arm.com        
1529264Sdjordje.kovacevic@arm.comclass Source(SourceFile):
1539264Sdjordje.kovacevic@arm.com    '''Add a c/c++ source file to the build'''
1549264Sdjordje.kovacevic@arm.com    def __init__(self, source, swig = False, **guards):
1559264Sdjordje.kovacevic@arm.com        '''specify the source file, and any guards'''
1569264Sdjordje.kovacevic@arm.com        super(Source, self).__init__(source, **guards)
1579264Sdjordje.kovacevic@arm.com
1589264Sdjordje.kovacevic@arm.com        self.swig = swig
1599264Sdjordje.kovacevic@arm.com
1609264Sdjordje.kovacevic@arm.comclass PySource(SourceFile):
1619264Sdjordje.kovacevic@arm.com    '''Add a python source file to the named package'''
1629264Sdjordje.kovacevic@arm.com    invalid_sym_char = re.compile('[^A-z0-9_]')
1639264Sdjordje.kovacevic@arm.com    modules = {}
1649264Sdjordje.kovacevic@arm.com    tnodes = {}
1659264Sdjordje.kovacevic@arm.com    symnames = {}
1669264Sdjordje.kovacevic@arm.com    
1679264Sdjordje.kovacevic@arm.com    def __init__(self, package, source, **guards):
1689264Sdjordje.kovacevic@arm.com        '''specify the python package, the source file, and any guards'''
1699264Sdjordje.kovacevic@arm.com        super(PySource, self).__init__(source, **guards)
1709264Sdjordje.kovacevic@arm.com
1719264Sdjordje.kovacevic@arm.com        modname,ext = self.extname
1729264Sdjordje.kovacevic@arm.com        assert ext == 'py'
1739264Sdjordje.kovacevic@arm.com
1749264Sdjordje.kovacevic@arm.com        if package:
1759264Sdjordje.kovacevic@arm.com            path = package.split('.')
1769264Sdjordje.kovacevic@arm.com        else:
1779264Sdjordje.kovacevic@arm.com            path = []
1789264Sdjordje.kovacevic@arm.com
1799264Sdjordje.kovacevic@arm.com        modpath = path[:]
1809264Sdjordje.kovacevic@arm.com        if modname != '__init__':
1819264Sdjordje.kovacevic@arm.com            modpath += [ modname ]
1829264Sdjordje.kovacevic@arm.com        modpath = '.'.join(modpath)
1839264Sdjordje.kovacevic@arm.com
1849264Sdjordje.kovacevic@arm.com        arcpath = path + [ self.basename ]
1859264Sdjordje.kovacevic@arm.com        abspath = self.snode.abspath
1869264Sdjordje.kovacevic@arm.com        if not exists(abspath):
1879264Sdjordje.kovacevic@arm.com            abspath = self.tnode.abspath
1889264Sdjordje.kovacevic@arm.com
1899264Sdjordje.kovacevic@arm.com        self.package = package
1909264Sdjordje.kovacevic@arm.com        self.modname = modname
1919264Sdjordje.kovacevic@arm.com        self.modpath = modpath
1929264Sdjordje.kovacevic@arm.com        self.arcname = joinpath(*arcpath)
1939264Sdjordje.kovacevic@arm.com        self.abspath = abspath
1949264Sdjordje.kovacevic@arm.com        self.compiled = File(self.filename + 'c')
1959264Sdjordje.kovacevic@arm.com        self.cpp = File(self.filename + '.cc')
1969264Sdjordje.kovacevic@arm.com        self.symname = PySource.invalid_sym_char.sub('_', modpath)
1979264Sdjordje.kovacevic@arm.com
1989264Sdjordje.kovacevic@arm.com        PySource.modules[modpath] = self
1999264Sdjordje.kovacevic@arm.com        PySource.tnodes[self.tnode] = self
2009264Sdjordje.kovacevic@arm.com        PySource.symnames[self.symname] = self
2019264Sdjordje.kovacevic@arm.com
2029264Sdjordje.kovacevic@arm.comclass SimObject(PySource):
2039264Sdjordje.kovacevic@arm.com    '''Add a SimObject python file as a python source object and add
2049264Sdjordje.kovacevic@arm.com    it to a list of sim object modules'''
2059264Sdjordje.kovacevic@arm.com
2069264Sdjordje.kovacevic@arm.com    fixed = False
2079264Sdjordje.kovacevic@arm.com    modnames = []
2089264Sdjordje.kovacevic@arm.com
2099264Sdjordje.kovacevic@arm.com    def __init__(self, source, **guards):
2109264Sdjordje.kovacevic@arm.com        '''Specify the source file and any guards (automatically in
2119264Sdjordje.kovacevic@arm.com        the m5.objects package)'''
2129264Sdjordje.kovacevic@arm.com        super(SimObject, self).__init__('m5.objects', source, **guards)
2139264Sdjordje.kovacevic@arm.com        if self.fixed:
2149264Sdjordje.kovacevic@arm.com            raise AttributeError, "Too late to call SimObject now."
2159264Sdjordje.kovacevic@arm.com
2169264Sdjordje.kovacevic@arm.com        bisect.insort_right(SimObject.modnames, self.modname)
2179264Sdjordje.kovacevic@arm.com
2189264Sdjordje.kovacevic@arm.comclass SwigSource(SourceFile):
2199264Sdjordje.kovacevic@arm.com    '''Add a swig file to build'''
2209264Sdjordje.kovacevic@arm.com
2219264Sdjordje.kovacevic@arm.com    def __init__(self, package, source, **guards):
2229264Sdjordje.kovacevic@arm.com        '''Specify the python package, the source file, and any guards'''
2239264Sdjordje.kovacevic@arm.com        super(SwigSource, self).__init__(source, **guards)
2249264Sdjordje.kovacevic@arm.com
2259264Sdjordje.kovacevic@arm.com        modname,ext = self.extname
2269264Sdjordje.kovacevic@arm.com        assert ext == 'i'
2279264Sdjordje.kovacevic@arm.com
2289264Sdjordje.kovacevic@arm.com        self.module = modname
2299264Sdjordje.kovacevic@arm.com        cc_file = joinpath(self.dirname, modname + '_wrap.cc')
2309264Sdjordje.kovacevic@arm.com        py_file = joinpath(self.dirname, modname + '.py')
2319264Sdjordje.kovacevic@arm.com
2329264Sdjordje.kovacevic@arm.com        self.cc_source = Source(cc_file, swig=True, parent=self)
2339264Sdjordje.kovacevic@arm.com        self.py_source = PySource(package, py_file, parent=self)
2349264Sdjordje.kovacevic@arm.com
2359264Sdjordje.kovacevic@arm.comclass UnitTest(object):
2369264Sdjordje.kovacevic@arm.com    '''Create a UnitTest'''
2379264Sdjordje.kovacevic@arm.com
2389264Sdjordje.kovacevic@arm.com    all = []
2399264Sdjordje.kovacevic@arm.com    def __init__(self, target, *sources):
2409264Sdjordje.kovacevic@arm.com        '''Specify the target name and any sources.  Sources that are
2419264Sdjordje.kovacevic@arm.com        not SourceFiles are evalued with Source().  All files are
2429264Sdjordje.kovacevic@arm.com        guarded with a guard of the same name as the UnitTest
2439264Sdjordje.kovacevic@arm.com        target.'''
2449264Sdjordje.kovacevic@arm.com
2459264Sdjordje.kovacevic@arm.com        srcs = []
2469264Sdjordje.kovacevic@arm.com        for src in sources:
2479264Sdjordje.kovacevic@arm.com            if not isinstance(src, SourceFile):
2489264Sdjordje.kovacevic@arm.com                src = Source(src, skip_lib=True)
2499264Sdjordje.kovacevic@arm.com            src.guards[target] = True
2509264Sdjordje.kovacevic@arm.com            srcs.append(src)
2519264Sdjordje.kovacevic@arm.com
2529264Sdjordje.kovacevic@arm.com        self.sources = srcs
2539264Sdjordje.kovacevic@arm.com        self.target = target
2549264Sdjordje.kovacevic@arm.com        UnitTest.all.append(self)
2559264Sdjordje.kovacevic@arm.com
2569264Sdjordje.kovacevic@arm.com# Children should have access
2579264Sdjordje.kovacevic@arm.comExport('Source')
2589264Sdjordje.kovacevic@arm.comExport('PySource')
2599264Sdjordje.kovacevic@arm.comExport('SimObject')
2609264Sdjordje.kovacevic@arm.comExport('SwigSource')
2619264Sdjordje.kovacevic@arm.comExport('UnitTest')
2629264Sdjordje.kovacevic@arm.com
2639264Sdjordje.kovacevic@arm.com########################################################################
2649264Sdjordje.kovacevic@arm.com#
2659264Sdjordje.kovacevic@arm.com# Debug Flags
2669264Sdjordje.kovacevic@arm.com#
2679264Sdjordje.kovacevic@arm.comdebug_flags = {}
2689264Sdjordje.kovacevic@arm.comdef DebugFlag(name, desc=None):
2699264Sdjordje.kovacevic@arm.com    if name in debug_flags:
2709264Sdjordje.kovacevic@arm.com        raise AttributeError, "Flag %s already specified" % name
2719264Sdjordje.kovacevic@arm.com    debug_flags[name] = (name, (), desc)
2729264Sdjordje.kovacevic@arm.com
2739264Sdjordje.kovacevic@arm.comdef CompoundFlag(name, flags, desc=None):
2749264Sdjordje.kovacevic@arm.com    if name in debug_flags:
2759264Sdjordje.kovacevic@arm.com        raise AttributeError, "Flag %s already specified" % name
2769264Sdjordje.kovacevic@arm.com
2779264Sdjordje.kovacevic@arm.com    compound = tuple(flags)
2789264Sdjordje.kovacevic@arm.com    debug_flags[name] = (name, compound, desc)
279
280Export('DebugFlag')
281Export('CompoundFlag')
282
283########################################################################
284#
285# Set some compiler variables
286#
287
288# Include file paths are rooted in this directory.  SCons will
289# automatically expand '.' to refer to both the source directory and
290# the corresponding build directory to pick up generated include
291# files.
292env.Append(CPPPATH=Dir('.'))
293
294for extra_dir in extras_dir_list:
295    env.Append(CPPPATH=Dir(extra_dir))
296
297# Workaround for bug in SCons version > 0.97d20071212
298# Scons bug id: 2006 gem5 Bug id: 308
299for root, dirs, files in os.walk(base_dir, topdown=True):
300    Dir(root[len(base_dir) + 1:])
301
302########################################################################
303#
304# Walk the tree and execute all SConscripts in subdirectories
305#
306
307here = Dir('.').srcnode().abspath
308for root, dirs, files in os.walk(base_dir, topdown=True):
309    if root == here:
310        # we don't want to recurse back into this SConscript
311        continue
312
313    if 'SConscript' in files:
314        build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
315        SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
316
317for extra_dir in extras_dir_list:
318    prefix_len = len(dirname(extra_dir)) + 1
319    for root, dirs, files in os.walk(extra_dir, topdown=True):
320        # if build lives in the extras directory, don't walk down it
321        if 'build' in dirs:
322            dirs.remove('build')
323
324        if 'SConscript' in files:
325            build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
326            SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
327
328for opt in export_vars:
329    env.ConfigFile(opt)
330
331def makeTheISA(source, target, env):
332    isas = [ src.get_contents() for src in source ]
333    target_isa = env['TARGET_ISA']
334    def define(isa):
335        return isa.upper() + '_ISA'
336    
337    def namespace(isa):
338        return isa[0].upper() + isa[1:].lower() + 'ISA' 
339
340
341    code = code_formatter()
342    code('''\
343#ifndef __CONFIG_THE_ISA_HH__
344#define __CONFIG_THE_ISA_HH__
345
346''')
347
348    for i,isa in enumerate(isas):
349        code('#define $0 $1', define(isa), i + 1)
350
351    code('''
352
353#define THE_ISA ${{define(target_isa)}}
354#define TheISA ${{namespace(target_isa)}}
355
356#endif // __CONFIG_THE_ISA_HH__''')
357
358    code.write(str(target[0]))
359
360env.Command('config/the_isa.hh', map(Value, all_isa_list),
361            MakeAction(makeTheISA, Transform("CFG ISA", 0)))
362
363########################################################################
364#
365# Prevent any SimObjects from being added after this point, they
366# should all have been added in the SConscripts above
367#
368SimObject.fixed = True
369
370class DictImporter(object):
371    '''This importer takes a dictionary of arbitrary module names that
372    map to arbitrary filenames.'''
373    def __init__(self, modules):
374        self.modules = modules
375        self.installed = set()
376
377    def __del__(self):
378        self.unload()
379
380    def unload(self):
381        import sys
382        for module in self.installed:
383            del sys.modules[module]
384        self.installed = set()
385
386    def find_module(self, fullname, path):
387        if fullname == 'm5.defines':
388            return self
389
390        if fullname == 'm5.objects':
391            return self
392
393        if fullname.startswith('m5.internal'):
394            return None
395
396        source = self.modules.get(fullname, None)
397        if source is not None and fullname.startswith('m5.objects'):
398            return self
399
400        return None
401
402    def load_module(self, fullname):
403        mod = imp.new_module(fullname)
404        sys.modules[fullname] = mod
405        self.installed.add(fullname)
406
407        mod.__loader__ = self
408        if fullname == 'm5.objects':
409            mod.__path__ = fullname.split('.')
410            return mod
411
412        if fullname == 'm5.defines':
413            mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
414            return mod
415
416        source = self.modules[fullname]
417        if source.modname == '__init__':
418            mod.__path__ = source.modpath
419        mod.__file__ = source.abspath
420
421        exec file(source.abspath, 'r') in mod.__dict__
422
423        return mod
424
425import m5.SimObject
426import m5.params
427from m5.util import code_formatter
428
429m5.SimObject.clear()
430m5.params.clear()
431
432# install the python importer so we can grab stuff from the source
433# tree itself.  We can't have SimObjects added after this point or
434# else we won't know about them for the rest of the stuff.
435importer = DictImporter(PySource.modules)
436sys.meta_path[0:0] = [ importer ]
437
438# import all sim objects so we can populate the all_objects list
439# make sure that we're working with a list, then let's sort it
440for modname in SimObject.modnames:
441    exec('from m5.objects import %s' % modname)
442
443# we need to unload all of the currently imported modules so that they
444# will be re-imported the next time the sconscript is run
445importer.unload()
446sys.meta_path.remove(importer)
447
448sim_objects = m5.SimObject.allClasses
449all_enums = m5.params.allEnums
450
451# Find param types that need to be explicitly wrapped with swig.
452# These will be recognized because the ParamDesc will have a
453# swig_decl() method.  Most param types are based on types that don't
454# need this, either because they're based on native types (like Int)
455# or because they're SimObjects (which get swigged independently).
456# For now the only things handled here are VectorParam types.
457params_to_swig = {}
458for name,obj in sorted(sim_objects.iteritems()):
459    for param in obj._params.local.values():
460        # load the ptype attribute now because it depends on the
461        # current version of SimObject.allClasses, but when scons
462        # actually uses the value, all versions of
463        # SimObject.allClasses will have been loaded
464        param.ptype
465
466        if not hasattr(param, 'swig_decl'):
467            continue
468        pname = param.ptype_str
469        if pname not in params_to_swig:
470            params_to_swig[pname] = param
471
472########################################################################
473#
474# calculate extra dependencies
475#
476module_depends = ["m5", "m5.SimObject", "m5.params"]
477depends = [ PySource.modules[dep].snode for dep in module_depends ]
478
479########################################################################
480#
481# Commands for the basic automatically generated python files
482#
483
484# Generate Python file containing a dict specifying the current
485# buildEnv flags.
486def makeDefinesPyFile(target, source, env):
487    build_env = source[0].get_contents()
488
489    code = code_formatter()
490    code("""
491import m5.internal
492import m5.util
493
494buildEnv = m5.util.SmartDict($build_env)
495
496compileDate = m5.internal.core.compileDate
497_globals = globals()
498for key,val in m5.internal.core.__dict__.iteritems():
499    if key.startswith('flag_'):
500        flag = key[5:]
501        _globals[flag] = val
502del _globals
503""")
504    code.write(target[0].abspath)
505
506defines_info = Value(build_env)
507# Generate a file with all of the compile options in it
508env.Command('python/m5/defines.py', defines_info,
509            MakeAction(makeDefinesPyFile, Transform("DEFINES", 0)))
510PySource('m5', 'python/m5/defines.py')
511
512# Generate python file containing info about the M5 source code
513def makeInfoPyFile(target, source, env):
514    code = code_formatter()
515    for src in source:
516        data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
517        code('$src = ${{repr(data)}}')
518    code.write(str(target[0]))
519
520# Generate a file that wraps the basic top level files
521env.Command('python/m5/info.py',
522            [ '#/COPYING', '#/LICENSE', '#/README', ],
523            MakeAction(makeInfoPyFile, Transform("INFO")))
524PySource('m5', 'python/m5/info.py')
525
526########################################################################
527#
528# Create all of the SimObject param headers and enum headers
529#
530
531def createSimObjectParamStruct(target, source, env):
532    assert len(target) == 1 and len(source) == 1
533
534    name = str(source[0].get_contents())
535    obj = sim_objects[name]
536
537    code = code_formatter()
538    obj.cxx_param_decl(code)
539    code.write(target[0].abspath)
540
541def createParamSwigWrapper(target, source, env):
542    assert len(target) == 1 and len(source) == 1
543
544    name = str(source[0].get_contents())
545    param = params_to_swig[name]
546
547    code = code_formatter()
548    param.swig_decl(code)
549    code.write(target[0].abspath)
550
551def createEnumStrings(target, source, env):
552    assert len(target) == 1 and len(source) == 1
553
554    name = str(source[0].get_contents())
555    obj = all_enums[name]
556
557    code = code_formatter()
558    obj.cxx_def(code)
559    code.write(target[0].abspath)
560
561def createEnumDecls(target, source, env):
562    assert len(target) == 1 and len(source) == 1
563
564    name = str(source[0].get_contents())
565    obj = all_enums[name]
566
567    code = code_formatter()
568    obj.cxx_decl(code)
569    code.write(target[0].abspath)
570
571def createEnumSwigWrapper(target, source, env):
572    assert len(target) == 1 and len(source) == 1
573
574    name = str(source[0].get_contents())
575    obj = all_enums[name]
576
577    code = code_formatter()
578    obj.swig_decl(code)
579    code.write(target[0].abspath)
580
581def createSimObjectSwigWrapper(target, source, env):
582    name = source[0].get_contents()
583    obj = sim_objects[name]
584
585    code = code_formatter()
586    obj.swig_decl(code)
587    code.write(target[0].abspath)
588
589# Generate all of the SimObject param C++ struct header files
590params_hh_files = []
591for name,simobj in sorted(sim_objects.iteritems()):
592    py_source = PySource.modules[simobj.__module__]
593    extra_deps = [ py_source.tnode ]
594
595    hh_file = File('params/%s.hh' % name)
596    params_hh_files.append(hh_file)
597    env.Command(hh_file, Value(name),
598                MakeAction(createSimObjectParamStruct, Transform("SO PARAM")))
599    env.Depends(hh_file, depends + extra_deps)
600
601# Generate any needed param SWIG wrapper files
602params_i_files = []
603for name,param in params_to_swig.iteritems():
604    i_file = File('python/m5/internal/%s.i' % (param.swig_module_name()))
605    params_i_files.append(i_file)
606    env.Command(i_file, Value(name),
607                MakeAction(createParamSwigWrapper, Transform("SW PARAM")))
608    env.Depends(i_file, depends)
609    SwigSource('m5.internal', i_file)
610
611# Generate all enum header files
612for name,enum in sorted(all_enums.iteritems()):
613    py_source = PySource.modules[enum.__module__]
614    extra_deps = [ py_source.tnode ]
615
616    cc_file = File('enums/%s.cc' % name)
617    env.Command(cc_file, Value(name),
618                MakeAction(createEnumStrings, Transform("ENUM STR")))
619    env.Depends(cc_file, depends + extra_deps)
620    Source(cc_file)
621
622    hh_file = File('enums/%s.hh' % name)
623    env.Command(hh_file, Value(name),
624                MakeAction(createEnumDecls, Transform("ENUMDECL")))
625    env.Depends(hh_file, depends + extra_deps)
626
627    i_file = File('python/m5/internal/enum_%s.i' % name)
628    env.Command(i_file, Value(name),
629                MakeAction(createEnumSwigWrapper, Transform("ENUMSWIG")))
630    env.Depends(i_file, depends + extra_deps)
631    SwigSource('m5.internal', i_file)
632
633# Generate SimObject SWIG wrapper files
634for name in sim_objects.iterkeys():
635    i_file = File('python/m5/internal/param_%s.i' % name)
636    env.Command(i_file, Value(name),
637                MakeAction(createSimObjectSwigWrapper, Transform("SO SWIG")))
638    env.Depends(i_file, depends)
639    SwigSource('m5.internal', i_file)
640
641# Generate the main swig init file
642def makeEmbeddedSwigInit(target, source, env):
643    code = code_formatter()
644    module = source[0].get_contents()
645    code('''\
646#include "sim/init.hh"
647
648extern "C" {
649    void init_${module}();
650}
651
652EmbeddedSwig embed_swig_${module}(init_${module});
653''')
654    code.write(str(target[0]))
655    
656# Build all swig modules
657for swig in SwigSource.all:
658    env.Command([swig.cc_source.tnode, swig.py_source.tnode], swig.tnode,
659                MakeAction('$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} '
660                '-o ${TARGETS[0]} $SOURCES', Transform("SWIG")))
661    cc_file = str(swig.tnode)
662    init_file = '%s/%s_init.cc' % (dirname(cc_file), basename(cc_file))
663    env.Command(init_file, Value(swig.module),
664                MakeAction(makeEmbeddedSwigInit, Transform("EMBED SW")))
665    Source(init_file, **swig.guards)
666
667#
668# Handle debug flags
669#
670def makeDebugFlagCC(target, source, env):
671    assert(len(target) == 1 and len(source) == 1)
672
673    val = eval(source[0].get_contents())
674    name, compound, desc = val
675    compound = list(sorted(compound))
676
677    code = code_formatter()
678
679    # file header
680    code('''
681/*
682 * DO NOT EDIT THIS FILE! Automatically generated
683 */
684
685#include "base/debug.hh"
686''')
687
688    for flag in compound:
689        code('#include "debug/$flag.hh"')
690    code()
691    code('namespace Debug {')
692    code()
693
694    if not compound:
695        code('SimpleFlag $name("$name", "$desc");')
696    else:
697        code('CompoundFlag $name("$name", "$desc",')
698        code.indent()
699        last = len(compound) - 1
700        for i,flag in enumerate(compound):
701            if i != last:
702                code('$flag,')
703            else:
704                code('$flag);')
705        code.dedent()
706
707    code()
708    code('} // namespace Debug')
709
710    code.write(str(target[0]))
711
712def makeDebugFlagHH(target, source, env):
713    assert(len(target) == 1 and len(source) == 1)
714
715    val = eval(source[0].get_contents())
716    name, compound, desc = val
717
718    code = code_formatter()
719
720    # file header boilerplate
721    code('''\
722/*
723 * DO NOT EDIT THIS FILE!
724 *
725 * Automatically generated by SCons
726 */
727
728#ifndef __DEBUG_${name}_HH__
729#define __DEBUG_${name}_HH__
730
731namespace Debug {
732''')
733
734    if compound:
735        code('class CompoundFlag;')
736    code('class SimpleFlag;')
737
738    if compound:
739        code('extern CompoundFlag $name;')
740        for flag in compound:
741            code('extern SimpleFlag $flag;')
742    else:
743        code('extern SimpleFlag $name;')
744
745    code('''
746}
747
748#endif // __DEBUG_${name}_HH__
749''')
750
751    code.write(str(target[0]))
752
753for name,flag in sorted(debug_flags.iteritems()):
754    n, compound, desc = flag
755    assert n == name
756
757    env.Command('debug/%s.hh' % name, Value(flag),
758                MakeAction(makeDebugFlagHH, Transform("TRACING", 0)))
759    env.Command('debug/%s.cc' % name, Value(flag),
760                MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
761    Source('debug/%s.cc' % name)
762
763# Embed python files.  All .py files that have been indicated by a
764# PySource() call in a SConscript need to be embedded into the M5
765# library.  To do that, we compile the file to byte code, marshal the
766# byte code, compress it, and then generate a c++ file that
767# inserts the result into an array.
768def embedPyFile(target, source, env):
769    def c_str(string):
770        if string is None:
771            return "0"
772        return '"%s"' % string
773
774    '''Action function to compile a .py into a code object, marshal
775    it, compress it, and stick it into an asm file so the code appears
776    as just bytes with a label in the data section'''
777
778    src = file(str(source[0]), 'r').read()
779
780    pysource = PySource.tnodes[source[0]]
781    compiled = compile(src, pysource.abspath, 'exec')
782    marshalled = marshal.dumps(compiled)
783    compressed = zlib.compress(marshalled)
784    data = compressed
785    sym = pysource.symname
786
787    code = code_formatter()
788    code('''\
789#include "sim/init.hh"
790
791namespace {
792
793const char data_${sym}[] = {
794''')
795    code.indent()
796    step = 16
797    for i in xrange(0, len(data), step):
798        x = array.array('B', data[i:i+step])
799        code(''.join('%d,' % d for d in x))
800    code.dedent()
801    
802    code('''};
803
804EmbeddedPython embedded_${sym}(
805    ${{c_str(pysource.arcname)}},
806    ${{c_str(pysource.abspath)}},
807    ${{c_str(pysource.modpath)}},
808    data_${sym},
809    ${{len(data)}},
810    ${{len(marshalled)}});
811
812} // anonymous namespace
813''')
814    code.write(str(target[0]))
815
816for source in PySource.all:
817    env.Command(source.cpp, source.tnode, 
818                MakeAction(embedPyFile, Transform("EMBED PY")))
819    Source(source.cpp)
820
821########################################################################
822#
823# Define binaries.  Each different build type (debug, opt, etc.) gets
824# a slightly different build environment.
825#
826
827# List of constructed environments to pass back to SConstruct
828envList = []
829
830date_source = Source('base/date.cc', skip_lib=True)
831
832# Function to create a new build environment as clone of current
833# environment 'env' with modified object suffix and optional stripped
834# binary.  Additional keyword arguments are appended to corresponding
835# build environment vars.
836def makeEnv(label, objsfx, strip = False, **kwargs):
837    # SCons doesn't know to append a library suffix when there is a '.' in the
838    # name.  Use '_' instead.
839    libname = 'gem5_' + label
840    exename = 'gem5.' + label
841    secondary_exename = 'm5.' + label
842
843    new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
844    new_env.Label = label
845    new_env.Append(**kwargs)
846    # Always consider warnings errors
847    new_env.Append(CCFLAGS='-Werror')
848
849    swig_env = new_env.Clone()
850    if env['GCC']:
851        swig_env.Append(CCFLAGS='-Wno-uninitialized')
852        swig_env.Append(CCFLAGS='-Wno-sign-compare')
853        swig_env.Append(CCFLAGS='-Wno-parentheses')
854        swig_env.Append(CCFLAGS='-Wno-unused-label')
855        if compareVersions(env['GCC_VERSION'], '4.6.0') != -1:
856            swig_env.Append(CCFLAGS='-Wno-unused-but-set-variable')
857    if env['CLANG']:
858        swig_env.Append(CCFLAGS=['-Wno-unused-label'])
859
860    def make_obj(source, static, extra_deps = None):
861        '''This function adds the specified source to the correct
862        build environment, and returns the corresponding SCons Object
863        nodes'''
864
865        if source.swig:
866            env = swig_env
867        else:
868            env = new_env
869
870        if static:
871            obj = env.StaticObject(source.tnode)
872        else:
873            obj = env.SharedObject(source.tnode)
874
875        if extra_deps:
876            env.Depends(obj, extra_deps)
877
878        return obj
879
880    static_objs = \
881        [ make_obj(s, True) for s in Source.get(main=False, skip_lib=False) ]
882    shared_objs = \
883        [ make_obj(s, False) for s in Source.get(main=False, skip_lib=False) ]
884
885    static_date = make_obj(date_source, static=True, extra_deps=static_objs)
886    static_objs.append(static_date)
887    
888    shared_date = make_obj(date_source, static=False, extra_deps=shared_objs)
889    shared_objs.append(shared_date)
890
891    # First make a library of everything but main() so other programs can
892    # link against m5.
893    static_lib = new_env.StaticLibrary(libname, static_objs)
894    shared_lib = new_env.SharedLibrary(libname, shared_objs)
895
896    # Now link a stub with main() and the static library.
897    main_objs = [ make_obj(s, True) for s in Source.get(main=True) ]
898
899    for test in UnitTest.all:
900        flags = { test.target : True }
901        test_sources = Source.get(**flags)
902        test_objs = [ make_obj(s, static=True) for s in test_sources ]
903        testname = "unittest/%s.%s" % (test.target, label)
904        new_env.Program(testname, test_objs + static_objs)
905
906    progname = exename
907    if strip:
908        progname += '.unstripped'
909
910    targets = new_env.Program(progname, main_objs + static_objs)
911
912    if strip:
913        if sys.platform == 'sunos5':
914            cmd = 'cp $SOURCE $TARGET; strip $TARGET'
915        else:
916            cmd = 'strip $SOURCE -o $TARGET'
917        targets = new_env.Command(exename, progname,
918                    MakeAction(cmd, Transform("STRIP")))
919
920    new_env.Command(secondary_exename, exename,
921            MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK")))
922
923    new_env.M5Binary = targets[0]
924    envList.append(new_env)
925
926# Debug binary
927ccflags = {}
928if env['GCC'] or env['CLANG']:
929    if sys.platform == 'sunos5':
930        ccflags['debug'] = '-gstabs+'
931    else:
932        ccflags['debug'] = '-ggdb3'
933    ccflags['opt'] = '-g -O3'
934    ccflags['fast'] = '-O3'
935    ccflags['prof'] = '-O3 -g -pg'
936elif env['SUNCC']:
937    ccflags['debug'] = '-g0'
938    ccflags['opt'] = '-g -O'
939    ccflags['fast'] = '-fast'
940    ccflags['prof'] = '-fast -g -pg'
941elif env['ICC']:
942    ccflags['debug'] = '-g -O0'
943    ccflags['opt'] = '-g -O'
944    ccflags['fast'] = '-fast'
945    ccflags['prof'] = '-fast -g -pg'
946else:
947    print 'Unknown compiler, please fix compiler options'
948    Exit(1)
949
950
951# To speed things up, we only instantiate the build environments we
952# need.  We try to identify the needed environment for each target; if
953# we can't, we fall back on instantiating all the environments just to
954# be safe.
955target_types = ['debug', 'opt', 'fast', 'prof']
956obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof'}
957
958def identifyTarget(t):
959    ext = t.split('.')[-1]
960    if ext in target_types:
961        return ext
962    if obj2target.has_key(ext):
963        return obj2target[ext]
964    match = re.search(r'/tests/([^/]+)/', t)
965    if match and match.group(1) in target_types:
966        return match.group(1)
967    return 'all'
968
969needed_envs = [identifyTarget(target) for target in BUILD_TARGETS]
970if 'all' in needed_envs:
971    needed_envs += target_types
972
973# Debug binary
974if 'debug' in needed_envs:
975    makeEnv('debug', '.do',
976            CCFLAGS = Split(ccflags['debug']),
977            CPPDEFINES = ['DEBUG', 'TRACING_ON=1'])
978
979# Optimized binary
980if 'opt' in needed_envs:
981    makeEnv('opt', '.o',
982            CCFLAGS = Split(ccflags['opt']),
983            CPPDEFINES = ['TRACING_ON=1'])
984
985# "Fast" binary
986if 'fast' in needed_envs:
987    makeEnv('fast', '.fo', strip = True,
988            CCFLAGS = Split(ccflags['fast']),
989            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'])
990
991# Profiled binary
992if 'prof' in needed_envs:
993    makeEnv('prof', '.po',
994            CCFLAGS = Split(ccflags['prof']),
995            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
996            LINKFLAGS = '-pg')
997
998Return('envList')
999