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