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