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