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