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