SConscript revision 6656
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                bisect.insort_right(base.all, 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        debugname = self.snode.abspath
136        if not exists(debugname):
137            debugname = self.tnode.abspath
138
139        self.package = package
140        self.modname = modname
141        self.modpath = modpath
142        self.arcname = joinpath(*arcpath)
143        self.debugname = debugname
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# Add a flag defining what THE_ISA should be for all compilation
232env.Append(CPPDEFINES=[('THE_ISA','%s_ISA' % env['TARGET_ISA'].upper())])
233
234# Workaround for bug in SCons version > 0.97d20071212
235# Scons bug id: 2006 M5 Bug id: 308 
236for root, dirs, files in os.walk(base_dir, topdown=True):
237    Dir(root[len(base_dir) + 1:])
238
239########################################################################
240#
241# Walk the tree and execute all SConscripts in subdirectories
242#
243
244here = Dir('.').srcnode().abspath
245for root, dirs, files in os.walk(base_dir, topdown=True):
246    if root == here:
247        # we don't want to recurse back into this SConscript
248        continue
249
250    if 'SConscript' in files:
251        build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
252        SConscript(joinpath(root, 'SConscript'), build_dir=build_dir)
253
254for extra_dir in extras_dir_list:
255    prefix_len = len(dirname(extra_dir)) + 1
256    for root, dirs, files in os.walk(extra_dir, topdown=True):
257        if 'SConscript' in files:
258            build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
259            SConscript(joinpath(root, 'SConscript'), build_dir=build_dir)
260
261for opt in export_vars:
262    env.ConfigFile(opt)
263
264########################################################################
265#
266# Prevent any SimObjects from being added after this point, they
267# should all have been added in the SConscripts above
268#
269SimObject.fixed = True
270
271class DictImporter(object):
272    '''This importer takes a dictionary of arbitrary module names that
273    map to arbitrary filenames.'''
274    def __init__(self, modules):
275        self.modules = modules
276        self.installed = set()
277
278    def __del__(self):
279        self.unload()
280
281    def unload(self):
282        import sys
283        for module in self.installed:
284            del sys.modules[module]
285        self.installed = set()
286
287    def find_module(self, fullname, path):
288        if fullname == 'm5.defines':
289            return self
290
291        if fullname == 'm5.objects':
292            return self
293
294        if fullname.startswith('m5.internal'):
295            return None
296
297        source = self.modules.get(fullname, None)
298        if source is not None and fullname.startswith('m5.objects'):
299            return self
300
301        return None
302
303    def load_module(self, fullname):
304        mod = imp.new_module(fullname)
305        sys.modules[fullname] = mod
306        self.installed.add(fullname)
307
308        mod.__loader__ = self
309        if fullname == 'm5.objects':
310            mod.__path__ = fullname.split('.')
311            return mod
312
313        if fullname == 'm5.defines':
314            mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
315            return mod
316
317        source = self.modules[fullname]
318        if source.modname == '__init__':
319            mod.__path__ = source.modpath
320        mod.__file__ = source.snode.abspath
321
322        exec file(source.snode.abspath, 'r') in mod.__dict__
323
324        return mod
325
326import m5.SimObject
327import m5.params
328
329m5.SimObject.clear()
330m5.params.clear()
331
332# install the python importer so we can grab stuff from the source
333# tree itself.  We can't have SimObjects added after this point or
334# else we won't know about them for the rest of the stuff.
335importer = DictImporter(PySource.modules)
336sys.meta_path[0:0] = [ importer ]
337
338# import all sim objects so we can populate the all_objects list
339# make sure that we're working with a list, then let's sort it
340for modname in SimObject.modnames:
341    exec('from m5.objects import %s' % modname)
342
343# we need to unload all of the currently imported modules so that they
344# will be re-imported the next time the sconscript is run
345importer.unload()
346sys.meta_path.remove(importer)
347
348sim_objects = m5.SimObject.allClasses
349all_enums = m5.params.allEnums
350
351all_params = {}
352for name,obj in sorted(sim_objects.iteritems()):
353    for param in obj._params.local.values():
354        # load the ptype attribute now because it depends on the
355        # current version of SimObject.allClasses, but when scons
356        # actually uses the value, all versions of
357        # SimObject.allClasses will have been loaded
358        param.ptype
359
360        if not hasattr(param, 'swig_decl'):
361            continue
362        pname = param.ptype_str
363        if pname not in all_params:
364            all_params[pname] = param
365
366########################################################################
367#
368# calculate extra dependencies
369#
370module_depends = ["m5", "m5.SimObject", "m5.params"]
371depends = [ PySource.modules[dep].tnode for dep in module_depends ]
372
373########################################################################
374#
375# Commands for the basic automatically generated python files
376#
377
378# Generate Python file containing a dict specifying the current
379# buildEnv flags.
380def makeDefinesPyFile(target, source, env):
381    build_env, hg_info = [ x.get_contents() for x in source ]
382
383    code = m5.util.code_formatter()
384    code("""
385import m5.internal
386import m5.util
387
388buildEnv = m5.util.SmartDict($build_env)
389hgRev = '$hg_info'
390
391compileDate = m5.internal.core.compileDate
392for k,v in m5.internal.core.__dict__.iteritems():
393    if k.startswith('flag_'):
394        setattr(buildEnv, k[5:], v)
395""")
396    code.write(str(target[0]))
397
398defines_info = [ Value(build_env), Value(env['HG_INFO']) ]
399# Generate a file with all of the compile options in it
400env.Command('python/m5/defines.py', defines_info, makeDefinesPyFile)
401PySource('m5', 'python/m5/defines.py')
402
403# Generate python file containing info about the M5 source code
404def makeInfoPyFile(target, source, env):
405    f = file(str(target[0]), 'w')
406    for src in source:
407        data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
408        print >>f, "%s = %s" % (src, repr(data))
409    f.close()
410
411# Generate a file that wraps the basic top level files
412env.Command('python/m5/info.py',
413            [ '#/AUTHORS', '#/LICENSE', '#/README', '#/RELEASE_NOTES' ],
414            makeInfoPyFile)
415PySource('m5', 'python/m5/info.py')
416
417# Generate the __init__.py file for m5.objects
418def makeObjectsInitFile(target, source, env):
419    f = file(str(target[0]), 'w')
420    print >>f, 'from params import *'
421    print >>f, 'from m5.SimObject import *'
422    for module in source:
423        print >>f, 'from %s import *' % module.get_contents()
424    f.close()
425
426# Generate an __init__.py file for the objects package
427env.Command('python/m5/objects/__init__.py',
428            map(Value, SimObject.modnames),
429            makeObjectsInitFile)
430PySource('m5.objects', 'python/m5/objects/__init__.py')
431
432########################################################################
433#
434# Create all of the SimObject param headers and enum headers
435#
436
437def createSimObjectParam(target, source, env):
438    assert len(target) == 1 and len(source) == 1
439
440    hh_file = file(target[0].abspath, 'w')
441    name = str(source[0].get_contents())
442    obj = sim_objects[name]
443
444    print >>hh_file, obj.cxx_decl()
445    hh_file.close()
446
447def createSwigParam(target, source, env):
448    assert len(target) == 1 and len(source) == 1
449
450    i_file = file(target[0].abspath, 'w')
451    name = str(source[0].get_contents())
452    param = all_params[name]
453
454    for line in param.swig_decl():
455        print >>i_file, line
456    i_file.close()
457
458def createEnumStrings(target, source, env):
459    assert len(target) == 1 and len(source) == 1
460
461    cc_file = file(target[0].abspath, 'w')
462    name = str(source[0].get_contents())
463    obj = all_enums[name]
464
465    print >>cc_file, obj.cxx_def()
466    cc_file.close()
467
468def createEnumParam(target, source, env):
469    assert len(target) == 1 and len(source) == 1
470
471    hh_file = file(target[0].abspath, 'w')
472    name = str(source[0].get_contents())
473    obj = all_enums[name]
474
475    print >>hh_file, obj.cxx_decl()
476    hh_file.close()
477
478# Generate all of the SimObject param struct header files
479params_hh_files = []
480for name,simobj in sorted(sim_objects.iteritems()):
481    py_source = PySource.modules[simobj.__module__]
482    extra_deps = [ py_source.tnode ]
483
484    hh_file = File('params/%s.hh' % name)
485    params_hh_files.append(hh_file)
486    env.Command(hh_file, Value(name), createSimObjectParam)
487    env.Depends(hh_file, depends + extra_deps)
488
489# Generate any parameter header files needed
490params_i_files = []
491for name,param in all_params.iteritems():
492    i_file = File('params/%s_%s.i' % (name, param.file_ext))
493    params_i_files.append(i_file)
494    env.Command(i_file, Value(name), createSwigParam)
495    env.Depends(i_file, depends)
496
497# Generate all enum header files
498for name,enum in sorted(all_enums.iteritems()):
499    py_source = PySource.modules[enum.__module__]
500    extra_deps = [ py_source.tnode ]
501
502    cc_file = File('enums/%s.cc' % name)
503    env.Command(cc_file, Value(name), createEnumStrings)
504    env.Depends(cc_file, depends + extra_deps)
505    Source(cc_file)
506
507    hh_file = File('enums/%s.hh' % name)
508    env.Command(hh_file, Value(name), createEnumParam)
509    env.Depends(hh_file, depends + extra_deps)
510
511# Build the big monolithic swigged params module (wraps all SimObject
512# param structs and enum structs)
513def buildParams(target, source, env):
514    names = [ s.get_contents() for s in source ]
515    objs = [ sim_objects[name] for name in names ]
516    out = file(target[0].abspath, 'w')
517
518    ordered_objs = []
519    obj_seen = set()
520    def order_obj(obj):
521        name = str(obj)
522        if name in obj_seen:
523            return
524
525        obj_seen.add(name)
526        if str(obj) != 'SimObject':
527            order_obj(obj.__bases__[0])
528
529        ordered_objs.append(obj)
530
531    for obj in objs:
532        order_obj(obj)
533
534    enums = set()
535    predecls = []
536    pd_seen = set()
537
538    def add_pds(*pds):
539        for pd in pds:
540            if pd not in pd_seen:
541                predecls.append(pd)
542                pd_seen.add(pd)
543
544    for obj in ordered_objs:
545        params = obj._params.local.values()
546        for param in params:
547            ptype = param.ptype
548            if issubclass(ptype, m5.params.Enum):
549                if ptype not in enums:
550                    enums.add(ptype)
551            pds = param.swig_predecls()
552            if isinstance(pds, (list, tuple)):
553                add_pds(*pds)
554            else:
555                add_pds(pds)
556
557    print >>out, '%module params'
558
559    print >>out, '%{'
560    for obj in ordered_objs:
561        print >>out, '#include "params/%s.hh"' % obj
562    print >>out, '%}'
563
564    for pd in predecls:
565        print >>out, pd
566
567    enums = list(enums)
568    enums.sort()
569    for enum in enums:
570        print >>out, '%%include "enums/%s.hh"' % enum.__name__
571    print >>out
572
573    for obj in ordered_objs:
574        if obj.swig_objdecls:
575            for decl in obj.swig_objdecls:
576                print >>out, decl
577            continue
578
579        class_path = obj.cxx_class.split('::')
580        classname = class_path[-1]
581        namespaces = class_path[:-1]
582        namespaces.reverse()
583
584        code = ''
585
586        if namespaces:
587            code += '// avoid name conflicts\n'
588            sep_string = '_COLONS_'
589            flat_name = sep_string.join(class_path)
590            code += '%%rename(%s) %s;\n' % (flat_name, classname)
591
592        code += '// stop swig from creating/wrapping default ctor/dtor\n'
593        code += '%%nodefault %s;\n' % classname
594        code += 'class %s ' % classname
595        if obj._base:
596            code += ': public %s' % obj._base.cxx_class
597        code += ' {};\n'
598
599        for ns in namespaces:
600            new_code = 'namespace %s {\n' % ns
601            new_code += code
602            new_code += '}\n'
603            code = new_code
604
605        print >>out, code
606
607    print >>out, '%%include "src/sim/sim_object_params.hh"' % obj
608    for obj in ordered_objs:
609        print >>out, '%%include "params/%s.hh"' % obj
610
611params_file = File('params/params.i')
612names = sorted(sim_objects.keys())
613env.Command(params_file, map(Value, names), buildParams)
614env.Depends(params_file, params_hh_files + params_i_files + depends)
615SwigSource('m5.objects', params_file)
616
617# Build all swig modules
618for swig in SwigSource.all:
619    env.Command([swig.cc_source.tnode, swig.py_source.tnode], swig.tnode,
620                '$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} '
621                '-o ${TARGETS[0]} $SOURCES')
622    env.Depends(swig.py_source.tnode, swig.tnode)
623    env.Depends(swig.cc_source.tnode, swig.tnode)
624
625# Generate the main swig init file
626def makeSwigInit(target, source, env):
627    f = file(str(target[0]), 'w')
628    print >>f, 'extern "C" {'
629    for module in source:
630        print >>f, '    void init_%s();' % module.get_contents()
631    print >>f, '}'
632    print >>f, 'void initSwig() {'
633    for module in source:
634        print >>f, '    init_%s();' % module.get_contents()
635    print >>f, '}'
636    f.close()
637
638env.Command('python/swig/init.cc',
639            map(Value, sorted(s.module for s in SwigSource.all)),
640            makeSwigInit)
641Source('python/swig/init.cc')
642
643def getFlags(source_flags):
644    flagsMap = {}
645    flagsList = []
646    for s in source_flags:
647        val = eval(s.get_contents())
648        name, compound, desc = val
649        flagsList.append(val)
650        flagsMap[name] = bool(compound)
651    
652    for name, compound, desc in flagsList:
653        for flag in compound:
654            if flag not in flagsMap:
655                raise AttributeError, "Trace flag %s not found" % flag
656            if flagsMap[flag]:
657                raise AttributeError, \
658                    "Compound flag can't point to another compound flag"
659
660    flagsList.sort()
661    return flagsList
662
663
664# Generate traceflags.py
665def traceFlagsPy(target, source, env):
666    assert(len(target) == 1)
667
668    f = file(str(target[0]), 'w')
669   
670    allFlags = getFlags(source)
671
672    print >>f, 'basic = ['
673    for flag, compound, desc in allFlags:
674        if not compound:
675            print >>f, "    '%s'," % flag
676    print >>f, "    ]"
677    print >>f
678
679    print >>f, 'compound = ['
680    print >>f, "    'All',"
681    for flag, compound, desc in allFlags:
682        if compound:
683            print >>f, "    '%s'," % flag
684    print >>f, "    ]"
685    print >>f
686
687    print >>f, "all = frozenset(basic + compound)"
688    print >>f
689
690    print >>f, 'compoundMap = {'
691    all = tuple([flag for flag,compound,desc in allFlags if not compound])
692    print >>f, "    'All' : %s," % (all, )
693    for flag, compound, desc in allFlags:
694        if compound:
695            print >>f, "    '%s' : %s," % (flag, compound)
696    print >>f, "    }"
697    print >>f
698
699    print >>f, 'descriptions = {'
700    print >>f, "    'All' : 'All flags',"
701    for flag, compound, desc in allFlags:
702        print >>f, "    '%s' : '%s'," % (flag, desc)
703    print >>f, "    }"
704
705    f.close()
706
707def traceFlagsCC(target, source, env):
708    assert(len(target) == 1)
709
710    f = file(str(target[0]), 'w')
711
712    allFlags = getFlags(source)
713
714    # file header
715    print >>f, '''
716/*
717 * DO NOT EDIT THIS FILE! Automatically generated
718 */
719
720#include "base/traceflags.hh"
721
722using namespace Trace;
723
724const char *Trace::flagStrings[] =
725{'''
726
727    # The string array is used by SimpleEnumParam to map the strings
728    # provided by the user to enum values.
729    for flag, compound, desc in allFlags:
730        if not compound:
731            print >>f, '    "%s",' % flag
732
733    print >>f, '    "All",'
734    for flag, compound, desc in allFlags:
735        if compound:
736            print >>f, '    "%s",' % flag
737
738    print >>f, '};'
739    print >>f
740    print >>f, 'const int Trace::numFlagStrings = %d;' % (len(allFlags) + 1)
741    print >>f
742
743    #
744    # Now define the individual compound flag arrays.  There is an array
745    # for each compound flag listing the component base flags.
746    #
747    all = tuple([flag for flag,compound,desc in allFlags if not compound])
748    print >>f, 'static const Flags AllMap[] = {'
749    for flag, compound, desc in allFlags:
750        if not compound:
751            print >>f, "    %s," % flag
752    print >>f, '};'
753    print >>f
754
755    for flag, compound, desc in allFlags:
756        if not compound:
757            continue
758        print >>f, 'static const Flags %sMap[] = {' % flag
759        for flag in compound:
760            print >>f, "    %s," % flag
761        print >>f, "    (Flags)-1"
762        print >>f, '};'
763        print >>f
764
765    #
766    # Finally the compoundFlags[] array maps the compound flags
767    # to their individual arrays/
768    #
769    print >>f, 'const Flags *Trace::compoundFlags[] ='
770    print >>f, '{'
771    print >>f, '    AllMap,'
772    for flag, compound, desc in allFlags:
773        if compound:
774            print >>f, '    %sMap,' % flag
775    # file trailer
776    print >>f, '};'
777
778    f.close()
779
780def traceFlagsHH(target, source, env):
781    assert(len(target) == 1)
782
783    f = file(str(target[0]), 'w')
784
785    allFlags = getFlags(source)
786
787    # file header boilerplate
788    print >>f, '''
789/*
790 * DO NOT EDIT THIS FILE!
791 *
792 * Automatically generated from traceflags.py
793 */
794
795#ifndef __BASE_TRACE_FLAGS_HH__
796#define __BASE_TRACE_FLAGS_HH__
797
798namespace Trace {
799
800enum Flags {'''
801
802    # Generate the enum.  Base flags come first, then compound flags.
803    idx = 0
804    for flag, compound, desc in allFlags:
805        if not compound:
806            print >>f, '    %s = %d,' % (flag, idx)
807            idx += 1
808
809    numBaseFlags = idx
810    print >>f, '    NumFlags = %d,' % idx
811
812    # put a comment in here to separate base from compound flags
813    print >>f, '''
814// The remaining enum values are *not* valid indices for Trace::flags.
815// They are "compound" flags, which correspond to sets of base
816// flags, and are used by changeFlag.'''
817
818    print >>f, '    All = %d,' % idx
819    idx += 1
820    for flag, compound, desc in allFlags:
821        if compound:
822            print >>f, '    %s = %d,' % (flag, idx)
823            idx += 1
824
825    numCompoundFlags = idx - numBaseFlags
826    print >>f, '    NumCompoundFlags = %d' % numCompoundFlags
827
828    # trailer boilerplate
829    print >>f, '''\
830}; // enum Flags
831
832// Array of strings for SimpleEnumParam
833extern const char *flagStrings[];
834extern const int numFlagStrings;
835
836// Array of arraay pointers: for each compound flag, gives the list of
837// base flags to set.  Inidividual flag arrays are terminated by -1.
838extern const Flags *compoundFlags[];
839
840/* namespace Trace */ }
841
842#endif // __BASE_TRACE_FLAGS_HH__
843'''
844
845    f.close()
846
847flags = map(Value, trace_flags.values())
848env.Command('base/traceflags.py', flags, traceFlagsPy)
849PySource('m5', 'base/traceflags.py')
850
851env.Command('base/traceflags.hh', flags, traceFlagsHH)
852env.Command('base/traceflags.cc', flags, traceFlagsCC)
853Source('base/traceflags.cc')
854
855# embed python files.  All .py files that have been indicated by a
856# PySource() call in a SConscript need to be embedded into the M5
857# library.  To do that, we compile the file to byte code, marshal the
858# byte code, compress it, and then generate an assembly file that
859# inserts the result into the data section with symbols indicating the
860# beginning, and end (and with the size at the end)
861def objectifyPyFile(target, source, env):
862    '''Action function to compile a .py into a code object, marshal
863    it, compress it, and stick it into an asm file so the code appears
864    as just bytes with a label in the data section'''
865
866    src = file(str(source[0]), 'r').read()
867    dst = file(str(target[0]), 'w')
868
869    pysource = PySource.tnodes[source[0]]
870    compiled = compile(src, pysource.debugname, 'exec')
871    marshalled = marshal.dumps(compiled)
872    compressed = zlib.compress(marshalled)
873    data = compressed
874
875    # Some C/C++ compilers prepend an underscore to global symbol
876    # names, so if they're going to do that, we need to prepend that
877    # leading underscore to globals in the assembly file.
878    if env['LEADING_UNDERSCORE']:
879        sym = '_' + pysource.symname
880    else:
881        sym = pysource.symname
882
883    step = 16
884    print >>dst, ".data"
885    print >>dst, ".globl %s_beg" % sym
886    print >>dst, ".globl %s_end" % sym
887    print >>dst, "%s_beg:" % sym
888    for i in xrange(0, len(data), step):
889        x = array.array('B', data[i:i+step])
890        print >>dst, ".byte", ','.join([str(d) for d in x])
891    print >>dst, "%s_end:" % sym
892    print >>dst, ".long %d" % len(marshalled)
893
894for source in PySource.all:
895    env.Command(source.assembly, source.tnode, objectifyPyFile)
896    Source(source.assembly)
897
898# Generate init_python.cc which creates a bunch of EmbeddedPyModule
899# structs that describe the embedded python code.  One such struct
900# contains information about the importer that python uses to get at
901# the embedded files, and then there's a list of all of the rest that
902# the importer uses to load the rest on demand.
903def pythonInit(target, source, env):
904    dst = file(str(target[0]), 'w')
905
906    def dump_mod(sym, endchar=','):
907        pysource = PySource.symnames[sym]
908        print >>dst, '    { "%s",' % pysource.arcname
909        print >>dst, '      "%s",' % pysource.modpath
910        print >>dst, '       %s_beg, %s_end,' % (sym, sym)
911        print >>dst, '       %s_end - %s_beg,' % (sym, sym)
912        print >>dst, '       *(int *)%s_end }%s'  % (sym, endchar)
913    
914    print >>dst, '#include "sim/init.hh"'
915
916    for sym in source:
917        sym = sym.get_contents()
918        print >>dst, "extern const char %s_beg[], %s_end[];" % (sym, sym)
919
920    print >>dst, "const EmbeddedPyModule embeddedPyImporter = "
921    dump_mod("PyEMB_importer", endchar=';');
922    print >>dst
923
924    print >>dst, "const EmbeddedPyModule embeddedPyModules[] = {"
925    for i,sym in enumerate(source):
926        sym = sym.get_contents()
927        if sym == "PyEMB_importer":
928            # Skip the importer since we've already exported it
929            continue
930        dump_mod(sym)
931    print >>dst, "    { 0, 0, 0, 0, 0, 0 }"
932    print >>dst, "};"
933
934
935env.Command('sim/init_python.cc',
936            map(Value, (s.symname for s in PySource.all)),
937            pythonInit)
938Source('sim/init_python.cc')
939
940########################################################################
941#
942# Define binaries.  Each different build type (debug, opt, etc.) gets
943# a slightly different build environment.
944#
945
946# List of constructed environments to pass back to SConstruct
947envList = []
948
949date_source = Source('base/date.cc', skip_lib=True)
950
951# Function to create a new build environment as clone of current
952# environment 'env' with modified object suffix and optional stripped
953# binary.  Additional keyword arguments are appended to corresponding
954# build environment vars.
955def makeEnv(label, objsfx, strip = False, **kwargs):
956    # SCons doesn't know to append a library suffix when there is a '.' in the
957    # name.  Use '_' instead.
958    libname = 'm5_' + label
959    exename = 'm5.' + label
960
961    new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
962    new_env.Label = label
963    new_env.Append(**kwargs)
964
965    swig_env = new_env.Clone()
966    swig_env.Append(CCFLAGS='-Werror')
967    if env['GCC']:
968        swig_env.Append(CCFLAGS='-Wno-uninitialized')
969        swig_env.Append(CCFLAGS='-Wno-sign-compare')
970        swig_env.Append(CCFLAGS='-Wno-parentheses')
971
972    werror_env = new_env.Clone()
973    werror_env.Append(CCFLAGS='-Werror')
974
975    def make_obj(source, static, extra_deps = None):
976        '''This function adds the specified source to the correct
977        build environment, and returns the corresponding SCons Object
978        nodes'''
979
980        if source.swig:
981            env = swig_env
982        elif source.Werror:
983            env = werror_env
984        else:
985            env = new_env
986
987        if static:
988            obj = env.StaticObject(source.tnode)
989        else:
990            obj = env.SharedObject(source.tnode)
991
992        if extra_deps:
993            env.Depends(obj, extra_deps)
994
995        return obj
996
997    static_objs = [ make_obj(s, True) for s in Source.get(skip_lib=False)]
998    shared_objs = [ make_obj(s, False) for s in Source.get(skip_lib=False)]
999
1000    static_date = make_obj(date_source, static=True, extra_deps=static_objs)
1001    static_objs.append(static_date)
1002    
1003    shared_date = make_obj(date_source, static=False, extra_deps=shared_objs)
1004    shared_objs.append(shared_date)
1005
1006    # First make a library of everything but main() so other programs can
1007    # link against m5.
1008    static_lib = new_env.StaticLibrary(libname, static_objs)
1009    shared_lib = new_env.SharedLibrary(libname, shared_objs)
1010
1011    for target, sources in unit_tests:
1012        objs = [ make_obj(s, static=True) for s in sources ]
1013        new_env.Program("unittest/%s.%s" % (target, label), objs + static_objs)
1014
1015    # Now link a stub with main() and the static library.
1016    bin_objs = [make_obj(s, True) for s in Source.get(bin_only=True) ]
1017    progname = exename
1018    if strip:
1019        progname += '.unstripped'
1020
1021    targets = new_env.Program(progname, bin_objs + static_objs)
1022
1023    if strip:
1024        if sys.platform == 'sunos5':
1025            cmd = 'cp $SOURCE $TARGET; strip $TARGET'
1026        else:
1027            cmd = 'strip $SOURCE -o $TARGET'
1028        targets = new_env.Command(exename, progname, cmd)
1029            
1030    new_env.M5Binary = targets[0]
1031    envList.append(new_env)
1032
1033# Debug binary
1034ccflags = {}
1035if env['GCC']:
1036    if sys.platform == 'sunos5':
1037        ccflags['debug'] = '-gstabs+'
1038    else:
1039        ccflags['debug'] = '-ggdb3'
1040    ccflags['opt'] = '-g -O3'
1041    ccflags['fast'] = '-O3'
1042    ccflags['prof'] = '-O3 -g -pg'
1043elif env['SUNCC']:
1044    ccflags['debug'] = '-g0'
1045    ccflags['opt'] = '-g -O'
1046    ccflags['fast'] = '-fast'
1047    ccflags['prof'] = '-fast -g -pg'
1048elif env['ICC']:
1049    ccflags['debug'] = '-g -O0'
1050    ccflags['opt'] = '-g -O'
1051    ccflags['fast'] = '-fast'
1052    ccflags['prof'] = '-fast -g -pg'
1053else:
1054    print 'Unknown compiler, please fix compiler options'
1055    Exit(1)
1056
1057makeEnv('debug', '.do',
1058        CCFLAGS = Split(ccflags['debug']),
1059        CPPDEFINES = ['DEBUG', 'TRACING_ON=1'])
1060
1061# Optimized binary
1062makeEnv('opt', '.o',
1063        CCFLAGS = Split(ccflags['opt']),
1064        CPPDEFINES = ['TRACING_ON=1'])
1065
1066# "Fast" binary
1067makeEnv('fast', '.fo', strip = True,
1068        CCFLAGS = Split(ccflags['fast']),
1069        CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'])
1070
1071# Profiled binary
1072makeEnv('prof', '.po',
1073        CCFLAGS = Split(ccflags['prof']),
1074        CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1075        LINKFLAGS = '-pg')
1076
1077Return('envList')
1078