SConscript revision 6658
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# 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.snode.abspath
343
344        exec file(source.snode.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
414for k,v in m5.internal.core.__dict__.iteritems():
415    if k.startswith('flag_'):
416        setattr(buildEnv, k[5:], v)
417""")
418    code.write(str(target[0]))
419
420defines_info = [ Value(build_env), Value(env['HG_INFO']) ]
421# Generate a file with all of the compile options in it
422env.Command('python/m5/defines.py', defines_info, makeDefinesPyFile)
423PySource('m5', 'python/m5/defines.py')
424
425# Generate python file containing info about the M5 source code
426def makeInfoPyFile(target, source, env):
427    f = file(str(target[0]), 'w')
428    for src in source:
429        data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
430        print >>f, "%s = %s" % (src, repr(data))
431    f.close()
432
433# Generate a file that wraps the basic top level files
434env.Command('python/m5/info.py',
435            [ '#/AUTHORS', '#/LICENSE', '#/README', '#/RELEASE_NOTES' ],
436            makeInfoPyFile)
437PySource('m5', 'python/m5/info.py')
438
439# Generate the __init__.py file for m5.objects
440def makeObjectsInitFile(target, source, env):
441    f = file(str(target[0]), 'w')
442    print >>f, 'from params import *'
443    print >>f, 'from m5.SimObject import *'
444    for module in source:
445        print >>f, 'from %s import *' % module.get_contents()
446    f.close()
447
448# Generate an __init__.py file for the objects package
449env.Command('python/m5/objects/__init__.py',
450            map(Value, SimObject.modnames),
451            makeObjectsInitFile)
452PySource('m5.objects', 'python/m5/objects/__init__.py')
453
454########################################################################
455#
456# Create all of the SimObject param headers and enum headers
457#
458
459def createSimObjectParam(target, source, env):
460    assert len(target) == 1 and len(source) == 1
461
462    hh_file = file(target[0].abspath, 'w')
463    name = str(source[0].get_contents())
464    obj = sim_objects[name]
465
466    print >>hh_file, obj.cxx_decl()
467    hh_file.close()
468
469def createSwigParam(target, source, env):
470    assert len(target) == 1 and len(source) == 1
471
472    i_file = file(target[0].abspath, 'w')
473    name = str(source[0].get_contents())
474    param = all_params[name]
475
476    for line in param.swig_decl():
477        print >>i_file, line
478    i_file.close()
479
480def createEnumStrings(target, source, env):
481    assert len(target) == 1 and len(source) == 1
482
483    cc_file = file(target[0].abspath, 'w')
484    name = str(source[0].get_contents())
485    obj = all_enums[name]
486
487    print >>cc_file, obj.cxx_def()
488    cc_file.close()
489
490def createEnumParam(target, source, env):
491    assert len(target) == 1 and len(source) == 1
492
493    hh_file = file(target[0].abspath, 'w')
494    name = str(source[0].get_contents())
495    obj = all_enums[name]
496
497    print >>hh_file, obj.cxx_decl()
498    hh_file.close()
499
500# Generate all of the SimObject param struct header files
501params_hh_files = []
502for name,simobj in sorted(sim_objects.iteritems()):
503    py_source = PySource.modules[simobj.__module__]
504    extra_deps = [ py_source.tnode ]
505
506    hh_file = File('params/%s.hh' % name)
507    params_hh_files.append(hh_file)
508    env.Command(hh_file, Value(name), createSimObjectParam)
509    env.Depends(hh_file, depends + extra_deps)
510
511# Generate any parameter header files needed
512params_i_files = []
513for name,param in all_params.iteritems():
514    i_file = File('params/%s_%s.i' % (name, param.file_ext))
515    params_i_files.append(i_file)
516    env.Command(i_file, Value(name), createSwigParam)
517    env.Depends(i_file, depends)
518
519# Generate all enum header files
520for name,enum in sorted(all_enums.iteritems()):
521    py_source = PySource.modules[enum.__module__]
522    extra_deps = [ py_source.tnode ]
523
524    cc_file = File('enums/%s.cc' % name)
525    env.Command(cc_file, Value(name), createEnumStrings)
526    env.Depends(cc_file, depends + extra_deps)
527    Source(cc_file)
528
529    hh_file = File('enums/%s.hh' % name)
530    env.Command(hh_file, Value(name), createEnumParam)
531    env.Depends(hh_file, depends + extra_deps)
532
533# Build the big monolithic swigged params module (wraps all SimObject
534# param structs and enum structs)
535def buildParams(target, source, env):
536    names = [ s.get_contents() for s in source ]
537    objs = [ sim_objects[name] for name in names ]
538    out = file(target[0].abspath, 'w')
539
540    ordered_objs = []
541    obj_seen = set()
542    def order_obj(obj):
543        name = str(obj)
544        if name in obj_seen:
545            return
546
547        obj_seen.add(name)
548        if str(obj) != 'SimObject':
549            order_obj(obj.__bases__[0])
550
551        ordered_objs.append(obj)
552
553    for obj in objs:
554        order_obj(obj)
555
556    enums = set()
557    predecls = []
558    pd_seen = set()
559
560    def add_pds(*pds):
561        for pd in pds:
562            if pd not in pd_seen:
563                predecls.append(pd)
564                pd_seen.add(pd)
565
566    for obj in ordered_objs:
567        params = obj._params.local.values()
568        for param in params:
569            ptype = param.ptype
570            if issubclass(ptype, m5.params.Enum):
571                if ptype not in enums:
572                    enums.add(ptype)
573            pds = param.swig_predecls()
574            if isinstance(pds, (list, tuple)):
575                add_pds(*pds)
576            else:
577                add_pds(pds)
578
579    print >>out, '%module params'
580
581    print >>out, '%{'
582    for obj in ordered_objs:
583        print >>out, '#include "params/%s.hh"' % obj
584    print >>out, '%}'
585
586    for pd in predecls:
587        print >>out, pd
588
589    enums = list(enums)
590    enums.sort()
591    for enum in enums:
592        print >>out, '%%include "enums/%s.hh"' % enum.__name__
593    print >>out
594
595    for obj in ordered_objs:
596        if obj.swig_objdecls:
597            for decl in obj.swig_objdecls:
598                print >>out, decl
599            continue
600
601        class_path = obj.cxx_class.split('::')
602        classname = class_path[-1]
603        namespaces = class_path[:-1]
604        namespaces.reverse()
605
606        code = ''
607
608        if namespaces:
609            code += '// avoid name conflicts\n'
610            sep_string = '_COLONS_'
611            flat_name = sep_string.join(class_path)
612            code += '%%rename(%s) %s;\n' % (flat_name, classname)
613
614        code += '// stop swig from creating/wrapping default ctor/dtor\n'
615        code += '%%nodefault %s;\n' % classname
616        code += 'class %s ' % classname
617        if obj._base:
618            code += ': public %s' % obj._base.cxx_class
619        code += ' {};\n'
620
621        for ns in namespaces:
622            new_code = 'namespace %s {\n' % ns
623            new_code += code
624            new_code += '}\n'
625            code = new_code
626
627        print >>out, code
628
629    print >>out, '%%include "src/sim/sim_object_params.hh"' % obj
630    for obj in ordered_objs:
631        print >>out, '%%include "params/%s.hh"' % obj
632
633params_file = File('params/params.i')
634names = sorted(sim_objects.keys())
635env.Command(params_file, map(Value, names), buildParams)
636env.Depends(params_file, params_hh_files + params_i_files + depends)
637SwigSource('m5.objects', params_file)
638
639# Build all swig modules
640for swig in SwigSource.all:
641    env.Command([swig.cc_source.tnode, swig.py_source.tnode], swig.tnode,
642                '$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} '
643                '-o ${TARGETS[0]} $SOURCES')
644    env.Depends(swig.py_source.tnode, swig.tnode)
645    env.Depends(swig.cc_source.tnode, swig.tnode)
646
647# Generate the main swig init file
648def makeSwigInit(target, source, env):
649    f = file(str(target[0]), 'w')
650    print >>f, 'extern "C" {'
651    for module in source:
652        print >>f, '    void init_%s();' % module.get_contents()
653    print >>f, '}'
654    print >>f, 'void initSwig() {'
655    for module in source:
656        print >>f, '    init_%s();' % module.get_contents()
657    print >>f, '}'
658    f.close()
659
660env.Command('python/swig/init.cc',
661            map(Value, sorted(s.module for s in SwigSource.all)),
662            makeSwigInit)
663Source('python/swig/init.cc')
664
665def getFlags(source_flags):
666    flagsMap = {}
667    flagsList = []
668    for s in source_flags:
669        val = eval(s.get_contents())
670        name, compound, desc = val
671        flagsList.append(val)
672        flagsMap[name] = bool(compound)
673    
674    for name, compound, desc in flagsList:
675        for flag in compound:
676            if flag not in flagsMap:
677                raise AttributeError, "Trace flag %s not found" % flag
678            if flagsMap[flag]:
679                raise AttributeError, \
680                    "Compound flag can't point to another compound flag"
681
682    flagsList.sort()
683    return flagsList
684
685
686# Generate traceflags.py
687def traceFlagsPy(target, source, env):
688    assert(len(target) == 1)
689
690    f = file(str(target[0]), 'w')
691   
692    allFlags = getFlags(source)
693
694    print >>f, 'basic = ['
695    for flag, compound, desc in allFlags:
696        if not compound:
697            print >>f, "    '%s'," % flag
698    print >>f, "    ]"
699    print >>f
700
701    print >>f, 'compound = ['
702    print >>f, "    'All',"
703    for flag, compound, desc in allFlags:
704        if compound:
705            print >>f, "    '%s'," % flag
706    print >>f, "    ]"
707    print >>f
708
709    print >>f, "all = frozenset(basic + compound)"
710    print >>f
711
712    print >>f, 'compoundMap = {'
713    all = tuple([flag for flag,compound,desc in allFlags if not compound])
714    print >>f, "    'All' : %s," % (all, )
715    for flag, compound, desc in allFlags:
716        if compound:
717            print >>f, "    '%s' : %s," % (flag, compound)
718    print >>f, "    }"
719    print >>f
720
721    print >>f, 'descriptions = {'
722    print >>f, "    'All' : 'All flags',"
723    for flag, compound, desc in allFlags:
724        print >>f, "    '%s' : '%s'," % (flag, desc)
725    print >>f, "    }"
726
727    f.close()
728
729def traceFlagsCC(target, source, env):
730    assert(len(target) == 1)
731
732    f = file(str(target[0]), 'w')
733
734    allFlags = getFlags(source)
735
736    # file header
737    print >>f, '''
738/*
739 * DO NOT EDIT THIS FILE! Automatically generated
740 */
741
742#include "base/traceflags.hh"
743
744using namespace Trace;
745
746const char *Trace::flagStrings[] =
747{'''
748
749    # The string array is used by SimpleEnumParam to map the strings
750    # provided by the user to enum values.
751    for flag, compound, desc in allFlags:
752        if not compound:
753            print >>f, '    "%s",' % flag
754
755    print >>f, '    "All",'
756    for flag, compound, desc in allFlags:
757        if compound:
758            print >>f, '    "%s",' % flag
759
760    print >>f, '};'
761    print >>f
762    print >>f, 'const int Trace::numFlagStrings = %d;' % (len(allFlags) + 1)
763    print >>f
764
765    #
766    # Now define the individual compound flag arrays.  There is an array
767    # for each compound flag listing the component base flags.
768    #
769    all = tuple([flag for flag,compound,desc in allFlags if not compound])
770    print >>f, 'static const Flags AllMap[] = {'
771    for flag, compound, desc in allFlags:
772        if not compound:
773            print >>f, "    %s," % flag
774    print >>f, '};'
775    print >>f
776
777    for flag, compound, desc in allFlags:
778        if not compound:
779            continue
780        print >>f, 'static const Flags %sMap[] = {' % flag
781        for flag in compound:
782            print >>f, "    %s," % flag
783        print >>f, "    (Flags)-1"
784        print >>f, '};'
785        print >>f
786
787    #
788    # Finally the compoundFlags[] array maps the compound flags
789    # to their individual arrays/
790    #
791    print >>f, 'const Flags *Trace::compoundFlags[] ='
792    print >>f, '{'
793    print >>f, '    AllMap,'
794    for flag, compound, desc in allFlags:
795        if compound:
796            print >>f, '    %sMap,' % flag
797    # file trailer
798    print >>f, '};'
799
800    f.close()
801
802def traceFlagsHH(target, source, env):
803    assert(len(target) == 1)
804
805    f = file(str(target[0]), 'w')
806
807    allFlags = getFlags(source)
808
809    # file header boilerplate
810    print >>f, '''
811/*
812 * DO NOT EDIT THIS FILE!
813 *
814 * Automatically generated from traceflags.py
815 */
816
817#ifndef __BASE_TRACE_FLAGS_HH__
818#define __BASE_TRACE_FLAGS_HH__
819
820namespace Trace {
821
822enum Flags {'''
823
824    # Generate the enum.  Base flags come first, then compound flags.
825    idx = 0
826    for flag, compound, desc in allFlags:
827        if not compound:
828            print >>f, '    %s = %d,' % (flag, idx)
829            idx += 1
830
831    numBaseFlags = idx
832    print >>f, '    NumFlags = %d,' % idx
833
834    # put a comment in here to separate base from compound flags
835    print >>f, '''
836// The remaining enum values are *not* valid indices for Trace::flags.
837// They are "compound" flags, which correspond to sets of base
838// flags, and are used by changeFlag.'''
839
840    print >>f, '    All = %d,' % idx
841    idx += 1
842    for flag, compound, desc in allFlags:
843        if compound:
844            print >>f, '    %s = %d,' % (flag, idx)
845            idx += 1
846
847    numCompoundFlags = idx - numBaseFlags
848    print >>f, '    NumCompoundFlags = %d' % numCompoundFlags
849
850    # trailer boilerplate
851    print >>f, '''\
852}; // enum Flags
853
854// Array of strings for SimpleEnumParam
855extern const char *flagStrings[];
856extern const int numFlagStrings;
857
858// Array of arraay pointers: for each compound flag, gives the list of
859// base flags to set.  Inidividual flag arrays are terminated by -1.
860extern const Flags *compoundFlags[];
861
862/* namespace Trace */ }
863
864#endif // __BASE_TRACE_FLAGS_HH__
865'''
866
867    f.close()
868
869flags = map(Value, trace_flags.values())
870env.Command('base/traceflags.py', flags, traceFlagsPy)
871PySource('m5', 'base/traceflags.py')
872
873env.Command('base/traceflags.hh', flags, traceFlagsHH)
874env.Command('base/traceflags.cc', flags, traceFlagsCC)
875Source('base/traceflags.cc')
876
877# embed python files.  All .py files that have been indicated by a
878# PySource() call in a SConscript need to be embedded into the M5
879# library.  To do that, we compile the file to byte code, marshal the
880# byte code, compress it, and then generate an assembly file that
881# inserts the result into the data section with symbols indicating the
882# beginning, and end (and with the size at the end)
883def objectifyPyFile(target, source, env):
884    '''Action function to compile a .py into a code object, marshal
885    it, compress it, and stick it into an asm file so the code appears
886    as just bytes with a label in the data section'''
887
888    src = file(str(source[0]), 'r').read()
889    dst = file(str(target[0]), 'w')
890
891    pysource = PySource.tnodes[source[0]]
892    compiled = compile(src, pysource.debugname, 'exec')
893    marshalled = marshal.dumps(compiled)
894    compressed = zlib.compress(marshalled)
895    data = compressed
896
897    # Some C/C++ compilers prepend an underscore to global symbol
898    # names, so if they're going to do that, we need to prepend that
899    # leading underscore to globals in the assembly file.
900    if env['LEADING_UNDERSCORE']:
901        sym = '_' + pysource.symname
902    else:
903        sym = pysource.symname
904
905    step = 16
906    print >>dst, ".data"
907    print >>dst, ".globl %s_beg" % sym
908    print >>dst, ".globl %s_end" % sym
909    print >>dst, "%s_beg:" % sym
910    for i in xrange(0, len(data), step):
911        x = array.array('B', data[i:i+step])
912        print >>dst, ".byte", ','.join([str(d) for d in x])
913    print >>dst, "%s_end:" % sym
914    print >>dst, ".long %d" % len(marshalled)
915
916for source in PySource.all:
917    env.Command(source.assembly, source.tnode, objectifyPyFile)
918    Source(source.assembly)
919
920# Generate init_python.cc which creates a bunch of EmbeddedPyModule
921# structs that describe the embedded python code.  One such struct
922# contains information about the importer that python uses to get at
923# the embedded files, and then there's a list of all of the rest that
924# the importer uses to load the rest on demand.
925def pythonInit(target, source, env):
926    dst = file(str(target[0]), 'w')
927
928    def dump_mod(sym, endchar=','):
929        pysource = PySource.symnames[sym]
930        print >>dst, '    { "%s",' % pysource.arcname
931        print >>dst, '      "%s",' % pysource.modpath
932        print >>dst, '       %s_beg, %s_end,' % (sym, sym)
933        print >>dst, '       %s_end - %s_beg,' % (sym, sym)
934        print >>dst, '       *(int *)%s_end }%s'  % (sym, endchar)
935    
936    print >>dst, '#include "sim/init.hh"'
937
938    for sym in source:
939        sym = sym.get_contents()
940        print >>dst, "extern const char %s_beg[], %s_end[];" % (sym, sym)
941
942    print >>dst, "const EmbeddedPyModule embeddedPyImporter = "
943    dump_mod("PyEMB_importer", endchar=';');
944    print >>dst
945
946    print >>dst, "const EmbeddedPyModule embeddedPyModules[] = {"
947    for i,sym in enumerate(source):
948        sym = sym.get_contents()
949        if sym == "PyEMB_importer":
950            # Skip the importer since we've already exported it
951            continue
952        dump_mod(sym)
953    print >>dst, "    { 0, 0, 0, 0, 0, 0 }"
954    print >>dst, "};"
955
956
957env.Command('sim/init_python.cc',
958            map(Value, (s.symname for s in PySource.all)),
959            pythonInit)
960Source('sim/init_python.cc')
961
962########################################################################
963#
964# Define binaries.  Each different build type (debug, opt, etc.) gets
965# a slightly different build environment.
966#
967
968# List of constructed environments to pass back to SConstruct
969envList = []
970
971date_source = Source('base/date.cc', skip_lib=True)
972
973# Function to create a new build environment as clone of current
974# environment 'env' with modified object suffix and optional stripped
975# binary.  Additional keyword arguments are appended to corresponding
976# build environment vars.
977def makeEnv(label, objsfx, strip = False, **kwargs):
978    # SCons doesn't know to append a library suffix when there is a '.' in the
979    # name.  Use '_' instead.
980    libname = 'm5_' + label
981    exename = 'm5.' + label
982
983    new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
984    new_env.Label = label
985    new_env.Append(**kwargs)
986
987    swig_env = new_env.Clone()
988    swig_env.Append(CCFLAGS='-Werror')
989    if env['GCC']:
990        swig_env.Append(CCFLAGS='-Wno-uninitialized')
991        swig_env.Append(CCFLAGS='-Wno-sign-compare')
992        swig_env.Append(CCFLAGS='-Wno-parentheses')
993
994    werror_env = new_env.Clone()
995    werror_env.Append(CCFLAGS='-Werror')
996
997    def make_obj(source, static, extra_deps = None):
998        '''This function adds the specified source to the correct
999        build environment, and returns the corresponding SCons Object
1000        nodes'''
1001
1002        if source.swig:
1003            env = swig_env
1004        elif source.Werror:
1005            env = werror_env
1006        else:
1007            env = new_env
1008
1009        if static:
1010            obj = env.StaticObject(source.tnode)
1011        else:
1012            obj = env.SharedObject(source.tnode)
1013
1014        if extra_deps:
1015            env.Depends(obj, extra_deps)
1016
1017        return obj
1018
1019    static_objs = [ make_obj(s, True) for s in Source.get(skip_lib=False)]
1020    shared_objs = [ make_obj(s, False) for s in Source.get(skip_lib=False)]
1021
1022    static_date = make_obj(date_source, static=True, extra_deps=static_objs)
1023    static_objs.append(static_date)
1024    
1025    shared_date = make_obj(date_source, static=False, extra_deps=shared_objs)
1026    shared_objs.append(shared_date)
1027
1028    # First make a library of everything but main() so other programs can
1029    # link against m5.
1030    static_lib = new_env.StaticLibrary(libname, static_objs)
1031    shared_lib = new_env.SharedLibrary(libname, shared_objs)
1032
1033    for target, sources in unit_tests:
1034        objs = [ make_obj(s, static=True) for s in sources ]
1035        new_env.Program("unittest/%s.%s" % (target, label), objs + static_objs)
1036
1037    # Now link a stub with main() and the static library.
1038    bin_objs = [make_obj(s, True) for s in Source.get(bin_only=True) ]
1039    progname = exename
1040    if strip:
1041        progname += '.unstripped'
1042
1043    targets = new_env.Program(progname, bin_objs + static_objs)
1044
1045    if strip:
1046        if sys.platform == 'sunos5':
1047            cmd = 'cp $SOURCE $TARGET; strip $TARGET'
1048        else:
1049            cmd = 'strip $SOURCE -o $TARGET'
1050        targets = new_env.Command(exename, progname, cmd)
1051            
1052    new_env.M5Binary = targets[0]
1053    envList.append(new_env)
1054
1055# Debug binary
1056ccflags = {}
1057if env['GCC']:
1058    if sys.platform == 'sunos5':
1059        ccflags['debug'] = '-gstabs+'
1060    else:
1061        ccflags['debug'] = '-ggdb3'
1062    ccflags['opt'] = '-g -O3'
1063    ccflags['fast'] = '-O3'
1064    ccflags['prof'] = '-O3 -g -pg'
1065elif env['SUNCC']:
1066    ccflags['debug'] = '-g0'
1067    ccflags['opt'] = '-g -O'
1068    ccflags['fast'] = '-fast'
1069    ccflags['prof'] = '-fast -g -pg'
1070elif env['ICC']:
1071    ccflags['debug'] = '-g -O0'
1072    ccflags['opt'] = '-g -O'
1073    ccflags['fast'] = '-fast'
1074    ccflags['prof'] = '-fast -g -pg'
1075else:
1076    print 'Unknown compiler, please fix compiler options'
1077    Exit(1)
1078
1079makeEnv('debug', '.do',
1080        CCFLAGS = Split(ccflags['debug']),
1081        CPPDEFINES = ['DEBUG', 'TRACING_ON=1'])
1082
1083# Optimized binary
1084makeEnv('opt', '.o',
1085        CCFLAGS = Split(ccflags['opt']),
1086        CPPDEFINES = ['TRACING_ON=1'])
1087
1088# "Fast" binary
1089makeEnv('fast', '.fo', strip = True,
1090        CCFLAGS = Split(ccflags['fast']),
1091        CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'])
1092
1093# Profiled binary
1094makeEnv('prof', '.po',
1095        CCFLAGS = Split(ccflags['prof']),
1096        CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1097        LINKFLAGS = '-pg')
1098
1099Return('envList')
1100