SConscript revision 8242:f52ece27e20d
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 M5
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
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 m5 main() function
65#     skip_lib -- do not put this file into the m5 library
66#     <unittest> -- unit tests use filters based on the unit test name
67#
68# A parent can now be specified for a source file and default filter
69# values will be retrieved recursively from parents (children override
70# parents).
71#
72class SourceMeta(type):
73    '''Meta class for source files that keeps track of all files of a
74    particular type and has a get function for finding all functions
75    of a certain type that match a set of guards'''
76    def __init__(cls, name, bases, dict):
77        super(SourceMeta, cls).__init__(name, bases, dict)
78        cls.all = []
79        
80    def get(cls, **guards):
81        '''Find all files that match the specified guards.  If a source
82        file does not specify a flag, the default is False'''
83        for src in cls.all:
84            for flag,value in guards.iteritems():
85                # if the flag is found and has a different value, skip
86                # this file
87                if src.all_guards.get(flag, False) != value:
88                    break
89            else:
90                yield src
91
92class SourceFile(object):
93    '''Base object that encapsulates the notion of a source file.
94    This includes, the source node, target node, various manipulations
95    of those.  A source file also specifies a set of guards which
96    describing which builds the source file applies to.  A parent can
97    also be specified to get default guards from'''
98    __metaclass__ = SourceMeta
99    def __init__(self, source, parent=None, **guards):
100        self.guards = guards
101        self.parent = parent
102
103        tnode = source
104        if not isinstance(source, SCons.Node.FS.File):
105            tnode = File(source)
106
107        self.tnode = tnode
108        self.snode = tnode.srcnode()
109
110        for base in type(self).__mro__:
111            if issubclass(base, SourceFile):
112                base.all.append(self)
113
114    @property
115    def filename(self):
116        return str(self.tnode)
117
118    @property
119    def dirname(self):
120        return dirname(self.filename)
121
122    @property
123    def basename(self):
124        return basename(self.filename)
125
126    @property
127    def extname(self):
128        index = self.basename.rfind('.')
129        if index <= 0:
130            # dot files aren't extensions
131            return self.basename, None
132
133        return self.basename[:index], self.basename[index+1:]
134
135    @property
136    def all_guards(self):
137        '''find all guards for this object getting default values
138        recursively from its parents'''
139        guards = {}
140        if self.parent:
141            guards.update(self.parent.guards)
142        guards.update(self.guards)
143        return guards
144
145    def __lt__(self, other): return self.filename < other.filename
146    def __le__(self, other): return self.filename <= other.filename
147    def __gt__(self, other): return self.filename > other.filename
148    def __ge__(self, other): return self.filename >= other.filename
149    def __eq__(self, other): return self.filename == other.filename
150    def __ne__(self, other): return self.filename != other.filename
151        
152class Source(SourceFile):
153    '''Add a c/c++ source file to the build'''
154    def __init__(self, source, Werror=True, swig=False, **guards):
155        '''specify the source file, and any guards'''
156        super(Source, self).__init__(source, **guards)
157
158        self.Werror = Werror
159        self.swig = swig
160
161class PySource(SourceFile):
162    '''Add a python source file to the named package'''
163    invalid_sym_char = re.compile('[^A-z0-9_]')
164    modules = {}
165    tnodes = {}
166    symnames = {}
167    
168    def __init__(self, package, source, **guards):
169        '''specify the python package, the source file, and any guards'''
170        super(PySource, self).__init__(source, **guards)
171
172        modname,ext = self.extname
173        assert ext == 'py'
174
175        if package:
176            path = package.split('.')
177        else:
178            path = []
179
180        modpath = path[:]
181        if modname != '__init__':
182            modpath += [ modname ]
183        modpath = '.'.join(modpath)
184
185        arcpath = path + [ self.basename ]
186        abspath = self.snode.abspath
187        if not exists(abspath):
188            abspath = self.tnode.abspath
189
190        self.package = package
191        self.modname = modname
192        self.modpath = modpath
193        self.arcname = joinpath(*arcpath)
194        self.abspath = abspath
195        self.compiled = File(self.filename + 'c')
196        self.cpp = File(self.filename + '.cc')
197        self.symname = PySource.invalid_sym_char.sub('_', modpath)
198
199        PySource.modules[modpath] = self
200        PySource.tnodes[self.tnode] = self
201        PySource.symnames[self.symname] = self
202
203class SimObject(PySource):
204    '''Add a SimObject python file as a python source object and add
205    it to a list of sim object modules'''
206
207    fixed = False
208    modnames = []
209
210    def __init__(self, source, **guards):
211        '''Specify the source file and any guards (automatically in
212        the m5.objects package)'''
213        super(SimObject, self).__init__('m5.objects', source, **guards)
214        if self.fixed:
215            raise AttributeError, "Too late to call SimObject now."
216
217        bisect.insort_right(SimObject.modnames, self.modname)
218
219class SwigSource(SourceFile):
220    '''Add a swig file to build'''
221
222    def __init__(self, package, source, **guards):
223        '''Specify the python package, the source file, and any guards'''
224        super(SwigSource, self).__init__(source, **guards)
225
226        modname,ext = self.extname
227        assert ext == 'i'
228
229        self.module = modname
230        cc_file = joinpath(self.dirname, modname + '_wrap.cc')
231        py_file = joinpath(self.dirname, modname + '.py')
232
233        self.cc_source = Source(cc_file, swig=True, parent=self)
234        self.py_source = PySource(package, py_file, parent=self)
235
236class UnitTest(object):
237    '''Create a UnitTest'''
238
239    all = []
240    def __init__(self, target, *sources):
241        '''Specify the target name and any sources.  Sources that are
242        not SourceFiles are evalued with Source().  All files are
243        guarded with a guard of the same name as the UnitTest
244        target.'''
245
246        srcs = []
247        for src in sources:
248            if not isinstance(src, SourceFile):
249                src = Source(src, skip_lib=True)
250            src.guards[target] = True
251            srcs.append(src)
252
253        self.sources = srcs
254        self.target = target
255        UnitTest.all.append(self)
256
257# Children should have access
258Export('Source')
259Export('PySource')
260Export('SimObject')
261Export('SwigSource')
262Export('UnitTest')
263
264########################################################################
265#
266# Debug Flags
267#
268debug_flags = {}
269def DebugFlag(name, desc=None):
270    if name in debug_flags:
271        raise AttributeError, "Flag %s already specified" % name
272    debug_flags[name] = (name, (), desc)
273TraceFlag = DebugFlag
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('TraceFlag')
284Export('CompoundFlag')
285
286########################################################################
287#
288# Set some compiler variables
289#
290
291# Include file paths are rooted in this directory.  SCons will
292# automatically expand '.' to refer to both the source directory and
293# the corresponding build directory to pick up generated include
294# files.
295env.Append(CPPPATH=Dir('.'))
296
297for extra_dir in extras_dir_list:
298    env.Append(CPPPATH=Dir(extra_dir))
299
300# Workaround for bug in SCons version > 0.97d20071212
301# Scons bug id: 2006 M5 Bug id: 308 
302for root, dirs, files in os.walk(base_dir, topdown=True):
303    Dir(root[len(base_dir) + 1:])
304
305########################################################################
306#
307# Walk the tree and execute all SConscripts in subdirectories
308#
309
310here = Dir('.').srcnode().abspath
311for root, dirs, files in os.walk(base_dir, topdown=True):
312    if root == here:
313        # we don't want to recurse back into this SConscript
314        continue
315
316    if 'SConscript' in files:
317        build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
318        SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
319
320for extra_dir in extras_dir_list:
321    prefix_len = len(dirname(extra_dir)) + 1
322    for root, dirs, files in os.walk(extra_dir, topdown=True):
323        # if build lives in the extras directory, don't walk down it
324        if 'build' in dirs:
325            dirs.remove('build')
326
327        if 'SConscript' in files:
328            build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
329            SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
330
331for opt in export_vars:
332    env.ConfigFile(opt)
333
334def makeTheISA(source, target, env):
335    isas = [ src.get_contents() for src in source ]
336    target_isa = env['TARGET_ISA']
337    def define(isa):
338        return isa.upper() + '_ISA'
339    
340    def namespace(isa):
341        return isa[0].upper() + isa[1:].lower() + 'ISA' 
342
343
344    code = code_formatter()
345    code('''\
346#ifndef __CONFIG_THE_ISA_HH__
347#define __CONFIG_THE_ISA_HH__
348
349''')
350
351    for i,isa in enumerate(isas):
352        code('#define $0 $1', define(isa), i + 1)
353
354    code('''
355
356#define THE_ISA ${{define(target_isa)}}
357#define TheISA ${{namespace(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
454all_params = {}
455for name,obj in sorted(sim_objects.iteritems()):
456    for param in obj._params.local.values():
457        # load the ptype attribute now because it depends on the
458        # current version of SimObject.allClasses, but when scons
459        # actually uses the value, all versions of
460        # SimObject.allClasses will have been loaded
461        param.ptype
462
463        if not hasattr(param, 'swig_decl'):
464            continue
465        pname = param.ptype_str
466        if pname not in all_params:
467            all_params[pname] = param
468
469########################################################################
470#
471# calculate extra dependencies
472#
473module_depends = ["m5", "m5.SimObject", "m5.params"]
474depends = [ PySource.modules[dep].snode for dep in module_depends ]
475
476########################################################################
477#
478# Commands for the basic automatically generated python files
479#
480
481# Generate Python file containing a dict specifying the current
482# buildEnv flags.
483def makeDefinesPyFile(target, source, env):
484    build_env = source[0].get_contents()
485
486    code = code_formatter()
487    code("""
488import m5.internal
489import m5.util
490
491buildEnv = m5.util.SmartDict($build_env)
492
493compileDate = m5.internal.core.compileDate
494_globals = globals()
495for key,val in m5.internal.core.__dict__.iteritems():
496    if key.startswith('flag_'):
497        flag = key[5:]
498        _globals[flag] = val
499del _globals
500""")
501    code.write(target[0].abspath)
502
503defines_info = Value(build_env)
504# Generate a file with all of the compile options in it
505env.Command('python/m5/defines.py', defines_info,
506            MakeAction(makeDefinesPyFile, Transform("DEFINES", 0)))
507PySource('m5', 'python/m5/defines.py')
508
509# Generate python file containing info about the M5 source code
510def makeInfoPyFile(target, source, env):
511    code = code_formatter()
512    for src in source:
513        data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
514        code('$src = ${{repr(data)}}')
515    code.write(str(target[0]))
516
517# Generate a file that wraps the basic top level files
518env.Command('python/m5/info.py',
519            [ '#/AUTHORS', '#/LICENSE', '#/README', ],
520            MakeAction(makeInfoPyFile, Transform("INFO")))
521PySource('m5', 'python/m5/info.py')
522
523########################################################################
524#
525# Create all of the SimObject param headers and enum headers
526#
527
528def createSimObjectParam(target, source, env):
529    assert len(target) == 1 and len(source) == 1
530
531    name = str(source[0].get_contents())
532    obj = sim_objects[name]
533
534    code = code_formatter()
535    obj.cxx_decl(code)
536    code.write(target[0].abspath)
537
538def createSwigParam(target, source, env):
539    assert len(target) == 1 and len(source) == 1
540
541    name = str(source[0].get_contents())
542    param = all_params[name]
543
544    code = code_formatter()
545    code('%module(package="m5.internal") $0_${name}', param.file_ext)
546    param.swig_decl(code)
547    code.write(target[0].abspath)
548
549def createEnumStrings(target, source, env):
550    assert len(target) == 1 and len(source) == 1
551
552    name = str(source[0].get_contents())
553    obj = all_enums[name]
554
555    code = code_formatter()
556    obj.cxx_def(code)
557    code.write(target[0].abspath)
558
559def createEnumParam(target, source, env):
560    assert len(target) == 1 and len(source) == 1
561
562    name = str(source[0].get_contents())
563    obj = all_enums[name]
564
565    code = code_formatter()
566    obj.cxx_decl(code)
567    code.write(target[0].abspath)
568
569def createEnumSwig(target, source, env):
570    assert len(target) == 1 and len(source) == 1
571
572    name = str(source[0].get_contents())
573    obj = all_enums[name]
574
575    code = code_formatter()
576    code('''\
577%module(package="m5.internal") enum_$name
578
579%{
580#include "enums/$name.hh"
581%}
582
583%include "enums/$name.hh"
584''')
585    code.write(target[0].abspath)
586
587# Generate all of the SimObject param struct header files
588params_hh_files = []
589for name,simobj in sorted(sim_objects.iteritems()):
590    py_source = PySource.modules[simobj.__module__]
591    extra_deps = [ py_source.tnode ]
592
593    hh_file = File('params/%s.hh' % name)
594    params_hh_files.append(hh_file)
595    env.Command(hh_file, Value(name),
596                MakeAction(createSimObjectParam, Transform("SO PARAM")))
597    env.Depends(hh_file, depends + extra_deps)
598
599# Generate any parameter header files needed
600params_i_files = []
601for name,param in all_params.iteritems():
602    i_file = File('python/m5/internal/%s_%s.i' % (param.file_ext, name))
603    params_i_files.append(i_file)
604    env.Command(i_file, Value(name),
605                MakeAction(createSwigParam, Transform("SW PARAM")))
606    env.Depends(i_file, depends)
607    SwigSource('m5.internal', i_file)
608
609# Generate all enum header files
610for name,enum in sorted(all_enums.iteritems()):
611    py_source = PySource.modules[enum.__module__]
612    extra_deps = [ py_source.tnode ]
613
614    cc_file = File('enums/%s.cc' % name)
615    env.Command(cc_file, Value(name),
616                MakeAction(createEnumStrings, Transform("ENUM STR")))
617    env.Depends(cc_file, depends + extra_deps)
618    Source(cc_file)
619
620    hh_file = File('enums/%s.hh' % name)
621    env.Command(hh_file, Value(name),
622                MakeAction(createEnumParam, Transform("EN PARAM")))
623    env.Depends(hh_file, depends + extra_deps)
624
625    i_file = File('python/m5/internal/enum_%s.i' % name)
626    env.Command(i_file, Value(name),
627                MakeAction(createEnumSwig, Transform("ENUMSWIG")))
628    env.Depends(i_file, depends + extra_deps)
629    SwigSource('m5.internal', i_file)
630
631def buildParam(target, source, env):
632    name = source[0].get_contents()
633    obj = sim_objects[name]
634    class_path = obj.cxx_class.split('::')
635    classname = class_path[-1]
636    namespaces = class_path[:-1]
637    params = obj._params.local.values()
638
639    code = code_formatter()
640
641    code('%module(package="m5.internal") param_$name')
642    code()
643    code('%{')
644    code('#include "params/$obj.hh"')
645    for param in params:
646        param.cxx_predecls(code)
647    code('%}')
648    code()
649
650    for param in params:
651        param.swig_predecls(code)
652
653    code()
654    if obj._base:
655        code('%import "python/m5/internal/param_${{obj._base}}.i"')
656    code()
657    obj.swig_objdecls(code)
658    code()
659
660    code('%include "params/$obj.hh"')
661
662    code.write(target[0].abspath)
663
664for name in sim_objects.iterkeys():
665    params_file = File('python/m5/internal/param_%s.i' % name)
666    env.Command(params_file, Value(name),
667                MakeAction(buildParam, Transform("BLDPARAM")))
668    env.Depends(params_file, depends)
669    SwigSource('m5.internal', params_file)
670
671# Generate the main swig init file
672def makeEmbeddedSwigInit(target, source, env):
673    code = code_formatter()
674    module = source[0].get_contents()
675    code('''\
676#include "sim/init.hh"
677
678extern "C" {
679    void init_${module}();
680}
681
682EmbeddedSwig embed_swig_${module}(init_${module});
683''')
684    code.write(str(target[0]))
685    
686# Build all swig modules
687for swig in SwigSource.all:
688    env.Command([swig.cc_source.tnode, swig.py_source.tnode], swig.tnode,
689                MakeAction('$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} '
690                '-o ${TARGETS[0]} $SOURCES', Transform("SWIG")))
691    cc_file = str(swig.tnode)
692    init_file = '%s/init_%s.cc' % (dirname(cc_file), basename(cc_file))
693    env.Command(init_file, Value(swig.module),
694                MakeAction(makeEmbeddedSwigInit, Transform("EMBED SW")))
695    Source(init_file, **swig.guards)
696
697#
698# Handle debug flags
699#
700def makeDebugFlagCC(target, source, env):
701    assert(len(target) == 1 and len(source) == 1)
702
703    val = eval(source[0].get_contents())
704    name, compound, desc = val
705    compound = list(sorted(compound))
706
707    code = code_formatter()
708
709    # file header
710    code('''
711/*
712 * DO NOT EDIT THIS FILE! Automatically generated
713 */
714
715#include "base/debug.hh"
716''')
717
718    for flag in compound:
719        code('#include "debug/$flag.hh"')
720    code()
721    code('namespace Debug {')
722    code()
723
724    if not compound:
725        code('SimpleFlag $name("$name", "$desc");')
726    else:
727        code('CompoundFlag $name("$name", "$desc",')
728        code.indent()
729        last = len(compound) - 1
730        for i,flag in enumerate(compound):
731            if i != last:
732                code('$flag,')
733            else:
734                code('$flag);')
735        code.dedent()
736
737    code()
738    code('} // namespace Debug')
739
740    code.write(str(target[0]))
741
742def makeDebugFlagHH(target, source, env):
743    assert(len(target) == 1 and len(source) == 1)
744
745    val = eval(source[0].get_contents())
746    name, compound, desc = val
747
748    code = code_formatter()
749
750    # file header boilerplate
751    code('''\
752/*
753 * DO NOT EDIT THIS FILE!
754 *
755 * Automatically generated by SCons
756 */
757
758#ifndef __DEBUG_${name}_HH__
759#define __DEBUG_${name}_HH__
760
761namespace Debug {
762''')
763
764    if compound:
765        code('class CompoundFlag;')
766    code('class SimpleFlag;')
767
768    if compound:
769        code('extern CompoundFlag $name;')
770        for flag in compound:
771            code('extern SimpleFlag $flag;')
772    else:
773        code('extern SimpleFlag $name;')
774
775    code('''
776}
777
778#endif // __DEBUG_${name}_HH__
779''')
780
781    code.write(str(target[0]))
782
783for name,flag in sorted(debug_flags.iteritems()):
784    n, compound, desc = flag
785    assert n == name
786
787    env.Command('debug/%s.hh' % name, Value(flag),
788                MakeAction(makeDebugFlagHH, Transform("TRACING", 0)))
789    env.Command('debug/%s.cc' % name, Value(flag),
790                MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
791    Source('debug/%s.cc' % name)
792
793# Embed python files.  All .py files that have been indicated by a
794# PySource() call in a SConscript need to be embedded into the M5
795# library.  To do that, we compile the file to byte code, marshal the
796# byte code, compress it, and then generate a c++ file that
797# inserts the result into an array.
798def embedPyFile(target, source, env):
799    def c_str(string):
800        if string is None:
801            return "0"
802        return '"%s"' % string
803
804    '''Action function to compile a .py into a code object, marshal
805    it, compress it, and stick it into an asm file so the code appears
806    as just bytes with a label in the data section'''
807
808    src = file(str(source[0]), 'r').read()
809
810    pysource = PySource.tnodes[source[0]]
811    compiled = compile(src, pysource.abspath, 'exec')
812    marshalled = marshal.dumps(compiled)
813    compressed = zlib.compress(marshalled)
814    data = compressed
815    sym = pysource.symname
816
817    code = code_formatter()
818    code('''\
819#include "sim/init.hh"
820
821namespace {
822
823const char data_${sym}[] = {
824''')
825    code.indent()
826    step = 16
827    for i in xrange(0, len(data), step):
828        x = array.array('B', data[i:i+step])
829        code(''.join('%d,' % d for d in x))
830    code.dedent()
831    
832    code('''};
833
834EmbeddedPython embedded_${sym}(
835    ${{c_str(pysource.arcname)}},
836    ${{c_str(pysource.abspath)}},
837    ${{c_str(pysource.modpath)}},
838    data_${sym},
839    ${{len(data)}},
840    ${{len(marshalled)}});
841
842} // anonymous namespace
843''')
844    code.write(str(target[0]))
845
846for source in PySource.all:
847    env.Command(source.cpp, source.tnode, 
848                MakeAction(embedPyFile, Transform("EMBED PY")))
849    Source(source.cpp)
850
851########################################################################
852#
853# Define binaries.  Each different build type (debug, opt, etc.) gets
854# a slightly different build environment.
855#
856
857# List of constructed environments to pass back to SConstruct
858envList = []
859
860date_source = Source('base/date.cc', skip_lib=True)
861
862# Function to create a new build environment as clone of current
863# environment 'env' with modified object suffix and optional stripped
864# binary.  Additional keyword arguments are appended to corresponding
865# build environment vars.
866def makeEnv(label, objsfx, strip = False, **kwargs):
867    # SCons doesn't know to append a library suffix when there is a '.' in the
868    # name.  Use '_' instead.
869    libname = 'm5_' + label
870    exename = 'm5.' + label
871
872    new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
873    new_env.Label = label
874    new_env.Append(**kwargs)
875
876    swig_env = new_env.Clone()
877    swig_env.Append(CCFLAGS='-Werror')
878    if env['GCC']:
879        swig_env.Append(CCFLAGS='-Wno-uninitialized')
880        swig_env.Append(CCFLAGS='-Wno-sign-compare')
881        swig_env.Append(CCFLAGS='-Wno-parentheses')
882
883    werror_env = new_env.Clone()
884    werror_env.Append(CCFLAGS='-Werror')
885
886    def make_obj(source, static, extra_deps = None):
887        '''This function adds the specified source to the correct
888        build environment, and returns the corresponding SCons Object
889        nodes'''
890
891        if source.swig:
892            env = swig_env
893        elif source.Werror:
894            env = werror_env
895        else:
896            env = new_env
897
898        if static:
899            obj = env.StaticObject(source.tnode)
900        else:
901            obj = env.SharedObject(source.tnode)
902
903        if extra_deps:
904            env.Depends(obj, extra_deps)
905
906        return obj
907
908    sources = Source.get(main=False, skip_lib=False)
909    static_objs = [ make_obj(s, True) for s in sources ]
910    shared_objs = [ make_obj(s, False) for s in sources ]
911
912    static_date = make_obj(date_source, static=True, extra_deps=static_objs)
913    static_objs.append(static_date)
914    
915    shared_date = make_obj(date_source, static=False, extra_deps=shared_objs)
916    shared_objs.append(shared_date)
917
918    # First make a library of everything but main() so other programs can
919    # link against m5.
920    static_lib = new_env.StaticLibrary(libname, static_objs)
921    shared_lib = new_env.SharedLibrary(libname, shared_objs)
922
923    # Now link a stub with main() and the static library.
924    main_objs = [ make_obj(s, True) for s in Source.get(main=True) ]
925
926    for test in UnitTest.all:
927        flags = { test.target : True }
928        test_sources = Source.get(**flags)
929        test_objs = [ make_obj(s, static=True) for s in test_sources ]
930        testname = "unittest/%s.%s" % (test.target, label)
931        new_env.Program(testname, main_objs + test_objs + static_objs)
932
933    progname = exename
934    if strip:
935        progname += '.unstripped'
936
937    targets = new_env.Program(progname, main_objs + static_objs)
938
939    if strip:
940        if sys.platform == 'sunos5':
941            cmd = 'cp $SOURCE $TARGET; strip $TARGET'
942        else:
943            cmd = 'strip $SOURCE -o $TARGET'
944        targets = new_env.Command(exename, progname,
945                    MakeAction(cmd, Transform("STRIP")))
946            
947    new_env.M5Binary = targets[0]
948    envList.append(new_env)
949
950# Debug binary
951ccflags = {}
952if env['GCC']:
953    if sys.platform == 'sunos5':
954        ccflags['debug'] = '-gstabs+'
955    else:
956        ccflags['debug'] = '-ggdb3'
957    ccflags['opt'] = '-g -O3'
958    ccflags['fast'] = '-O3'
959    ccflags['prof'] = '-O3 -g -pg'
960elif env['SUNCC']:
961    ccflags['debug'] = '-g0'
962    ccflags['opt'] = '-g -O'
963    ccflags['fast'] = '-fast'
964    ccflags['prof'] = '-fast -g -pg'
965elif env['ICC']:
966    ccflags['debug'] = '-g -O0'
967    ccflags['opt'] = '-g -O'
968    ccflags['fast'] = '-fast'
969    ccflags['prof'] = '-fast -g -pg'
970else:
971    print 'Unknown compiler, please fix compiler options'
972    Exit(1)
973
974makeEnv('debug', '.do',
975        CCFLAGS = Split(ccflags['debug']),
976        CPPDEFINES = ['DEBUG', 'TRACING_ON=1'])
977
978# Optimized binary
979makeEnv('opt', '.o',
980        CCFLAGS = Split(ccflags['opt']),
981        CPPDEFINES = ['TRACING_ON=1'])
982
983# "Fast" binary
984makeEnv('fast', '.fo', strip = True,
985        CCFLAGS = Split(ccflags['fast']),
986        CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'])
987
988# Profiled binary
989makeEnv('prof', '.po',
990        CCFLAGS = Split(ccflags['prof']),
991        CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
992        LINKFLAGS = '-pg')
993
994Return('envList')
995