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