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