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