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