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