SConscript revision 9248:fdf49f35d2a4
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,simobj in sim_objects.iteritems():
638    py_source = PySource.modules[simobj.__module__]
639    extra_deps = [ py_source.tnode ]
640
641    i_file = File('python/m5/internal/param_%s.i' % name)
642    env.Command(i_file, Value(name),
643                MakeAction(createSimObjectSwigWrapper, Transform("SO SWIG")))
644    env.Depends(i_file, depends + extra_deps)
645    SwigSource('m5.internal', i_file)
646
647# Generate the main swig init file
648def makeEmbeddedSwigInit(target, source, env):
649    code = code_formatter()
650    module = source[0].get_contents()
651    code('''\
652#include "sim/init.hh"
653
654extern "C" {
655    void init_${module}();
656}
657
658EmbeddedSwig embed_swig_${module}(init_${module});
659''')
660    code.write(str(target[0]))
661    
662# Build all swig modules
663for swig in SwigSource.all:
664    env.Command([swig.cc_source.tnode, swig.py_source.tnode], swig.tnode,
665                MakeAction('$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} '
666                '-o ${TARGETS[0]} $SOURCES', Transform("SWIG")))
667    cc_file = str(swig.tnode)
668    init_file = '%s/%s_init.cc' % (dirname(cc_file), basename(cc_file))
669    env.Command(init_file, Value(swig.module),
670                MakeAction(makeEmbeddedSwigInit, Transform("EMBED SW")))
671    Source(init_file, **swig.guards)
672
673#
674# Handle debug flags
675#
676def makeDebugFlagCC(target, source, env):
677    assert(len(target) == 1 and len(source) == 1)
678
679    val = eval(source[0].get_contents())
680    name, compound, desc = val
681    compound = list(sorted(compound))
682
683    code = code_formatter()
684
685    # file header
686    code('''
687/*
688 * DO NOT EDIT THIS FILE! Automatically generated
689 */
690
691#include "base/debug.hh"
692''')
693
694    for flag in compound:
695        code('#include "debug/$flag.hh"')
696    code()
697    code('namespace Debug {')
698    code()
699
700    if not compound:
701        code('SimpleFlag $name("$name", "$desc");')
702    else:
703        code('CompoundFlag $name("$name", "$desc",')
704        code.indent()
705        last = len(compound) - 1
706        for i,flag in enumerate(compound):
707            if i != last:
708                code('$flag,')
709            else:
710                code('$flag);')
711        code.dedent()
712
713    code()
714    code('} // namespace Debug')
715
716    code.write(str(target[0]))
717
718def makeDebugFlagHH(target, source, env):
719    assert(len(target) == 1 and len(source) == 1)
720
721    val = eval(source[0].get_contents())
722    name, compound, desc = val
723
724    code = code_formatter()
725
726    # file header boilerplate
727    code('''\
728/*
729 * DO NOT EDIT THIS FILE!
730 *
731 * Automatically generated by SCons
732 */
733
734#ifndef __DEBUG_${name}_HH__
735#define __DEBUG_${name}_HH__
736
737namespace Debug {
738''')
739
740    if compound:
741        code('class CompoundFlag;')
742    code('class SimpleFlag;')
743
744    if compound:
745        code('extern CompoundFlag $name;')
746        for flag in compound:
747            code('extern SimpleFlag $flag;')
748    else:
749        code('extern SimpleFlag $name;')
750
751    code('''
752}
753
754#endif // __DEBUG_${name}_HH__
755''')
756
757    code.write(str(target[0]))
758
759for name,flag in sorted(debug_flags.iteritems()):
760    n, compound, desc = flag
761    assert n == name
762
763    env.Command('debug/%s.hh' % name, Value(flag),
764                MakeAction(makeDebugFlagHH, Transform("TRACING", 0)))
765    env.Command('debug/%s.cc' % name, Value(flag),
766                MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
767    Source('debug/%s.cc' % name)
768
769# Embed python files.  All .py files that have been indicated by a
770# PySource() call in a SConscript need to be embedded into the M5
771# library.  To do that, we compile the file to byte code, marshal the
772# byte code, compress it, and then generate a c++ file that
773# inserts the result into an array.
774def embedPyFile(target, source, env):
775    def c_str(string):
776        if string is None:
777            return "0"
778        return '"%s"' % string
779
780    '''Action function to compile a .py into a code object, marshal
781    it, compress it, and stick it into an asm file so the code appears
782    as just bytes with a label in the data section'''
783
784    src = file(str(source[0]), 'r').read()
785
786    pysource = PySource.tnodes[source[0]]
787    compiled = compile(src, pysource.abspath, 'exec')
788    marshalled = marshal.dumps(compiled)
789    compressed = zlib.compress(marshalled)
790    data = compressed
791    sym = pysource.symname
792
793    code = code_formatter()
794    code('''\
795#include "sim/init.hh"
796
797namespace {
798
799const uint8_t data_${sym}[] = {
800''')
801    code.indent()
802    step = 16
803    for i in xrange(0, len(data), step):
804        x = array.array('B', data[i:i+step])
805        code(''.join('%d,' % d for d in x))
806    code.dedent()
807    
808    code('''};
809
810EmbeddedPython embedded_${sym}(
811    ${{c_str(pysource.arcname)}},
812    ${{c_str(pysource.abspath)}},
813    ${{c_str(pysource.modpath)}},
814    data_${sym},
815    ${{len(data)}},
816    ${{len(marshalled)}});
817
818} // anonymous namespace
819''')
820    code.write(str(target[0]))
821
822for source in PySource.all:
823    env.Command(source.cpp, source.tnode, 
824                MakeAction(embedPyFile, Transform("EMBED PY")))
825    Source(source.cpp)
826
827########################################################################
828#
829# Define binaries.  Each different build type (debug, opt, etc.) gets
830# a slightly different build environment.
831#
832
833# List of constructed environments to pass back to SConstruct
834envList = []
835
836date_source = Source('base/date.cc', skip_lib=True)
837
838# Function to create a new build environment as clone of current
839# environment 'env' with modified object suffix and optional stripped
840# binary.  Additional keyword arguments are appended to corresponding
841# build environment vars.
842def makeEnv(label, objsfx, strip = False, **kwargs):
843    # SCons doesn't know to append a library suffix when there is a '.' in the
844    # name.  Use '_' instead.
845    libname = 'gem5_' + label
846    exename = 'gem5.' + label
847    secondary_exename = 'm5.' + label
848
849    new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
850    new_env.Label = label
851    new_env.Append(**kwargs)
852
853    swig_env = new_env.Clone()
854    swig_env.Append(CCFLAGS='-Werror')
855    if env['GCC']:
856        swig_env.Append(CCFLAGS=['-Wno-uninitialized', '-Wno-sign-compare',
857                                 '-Wno-parentheses', '-Wno-unused-label',
858                                 '-Wno-unused-value'])
859        if compareVersions(env['GCC_VERSION'], '4.6') >= 0:
860            swig_env.Append(CCFLAGS='-Wno-unused-but-set-variable')
861    if env['CLANG']:
862        swig_env.Append(CCFLAGS=['-Wno-unused-label', '-Wno-unused-value'])
863
864    werror_env = new_env.Clone()
865    werror_env.Append(CCFLAGS='-Werror')
866
867    def make_obj(source, static, extra_deps = None):
868        '''This function adds the specified source to the correct
869        build environment, and returns the corresponding SCons Object
870        nodes'''
871
872        if source.swig:
873            env = swig_env
874        elif source.Werror:
875            env = werror_env
876        else:
877            env = new_env
878
879        if static:
880            obj = env.StaticObject(source.tnode)
881        else:
882            obj = env.SharedObject(source.tnode)
883
884        if extra_deps:
885            env.Depends(obj, extra_deps)
886
887        return obj
888
889    static_objs = \
890        [ make_obj(s, True) for s in Source.get(main=False, skip_lib=False) ]
891    shared_objs = \
892        [ make_obj(s, False) for s in Source.get(main=False, skip_lib=False) ]
893
894    static_date = make_obj(date_source, static=True, extra_deps=static_objs)
895    static_objs.append(static_date)
896    
897    shared_date = make_obj(date_source, static=False, extra_deps=shared_objs)
898    shared_objs.append(shared_date)
899
900    # First make a library of everything but main() so other programs can
901    # link against m5.
902    static_lib = new_env.StaticLibrary(libname, static_objs)
903    shared_lib = new_env.SharedLibrary(libname, shared_objs)
904
905    # Now link a stub with main() and the static library.
906    main_objs = [ make_obj(s, True) for s in Source.get(main=True) ]
907
908    for test in UnitTest.all:
909        flags = { test.target : True }
910        test_sources = Source.get(**flags)
911        test_objs = [ make_obj(s, static=True) for s in test_sources ]
912        if test.main:
913            test_objs += main_objs
914        testname = "unittest/%s.%s" % (test.target, label)
915        new_env.Program(testname, test_objs + static_objs)
916
917    progname = exename
918    if strip:
919        progname += '.unstripped'
920
921    targets = new_env.Program(progname, main_objs + static_objs)
922
923    if strip:
924        if sys.platform == 'sunos5':
925            cmd = 'cp $SOURCE $TARGET; strip $TARGET'
926        else:
927            cmd = 'strip $SOURCE -o $TARGET'
928        targets = new_env.Command(exename, progname,
929                    MakeAction(cmd, Transform("STRIP")))
930
931    new_env.Command(secondary_exename, exename,
932            MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK")))
933
934    new_env.M5Binary = targets[0]
935    envList.append(new_env)
936
937# Start out with the compiler flags common to all compilers,
938# i.e. they all use -g for opt and -g -pg for prof
939ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'],
940           'perf' : ['-g']}
941
942# Start out with the linker flags common to all linkers, i.e. -pg for
943# prof, and -lprofiler for perf. The -lprofile flag is surrounded by
944# no-as-needed and as-needed as the binutils linker is too clever and
945# simply doesn't link to the library otherwise.
946ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'],
947           'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']}
948
949# For Link Time Optimization, the optimisation flags used to compile
950# individual files are decoupled from those used at link time
951# (i.e. you can compile with -O3 and perform LTO with -O0), so we need
952# to also update the linker flags based on the target.
953if env['GCC']:
954    if sys.platform == 'sunos5':
955        ccflags['debug'] += ['-gstabs+']
956    else:
957        ccflags['debug'] += ['-ggdb3']
958    ldflags['debug'] += ['-O0']
959    # opt, fast, prof and perf all share the same cc flags, also add
960    # the optimization to the ldflags as LTO defers the optimization
961    # to link time
962    for target in ['opt', 'fast', 'prof', 'perf']:
963        ccflags[target] += ['-O3']
964        ldflags[target] += ['-O3']
965
966    ccflags['fast'] += env['LTO_CCFLAGS']
967    ldflags['fast'] += env['LTO_LDFLAGS']
968
969elif env['SUNCC']:
970    ccflags['debug'] += ['-g0']
971    ccflags['opt'] += ['-O']
972    for target in ['fast', 'prof', 'perf']:
973        ccflags[target] += ['-fast']
974elif env['ICC']:
975    ccflags['debug'] += ['-g', '-O0']
976    ccflags['opt'] += ['-O']
977    for target in ['fast', 'prof', 'perf']:
978        ccflags[target] += ['-fast']
979elif env['CLANG']:
980    ccflags['debug'] += ['-g', '-O0']
981    # opt, fast, prof and perf all share the same cc flags
982    for target in ['opt', 'fast', 'prof', 'perf']:
983        ccflags[target] += ['-O3']
984else:
985    print 'Unknown compiler, please fix compiler options'
986    Exit(1)
987
988
989# To speed things up, we only instantiate the build environments we
990# need.  We try to identify the needed environment for each target; if
991# we can't, we fall back on instantiating all the environments just to
992# be safe.
993target_types = ['debug', 'opt', 'fast', 'prof', 'perf']
994obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof',
995              'gpo' : 'perf'}
996
997def identifyTarget(t):
998    ext = t.split('.')[-1]
999    if ext in target_types:
1000        return ext
1001    if obj2target.has_key(ext):
1002        return obj2target[ext]
1003    match = re.search(r'/tests/([^/]+)/', t)
1004    if match and match.group(1) in target_types:
1005        return match.group(1)
1006    return 'all'
1007
1008needed_envs = [identifyTarget(target) for target in BUILD_TARGETS]
1009if 'all' in needed_envs:
1010    needed_envs += target_types
1011
1012# Debug binary
1013if 'debug' in needed_envs:
1014    makeEnv('debug', '.do',
1015            CCFLAGS = Split(ccflags['debug']),
1016            CPPDEFINES = ['DEBUG', 'TRACING_ON=1'],
1017            LINKFLAGS = Split(ldflags['debug']))
1018
1019# Optimized binary
1020if 'opt' in needed_envs:
1021    makeEnv('opt', '.o',
1022            CCFLAGS = Split(ccflags['opt']),
1023            CPPDEFINES = ['TRACING_ON=1'],
1024            LINKFLAGS = Split(ldflags['opt']))
1025
1026# "Fast" binary
1027if 'fast' in needed_envs:
1028    makeEnv('fast', '.fo', strip = True,
1029            CCFLAGS = Split(ccflags['fast']),
1030            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1031            LINKFLAGS = Split(ldflags['fast']))
1032
1033# Profiled binary using gprof
1034if 'prof' in needed_envs:
1035    makeEnv('prof', '.po',
1036            CCFLAGS = Split(ccflags['prof']),
1037            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1038            LINKFLAGS = Split(ldflags['prof']))
1039
1040# Profiled binary using google-pprof
1041if 'perf' in needed_envs:
1042    makeEnv('perf', '.gpo',
1043            CCFLAGS = Split(ccflags['perf']),
1044            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1045            LINKFLAGS = Split(ldflags['perf']))
1046
1047Return('envList')
1048