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