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