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