SConscript revision 7170
1# -*- mode:python -*-
2
3# Copyright (c) 2004-2005 The Regents of The University of Michigan
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions are
8# met: redistributions of source code must retain the above copyright
9# notice, this list of conditions and the following disclaimer;
10# redistributions in binary form must reproduce the above copyright
11# notice, this list of conditions and the following disclaimer in the
12# documentation and/or other materials provided with the distribution;
13# neither the name of the copyright holders nor the names of its
14# contributors may be used to endorse or promote products derived from
15# this software without specific prior written permission.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28#
29# Authors: Nathan Binkert
30
31import array
32import bisect
33import imp
34import marshal
35import os
36import re
37import sys
38import zlib
39
40from os.path import basename, dirname, exists, isdir, isfile, join as joinpath
41
42import SCons
43
44# This file defines how to build a particular configuration of M5
45# based on variable settings in the 'env' build environment.
46
47Import('*')
48
49# Children need to see the environment
50Export('env')
51
52build_env = [(opt, env[opt]) for opt in export_vars]
53
54########################################################################
55# Code for adding source files of various types
56#
57class SourceMeta(type):
58    def __init__(cls, name, bases, dict):
59        super(SourceMeta, cls).__init__(name, bases, dict)
60        cls.all = []
61        
62    def get(cls, **kwargs):
63        for src in cls.all:
64            for attr,value in kwargs.iteritems():
65                if getattr(src, attr) != value:
66                    break
67            else:
68                yield src
69
70class SourceFile(object):
71    __metaclass__ = SourceMeta
72    def __init__(self, source):
73        tnode = source
74        if not isinstance(source, SCons.Node.FS.File):
75            tnode = File(source)
76
77        self.tnode = tnode
78        self.snode = tnode.srcnode()
79        self.filename = str(tnode)
80        self.dirname = dirname(self.filename)
81        self.basename = basename(self.filename)
82        index = self.basename.rfind('.')
83        if index <= 0:
84            # dot files aren't extensions
85            self.extname = self.basename, None
86        else:
87            self.extname = self.basename[:index], self.basename[index+1:]
88
89        for base in type(self).__mro__:
90            if issubclass(base, SourceFile):
91                base.all.append(self)
92
93    def __lt__(self, other): return self.filename < other.filename
94    def __le__(self, other): return self.filename <= other.filename
95    def __gt__(self, other): return self.filename > other.filename
96    def __ge__(self, other): return self.filename >= other.filename
97    def __eq__(self, other): return self.filename == other.filename
98    def __ne__(self, other): return self.filename != other.filename
99        
100class Source(SourceFile):
101    '''Add a c/c++ source file to the build'''
102    def __init__(self, source, Werror=True, swig=False, bin_only=False,
103                 skip_lib=False):
104        super(Source, self).__init__(source)
105
106        self.Werror = Werror
107        self.swig = swig
108        self.bin_only = bin_only
109        self.skip_lib = bin_only or skip_lib
110
111class PySource(SourceFile):
112    '''Add a python source file to the named package'''
113    invalid_sym_char = re.compile('[^A-z0-9_]')
114    modules = {}
115    tnodes = {}
116    symnames = {}
117    
118    def __init__(self, package, source):
119        super(PySource, self).__init__(source)
120
121        modname,ext = self.extname
122        assert ext == 'py'
123
124        if package:
125            path = package.split('.')
126        else:
127            path = []
128
129        modpath = path[:]
130        if modname != '__init__':
131            modpath += [ modname ]
132        modpath = '.'.join(modpath)
133
134        arcpath = path + [ self.basename ]
135        abspath = self.snode.abspath
136        if not exists(abspath):
137            abspath = self.tnode.abspath
138
139        self.package = package
140        self.modname = modname
141        self.modpath = modpath
142        self.arcname = joinpath(*arcpath)
143        self.abspath = abspath
144        self.compiled = File(self.filename + 'c')
145        self.assembly = File(self.filename + '.s')
146        self.symname = "PyEMB_" + PySource.invalid_sym_char.sub('_', modpath)
147
148        PySource.modules[modpath] = self
149        PySource.tnodes[self.tnode] = self
150        PySource.symnames[self.symname] = self
151
152class SimObject(PySource):
153    '''Add a SimObject python file as a python source object and add
154    it to a list of sim object modules'''
155
156    fixed = False
157    modnames = []
158
159    def __init__(self, source):
160        super(SimObject, self).__init__('m5.objects', source)
161        if self.fixed:
162            raise AttributeError, "Too late to call SimObject now."
163
164        bisect.insort_right(SimObject.modnames, self.modname)
165
166class SwigSource(SourceFile):
167    '''Add a swig file to build'''
168
169    def __init__(self, package, source):
170        super(SwigSource, self).__init__(source)
171
172        modname,ext = self.extname
173        assert ext == 'i'
174
175        self.module = modname
176        cc_file = joinpath(self.dirname, modname + '_wrap.cc')
177        py_file = joinpath(self.dirname, modname + '.py')
178
179        self.cc_source = Source(cc_file, swig=True)
180        self.py_source = PySource(package, py_file)
181
182unit_tests = []
183def UnitTest(target, sources):
184    if not isinstance(sources, (list, tuple)):
185        sources = [ sources ]
186
187    sources = [ Source(src, skip_lib=True) for src in sources ]
188    unit_tests.append((target, sources))
189
190# Children should have access
191Export('Source')
192Export('PySource')
193Export('SimObject')
194Export('SwigSource')
195Export('UnitTest')
196
197########################################################################
198#
199# Trace Flags
200#
201trace_flags = {}
202def TraceFlag(name, desc=None):
203    if name in trace_flags:
204        raise AttributeError, "Flag %s already specified" % name
205    trace_flags[name] = (name, (), desc)
206
207def CompoundFlag(name, flags, desc=None):
208    if name in trace_flags:
209        raise AttributeError, "Flag %s already specified" % name
210
211    compound = tuple(flags)
212    trace_flags[name] = (name, compound, desc)
213
214Export('TraceFlag')
215Export('CompoundFlag')
216
217########################################################################
218#
219# Set some compiler variables
220#
221
222# Include file paths are rooted in this directory.  SCons will
223# automatically expand '.' to refer to both the source directory and
224# the corresponding build directory to pick up generated include
225# files.
226env.Append(CPPPATH=Dir('.'))
227
228for extra_dir in extras_dir_list:
229    env.Append(CPPPATH=Dir(extra_dir))
230
231# Workaround for bug in SCons version > 0.97d20071212
232# Scons bug id: 2006 M5 Bug id: 308 
233for root, dirs, files in os.walk(base_dir, topdown=True):
234    Dir(root[len(base_dir) + 1:])
235
236########################################################################
237#
238# Walk the tree and execute all SConscripts in subdirectories
239#
240
241here = Dir('.').srcnode().abspath
242for root, dirs, files in os.walk(base_dir, topdown=True):
243    if root == here:
244        # we don't want to recurse back into this SConscript
245        continue
246
247    if 'SConscript' in files:
248        build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
249        SConscript(joinpath(root, 'SConscript'), build_dir=build_dir)
250
251for extra_dir in extras_dir_list:
252    prefix_len = len(dirname(extra_dir)) + 1
253    for root, dirs, files in os.walk(extra_dir, topdown=True):
254        if 'SConscript' in files:
255            build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
256            SConscript(joinpath(root, 'SConscript'), build_dir=build_dir)
257
258for opt in export_vars:
259    env.ConfigFile(opt)
260
261def makeTheISA(source, target, env):
262    f = file(str(target[0]), 'w')
263
264    isas = [ src.get_contents() for src in source ]
265    target = env['TARGET_ISA']
266    def define(isa):
267        return isa.upper() + '_ISA'
268    
269    def namespace(isa):
270        return isa[0].upper() + isa[1:].lower() + 'ISA' 
271
272
273    print >>f, '#ifndef __CONFIG_THE_ISA_HH__'
274    print >>f, '#define __CONFIG_THE_ISA_HH__'
275    print >>f
276    for i,isa in enumerate(isas):
277        print >>f, '#define %s %d' % (define(isa), i + 1)
278    print >>f
279    print >>f, '#define THE_ISA %s' % (define(target))
280    print >>f, '#define TheISA %s' % (namespace(target))
281    print >>f
282    print >>f, '#endif // __CONFIG_THE_ISA_HH__'  
283
284env.Command('config/the_isa.hh', map(Value, all_isa_list), makeTheISA)
285
286########################################################################
287#
288# Prevent any SimObjects from being added after this point, they
289# should all have been added in the SConscripts above
290#
291SimObject.fixed = True
292
293class DictImporter(object):
294    '''This importer takes a dictionary of arbitrary module names that
295    map to arbitrary filenames.'''
296    def __init__(self, modules):
297        self.modules = modules
298        self.installed = set()
299
300    def __del__(self):
301        self.unload()
302
303    def unload(self):
304        import sys
305        for module in self.installed:
306            del sys.modules[module]
307        self.installed = set()
308
309    def find_module(self, fullname, path):
310        if fullname == 'm5.defines':
311            return self
312
313        if fullname == 'm5.objects':
314            return self
315
316        if fullname.startswith('m5.internal'):
317            return None
318
319        source = self.modules.get(fullname, None)
320        if source is not None and fullname.startswith('m5.objects'):
321            return self
322
323        return None
324
325    def load_module(self, fullname):
326        mod = imp.new_module(fullname)
327        sys.modules[fullname] = mod
328        self.installed.add(fullname)
329
330        mod.__loader__ = self
331        if fullname == 'm5.objects':
332            mod.__path__ = fullname.split('.')
333            return mod
334
335        if fullname == 'm5.defines':
336            mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
337            return mod
338
339        source = self.modules[fullname]
340        if source.modname == '__init__':
341            mod.__path__ = source.modpath
342        mod.__file__ = source.abspath
343
344        exec file(source.abspath, 'r') in mod.__dict__
345
346        return mod
347
348import m5.SimObject
349import m5.params
350
351m5.SimObject.clear()
352m5.params.clear()
353
354# install the python importer so we can grab stuff from the source
355# tree itself.  We can't have SimObjects added after this point or
356# else we won't know about them for the rest of the stuff.
357importer = DictImporter(PySource.modules)
358sys.meta_path[0:0] = [ importer ]
359
360# import all sim objects so we can populate the all_objects list
361# make sure that we're working with a list, then let's sort it
362for modname in SimObject.modnames:
363    exec('from m5.objects import %s' % modname)
364
365# we need to unload all of the currently imported modules so that they
366# will be re-imported the next time the sconscript is run
367importer.unload()
368sys.meta_path.remove(importer)
369
370sim_objects = m5.SimObject.allClasses
371all_enums = m5.params.allEnums
372
373all_params = {}
374for name,obj in sorted(sim_objects.iteritems()):
375    for param in obj._params.local.values():
376        # load the ptype attribute now because it depends on the
377        # current version of SimObject.allClasses, but when scons
378        # actually uses the value, all versions of
379        # SimObject.allClasses will have been loaded
380        param.ptype
381
382        if not hasattr(param, 'swig_decl'):
383            continue
384        pname = param.ptype_str
385        if pname not in all_params:
386            all_params[pname] = param
387
388########################################################################
389#
390# calculate extra dependencies
391#
392module_depends = ["m5", "m5.SimObject", "m5.params"]
393depends = [ PySource.modules[dep].tnode for dep in module_depends ]
394
395########################################################################
396#
397# Commands for the basic automatically generated python files
398#
399
400# Generate Python file containing a dict specifying the current
401# buildEnv flags.
402def makeDefinesPyFile(target, source, env):
403    build_env, hg_info = [ x.get_contents() for x in source ]
404
405    code = m5.util.code_formatter()
406    code("""
407import m5.internal
408import m5.util
409
410buildEnv = m5.util.SmartDict($build_env)
411hgRev = '$hg_info'
412
413compileDate = m5.internal.core.compileDate
414_globals = globals()
415for key,val in m5.internal.core.__dict__.iteritems():
416    if key.startswith('flag_'):
417        flag = key[5:]
418        _globals[flag] = val
419del _globals
420""")
421    code.write(str(target[0]))
422
423defines_info = [ Value(build_env), Value(env['HG_INFO']) ]
424# Generate a file with all of the compile options in it
425env.Command('python/m5/defines.py', defines_info, makeDefinesPyFile)
426PySource('m5', 'python/m5/defines.py')
427
428# Generate python file containing info about the M5 source code
429def makeInfoPyFile(target, source, env):
430    f = file(str(target[0]), 'w')
431    for src in source:
432        data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
433        print >>f, "%s = %s" % (src, repr(data))
434    f.close()
435
436# Generate a file that wraps the basic top level files
437env.Command('python/m5/info.py',
438            [ '#/AUTHORS', '#/LICENSE', '#/README', '#/RELEASE_NOTES' ],
439            makeInfoPyFile)
440PySource('m5', 'python/m5/info.py')
441
442# Generate the __init__.py file for m5.objects
443def makeObjectsInitFile(target, source, env):
444    f = file(str(target[0]), 'w')
445    print >>f, 'from params import *'
446    print >>f, 'from m5.SimObject import *'
447    for module in source:
448        print >>f, 'from %s import *' % module.get_contents()
449    f.close()
450
451# Generate an __init__.py file for the objects package
452env.Command('python/m5/objects/__init__.py',
453            map(Value, SimObject.modnames),
454            makeObjectsInitFile)
455PySource('m5.objects', 'python/m5/objects/__init__.py')
456
457########################################################################
458#
459# Create all of the SimObject param headers and enum headers
460#
461
462def createSimObjectParam(target, source, env):
463    assert len(target) == 1 and len(source) == 1
464
465    hh_file = file(target[0].abspath, 'w')
466    name = str(source[0].get_contents())
467    obj = sim_objects[name]
468
469    print >>hh_file, obj.cxx_decl()
470    hh_file.close()
471
472def createSwigParam(target, source, env):
473    assert len(target) == 1 and len(source) == 1
474
475    i_file = file(target[0].abspath, 'w')
476    name = str(source[0].get_contents())
477    param = all_params[name]
478
479    for line in param.swig_decl():
480        print >>i_file, line
481    i_file.close()
482
483def createEnumStrings(target, source, env):
484    assert len(target) == 1 and len(source) == 1
485
486    cc_file = file(target[0].abspath, 'w')
487    name = str(source[0].get_contents())
488    obj = all_enums[name]
489
490    print >>cc_file, obj.cxx_def()
491    cc_file.close()
492
493def createEnumParam(target, source, env):
494    assert len(target) == 1 and len(source) == 1
495
496    hh_file = file(target[0].abspath, 'w')
497    name = str(source[0].get_contents())
498    obj = all_enums[name]
499
500    print >>hh_file, obj.cxx_decl()
501    hh_file.close()
502
503# Generate all of the SimObject param struct header files
504params_hh_files = []
505for name,simobj in sorted(sim_objects.iteritems()):
506    py_source = PySource.modules[simobj.__module__]
507    extra_deps = [ py_source.tnode ]
508
509    hh_file = File('params/%s.hh' % name)
510    params_hh_files.append(hh_file)
511    env.Command(hh_file, Value(name), createSimObjectParam)
512    env.Depends(hh_file, depends + extra_deps)
513
514# Generate any parameter header files needed
515params_i_files = []
516for name,param in all_params.iteritems():
517    i_file = File('params/%s_%s.i' % (name, param.file_ext))
518    params_i_files.append(i_file)
519    env.Command(i_file, Value(name), createSwigParam)
520    env.Depends(i_file, depends)
521
522# Generate all enum header files
523for name,enum in sorted(all_enums.iteritems()):
524    py_source = PySource.modules[enum.__module__]
525    extra_deps = [ py_source.tnode ]
526
527    cc_file = File('enums/%s.cc' % name)
528    env.Command(cc_file, Value(name), createEnumStrings)
529    env.Depends(cc_file, depends + extra_deps)
530    Source(cc_file)
531
532    hh_file = File('enums/%s.hh' % name)
533    env.Command(hh_file, Value(name), createEnumParam)
534    env.Depends(hh_file, depends + extra_deps)
535
536# Build the big monolithic swigged params module (wraps all SimObject
537# param structs and enum structs)
538def buildParams(target, source, env):
539    names = [ s.get_contents() for s in source ]
540    objs = [ sim_objects[name] for name in names ]
541    out = file(target[0].abspath, 'w')
542
543    ordered_objs = []
544    obj_seen = set()
545    def order_obj(obj):
546        name = str(obj)
547        if name in obj_seen:
548            return
549
550        obj_seen.add(name)
551        if str(obj) != 'SimObject':
552            order_obj(obj.__bases__[0])
553
554        ordered_objs.append(obj)
555
556    for obj in objs:
557        order_obj(obj)
558
559    enums = set()
560    predecls = []
561    pd_seen = set()
562
563    def add_pds(*pds):
564        for pd in pds:
565            if pd not in pd_seen:
566                predecls.append(pd)
567                pd_seen.add(pd)
568
569    for obj in ordered_objs:
570        params = obj._params.local.values()
571        for param in params:
572            ptype = param.ptype
573            if issubclass(ptype, m5.params.Enum):
574                if ptype not in enums:
575                    enums.add(ptype)
576            pds = param.swig_predecls()
577            if isinstance(pds, (list, tuple)):
578                add_pds(*pds)
579            else:
580                add_pds(pds)
581
582    print >>out, '%module params'
583
584    print >>out, '%{'
585    for obj in ordered_objs:
586        print >>out, '#include "params/%s.hh"' % obj
587    print >>out, '%}'
588
589    for pd in predecls:
590        print >>out, pd
591
592    enums = list(enums)
593    enums.sort()
594    for enum in enums:
595        print >>out, '%%include "enums/%s.hh"' % enum.__name__
596    print >>out
597
598    for obj in ordered_objs:
599        if obj.swig_objdecls:
600            for decl in obj.swig_objdecls:
601                print >>out, decl
602            continue
603
604        class_path = obj.cxx_class.split('::')
605        classname = class_path[-1]
606        namespaces = class_path[:-1]
607        namespaces.reverse()
608
609        code = ''
610
611        if namespaces:
612            code += '// avoid name conflicts\n'
613            sep_string = '_COLONS_'
614            flat_name = sep_string.join(class_path)
615            code += '%%rename(%s) %s;\n' % (flat_name, classname)
616
617        code += '// stop swig from creating/wrapping default ctor/dtor\n'
618        code += '%%nodefault %s;\n' % classname
619        code += 'class %s ' % classname
620        if obj._base:
621            code += ': public %s' % obj._base.cxx_class
622        code += ' {};\n'
623
624        for ns in namespaces:
625            new_code = 'namespace %s {\n' % ns
626            new_code += code
627            new_code += '}\n'
628            code = new_code
629
630        print >>out, code
631
632    print >>out, '%%include "src/sim/sim_object_params.hh"' % obj
633    for obj in ordered_objs:
634        print >>out, '%%include "params/%s.hh"' % obj
635
636params_file = File('params/params.i')
637names = sorted(sim_objects.keys())
638env.Command(params_file, map(Value, names), buildParams)
639env.Depends(params_file, params_hh_files + params_i_files + depends)
640SwigSource('m5.objects', params_file)
641
642# Build all swig modules
643for swig in SwigSource.all:
644    env.Command([swig.cc_source.tnode, swig.py_source.tnode], swig.tnode,
645                '$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} '
646                '-o ${TARGETS[0]} $SOURCES')
647    env.Depends(swig.py_source.tnode, swig.tnode)
648    env.Depends(swig.cc_source.tnode, swig.tnode)
649
650# Generate the main swig init file
651def makeSwigInit(target, source, env):
652    f = file(str(target[0]), 'w')
653    print >>f, 'extern "C" {'
654    for module in source:
655        print >>f, '    void init_%s();' % module.get_contents()
656    print >>f, '}'
657    print >>f, 'void initSwig() {'
658    for module in source:
659        print >>f, '    init_%s();' % module.get_contents()
660    print >>f, '}'
661    f.close()
662
663env.Command('python/swig/init.cc',
664            map(Value, sorted(s.module for s in SwigSource.all)),
665            makeSwigInit)
666Source('python/swig/init.cc')
667
668def getFlags(source_flags):
669    flagsMap = {}
670    flagsList = []
671    for s in source_flags:
672        val = eval(s.get_contents())
673        name, compound, desc = val
674        flagsList.append(val)
675        flagsMap[name] = bool(compound)
676    
677    for name, compound, desc in flagsList:
678        for flag in compound:
679            if flag not in flagsMap:
680                raise AttributeError, "Trace flag %s not found" % flag
681            if flagsMap[flag]:
682                raise AttributeError, \
683                    "Compound flag can't point to another compound flag"
684
685    flagsList.sort()
686    return flagsList
687
688
689# Generate traceflags.py
690def traceFlagsPy(target, source, env):
691    assert(len(target) == 1)
692
693    f = file(str(target[0]), 'w')
694   
695    allFlags = getFlags(source)
696
697    print >>f, 'basic = ['
698    for flag, compound, desc in allFlags:
699        if not compound:
700            print >>f, "    '%s'," % flag
701    print >>f, "    ]"
702    print >>f
703
704    print >>f, 'compound = ['
705    print >>f, "    'All',"
706    for flag, compound, desc in allFlags:
707        if compound:
708            print >>f, "    '%s'," % flag
709    print >>f, "    ]"
710    print >>f
711
712    print >>f, "all = frozenset(basic + compound)"
713    print >>f
714
715    print >>f, 'compoundMap = {'
716    all = tuple([flag for flag,compound,desc in allFlags if not compound])
717    print >>f, "    'All' : %s," % (all, )
718    for flag, compound, desc in allFlags:
719        if compound:
720            print >>f, "    '%s' : %s," % (flag, compound)
721    print >>f, "    }"
722    print >>f
723
724    print >>f, 'descriptions = {'
725    print >>f, "    'All' : 'All flags',"
726    for flag, compound, desc in allFlags:
727        print >>f, "    '%s' : '%s'," % (flag, desc)
728    print >>f, "    }"
729
730    f.close()
731
732def traceFlagsCC(target, source, env):
733    assert(len(target) == 1)
734
735    f = file(str(target[0]), 'w')
736
737    allFlags = getFlags(source)
738
739    # file header
740    print >>f, '''
741/*
742 * DO NOT EDIT THIS FILE! Automatically generated
743 */
744
745#include "base/traceflags.hh"
746
747using namespace Trace;
748
749const char *Trace::flagStrings[] =
750{'''
751
752    # The string array is used by SimpleEnumParam to map the strings
753    # provided by the user to enum values.
754    for flag, compound, desc in allFlags:
755        if not compound:
756            print >>f, '    "%s",' % flag
757
758    print >>f, '    "All",'
759    for flag, compound, desc in allFlags:
760        if compound:
761            print >>f, '    "%s",' % flag
762
763    print >>f, '};'
764    print >>f
765    print >>f, 'const int Trace::numFlagStrings = %d;' % (len(allFlags) + 1)
766    print >>f
767
768    #
769    # Now define the individual compound flag arrays.  There is an array
770    # for each compound flag listing the component base flags.
771    #
772    all = tuple([flag for flag,compound,desc in allFlags if not compound])
773    print >>f, 'static const Flags AllMap[] = {'
774    for flag, compound, desc in allFlags:
775        if not compound:
776            print >>f, "    %s," % flag
777    print >>f, '};'
778    print >>f
779
780    for flag, compound, desc in allFlags:
781        if not compound:
782            continue
783        print >>f, 'static const Flags %sMap[] = {' % flag
784        for flag in compound:
785            print >>f, "    %s," % flag
786        print >>f, "    (Flags)-1"
787        print >>f, '};'
788        print >>f
789
790    #
791    # Finally the compoundFlags[] array maps the compound flags
792    # to their individual arrays/
793    #
794    print >>f, 'const Flags *Trace::compoundFlags[] ='
795    print >>f, '{'
796    print >>f, '    AllMap,'
797    for flag, compound, desc in allFlags:
798        if compound:
799            print >>f, '    %sMap,' % flag
800    # file trailer
801    print >>f, '};'
802
803    f.close()
804
805def traceFlagsHH(target, source, env):
806    assert(len(target) == 1)
807
808    f = file(str(target[0]), 'w')
809
810    allFlags = getFlags(source)
811
812    # file header boilerplate
813    print >>f, '''
814/*
815 * DO NOT EDIT THIS FILE!
816 *
817 * Automatically generated from traceflags.py
818 */
819
820#ifndef __BASE_TRACE_FLAGS_HH__
821#define __BASE_TRACE_FLAGS_HH__
822
823namespace Trace {
824
825enum Flags {'''
826
827    # Generate the enum.  Base flags come first, then compound flags.
828    idx = 0
829    for flag, compound, desc in allFlags:
830        if not compound:
831            print >>f, '    %s = %d,' % (flag, idx)
832            idx += 1
833
834    numBaseFlags = idx
835    print >>f, '    NumFlags = %d,' % idx
836
837    # put a comment in here to separate base from compound flags
838    print >>f, '''
839// The remaining enum values are *not* valid indices for Trace::flags.
840// They are "compound" flags, which correspond to sets of base
841// flags, and are used by changeFlag.'''
842
843    print >>f, '    All = %d,' % idx
844    idx += 1
845    for flag, compound, desc in allFlags:
846        if compound:
847            print >>f, '    %s = %d,' % (flag, idx)
848            idx += 1
849
850    numCompoundFlags = idx - numBaseFlags
851    print >>f, '    NumCompoundFlags = %d' % numCompoundFlags
852
853    # trailer boilerplate
854    print >>f, '''\
855}; // enum Flags
856
857// Array of strings for SimpleEnumParam
858extern const char *flagStrings[];
859extern const int numFlagStrings;
860
861// Array of arraay pointers: for each compound flag, gives the list of
862// base flags to set.  Inidividual flag arrays are terminated by -1.
863extern const Flags *compoundFlags[];
864
865/* namespace Trace */ }
866
867#endif // __BASE_TRACE_FLAGS_HH__
868'''
869
870    f.close()
871
872flags = map(Value, trace_flags.values())
873env.Command('base/traceflags.py', flags, traceFlagsPy)
874PySource('m5', 'base/traceflags.py')
875
876env.Command('base/traceflags.hh', flags, traceFlagsHH)
877env.Command('base/traceflags.cc', flags, traceFlagsCC)
878Source('base/traceflags.cc')
879
880# embed python files.  All .py files that have been indicated by a
881# PySource() call in a SConscript need to be embedded into the M5
882# library.  To do that, we compile the file to byte code, marshal the
883# byte code, compress it, and then generate an assembly file that
884# inserts the result into the data section with symbols indicating the
885# beginning, and end (and with the size at the end)
886def objectifyPyFile(target, source, env):
887    '''Action function to compile a .py into a code object, marshal
888    it, compress it, and stick it into an asm file so the code appears
889    as just bytes with a label in the data section'''
890
891    src = file(str(source[0]), 'r').read()
892    dst = file(str(target[0]), 'w')
893
894    pysource = PySource.tnodes[source[0]]
895    compiled = compile(src, pysource.abspath, 'exec')
896    marshalled = marshal.dumps(compiled)
897    compressed = zlib.compress(marshalled)
898    data = compressed
899
900    # Some C/C++ compilers prepend an underscore to global symbol
901    # names, so if they're going to do that, we need to prepend that
902    # leading underscore to globals in the assembly file.
903    if env['LEADING_UNDERSCORE']:
904        sym = '_' + pysource.symname
905    else:
906        sym = pysource.symname
907
908    step = 16
909    print >>dst, ".data"
910    print >>dst, ".globl %s_beg" % sym
911    print >>dst, ".globl %s_end" % sym
912    print >>dst, "%s_beg:" % sym
913    for i in xrange(0, len(data), step):
914        x = array.array('B', data[i:i+step])
915        print >>dst, ".byte", ','.join([str(d) for d in x])
916    print >>dst, "%s_end:" % sym
917    print >>dst, ".long %d" % len(marshalled)
918
919for source in PySource.all:
920    env.Command(source.assembly, source.tnode, objectifyPyFile)
921    Source(source.assembly)
922
923# Generate init_python.cc which creates a bunch of EmbeddedPyModule
924# structs that describe the embedded python code.  One such struct
925# contains information about the importer that python uses to get at
926# the embedded files, and then there's a list of all of the rest that
927# the importer uses to load the rest on demand.
928def pythonInit(target, source, env):
929    dst = file(str(target[0]), 'w')
930
931    def dump_mod(sym, endchar=','):
932        pysource = PySource.symnames[sym]
933        print >>dst, '    { "%s",' % pysource.arcname
934        print >>dst, '      "%s",' % pysource.modpath
935        print >>dst, '       %s_beg, %s_end,' % (sym, sym)
936        print >>dst, '       %s_end - %s_beg,' % (sym, sym)
937        print >>dst, '       *(int *)%s_end }%s'  % (sym, endchar)
938    
939    print >>dst, '#include "sim/init.hh"'
940
941    for sym in source:
942        sym = sym.get_contents()
943        print >>dst, "extern const char %s_beg[], %s_end[];" % (sym, sym)
944
945    print >>dst, "const EmbeddedPyModule embeddedPyImporter = "
946    dump_mod("PyEMB_importer", endchar=';');
947    print >>dst
948
949    print >>dst, "const EmbeddedPyModule embeddedPyModules[] = {"
950    for i,sym in enumerate(source):
951        sym = sym.get_contents()
952        if sym == "PyEMB_importer":
953            # Skip the importer since we've already exported it
954            continue
955        dump_mod(sym)
956    print >>dst, "    { 0, 0, 0, 0, 0, 0 }"
957    print >>dst, "};"
958
959
960env.Command('sim/init_python.cc',
961            map(Value, (s.symname for s in PySource.all)),
962            pythonInit)
963Source('sim/init_python.cc')
964
965########################################################################
966#
967# Define binaries.  Each different build type (debug, opt, etc.) gets
968# a slightly different build environment.
969#
970
971# List of constructed environments to pass back to SConstruct
972envList = []
973
974date_source = Source('base/date.cc', skip_lib=True)
975
976# Function to create a new build environment as clone of current
977# environment 'env' with modified object suffix and optional stripped
978# binary.  Additional keyword arguments are appended to corresponding
979# build environment vars.
980def makeEnv(label, objsfx, strip = False, **kwargs):
981    # SCons doesn't know to append a library suffix when there is a '.' in the
982    # name.  Use '_' instead.
983    libname = 'm5_' + label
984    exename = 'm5.' + label
985
986    new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
987    new_env.Label = label
988    new_env.Append(**kwargs)
989
990    swig_env = new_env.Clone()
991    swig_env.Append(CCFLAGS='-Werror')
992    if env['GCC']:
993        swig_env.Append(CCFLAGS='-Wno-uninitialized')
994        swig_env.Append(CCFLAGS='-Wno-sign-compare')
995        swig_env.Append(CCFLAGS='-Wno-parentheses')
996
997    werror_env = new_env.Clone()
998    werror_env.Append(CCFLAGS='-Werror')
999
1000    def make_obj(source, static, extra_deps = None):
1001        '''This function adds the specified source to the correct
1002        build environment, and returns the corresponding SCons Object
1003        nodes'''
1004
1005        if source.swig:
1006            env = swig_env
1007        elif source.Werror:
1008            env = werror_env
1009        else:
1010            env = new_env
1011
1012        if static:
1013            obj = env.StaticObject(source.tnode)
1014        else:
1015            obj = env.SharedObject(source.tnode)
1016
1017        if extra_deps:
1018            env.Depends(obj, extra_deps)
1019
1020        return obj
1021
1022    static_objs = [ make_obj(s, True) for s in Source.get(skip_lib=False)]
1023    shared_objs = [ make_obj(s, False) for s in Source.get(skip_lib=False)]
1024
1025    static_date = make_obj(date_source, static=True, extra_deps=static_objs)
1026    static_objs.append(static_date)
1027    
1028    shared_date = make_obj(date_source, static=False, extra_deps=shared_objs)
1029    shared_objs.append(shared_date)
1030
1031    # First make a library of everything but main() so other programs can
1032    # link against m5.
1033    static_lib = new_env.StaticLibrary(libname, static_objs)
1034    shared_lib = new_env.SharedLibrary(libname, shared_objs)
1035
1036    for target, sources in unit_tests:
1037        objs = [ make_obj(s, static=True) for s in sources ]
1038        new_env.Program("unittest/%s.%s" % (target, label), objs + static_objs)
1039
1040    # Now link a stub with main() and the static library.
1041    bin_objs = [make_obj(s, True) for s in Source.get(bin_only=True) ]
1042    progname = exename
1043    if strip:
1044        progname += '.unstripped'
1045
1046    targets = new_env.Program(progname, bin_objs + static_objs)
1047
1048    if strip:
1049        if sys.platform == 'sunos5':
1050            cmd = 'cp $SOURCE $TARGET; strip $TARGET'
1051        else:
1052            cmd = 'strip $SOURCE -o $TARGET'
1053        targets = new_env.Command(exename, progname, cmd)
1054            
1055    new_env.M5Binary = targets[0]
1056    envList.append(new_env)
1057
1058# Debug binary
1059ccflags = {}
1060if env['GCC']:
1061    if sys.platform == 'sunos5':
1062        ccflags['debug'] = '-gstabs+'
1063    else:
1064        ccflags['debug'] = '-ggdb3'
1065    ccflags['opt'] = '-g -O3'
1066    ccflags['fast'] = '-O3'
1067    ccflags['prof'] = '-O3 -g -pg'
1068elif env['SUNCC']:
1069    ccflags['debug'] = '-g0'
1070    ccflags['opt'] = '-g -O'
1071    ccflags['fast'] = '-fast'
1072    ccflags['prof'] = '-fast -g -pg'
1073elif env['ICC']:
1074    ccflags['debug'] = '-g -O0'
1075    ccflags['opt'] = '-g -O'
1076    ccflags['fast'] = '-fast'
1077    ccflags['prof'] = '-fast -g -pg'
1078else:
1079    print 'Unknown compiler, please fix compiler options'
1080    Exit(1)
1081
1082makeEnv('debug', '.do',
1083        CCFLAGS = Split(ccflags['debug']),
1084        CPPDEFINES = ['DEBUG', 'TRACING_ON=1'])
1085
1086# Optimized binary
1087makeEnv('opt', '.o',
1088        CCFLAGS = Split(ccflags['opt']),
1089        CPPDEFINES = ['TRACING_ON=1'])
1090
1091# "Fast" binary
1092makeEnv('fast', '.fo', strip = True,
1093        CCFLAGS = Split(ccflags['fast']),
1094        CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'])
1095
1096# Profiled binary
1097makeEnv('prof', '.po',
1098        CCFLAGS = Split(ccflags['prof']),
1099        CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1100        LINKFLAGS = '-pg')
1101
1102Return('envList')
1103