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