SConscript revision 9958
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 ProtoBuf(SourceFile):
237    '''Add a Protocol Buffer to build'''
238
239    def __init__(self, source, **guards):
240        '''Specify the source file, and any guards'''
241        super(ProtoBuf, self).__init__(source, **guards)
242
243        # Get the file name and the extension
244        modname,ext = self.extname
245        assert ext == 'proto'
246
247        # Currently, we stick to generating the C++ headers, so we
248        # only need to track the source and header.
249        self.cc_file = File(modname + '.pb.cc')
250        self.hh_file = File(modname + '.pb.h')
251
252class UnitTest(object):
253    '''Create a UnitTest'''
254
255    all = []
256    def __init__(self, target, *sources, **kwargs):
257        '''Specify the target name and any sources.  Sources that are
258        not SourceFiles are evalued with Source().  All files are
259        guarded with a guard of the same name as the UnitTest
260        target.'''
261
262        srcs = []
263        for src in sources:
264            if not isinstance(src, SourceFile):
265                src = Source(src, skip_lib=True)
266            src.guards[target] = True
267            srcs.append(src)
268
269        self.sources = srcs
270        self.target = target
271        self.main = kwargs.get('main', False)
272        UnitTest.all.append(self)
273
274# Children should have access
275Export('Source')
276Export('PySource')
277Export('SimObject')
278Export('SwigSource')
279Export('ProtoBuf')
280Export('UnitTest')
281
282########################################################################
283#
284# Debug Flags
285#
286debug_flags = {}
287def DebugFlag(name, desc=None):
288    if name in debug_flags:
289        raise AttributeError, "Flag %s already specified" % name
290    debug_flags[name] = (name, (), desc)
291
292def CompoundFlag(name, flags, desc=None):
293    if name in debug_flags:
294        raise AttributeError, "Flag %s already specified" % name
295
296    compound = tuple(flags)
297    debug_flags[name] = (name, compound, desc)
298
299Export('DebugFlag')
300Export('CompoundFlag')
301
302########################################################################
303#
304# Set some compiler variables
305#
306
307# Include file paths are rooted in this directory.  SCons will
308# automatically expand '.' to refer to both the source directory and
309# the corresponding build directory to pick up generated include
310# files.
311env.Append(CPPPATH=Dir('.'))
312
313for extra_dir in extras_dir_list:
314    env.Append(CPPPATH=Dir(extra_dir))
315
316# Workaround for bug in SCons version > 0.97d20071212
317# Scons bug id: 2006 gem5 Bug id: 308
318for root, dirs, files in os.walk(base_dir, topdown=True):
319    Dir(root[len(base_dir) + 1:])
320
321########################################################################
322#
323# Walk the tree and execute all SConscripts in subdirectories
324#
325
326here = Dir('.').srcnode().abspath
327for root, dirs, files in os.walk(base_dir, topdown=True):
328    if root == here:
329        # we don't want to recurse back into this SConscript
330        continue
331
332    if 'SConscript' in files:
333        build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
334        SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
335
336for extra_dir in extras_dir_list:
337    prefix_len = len(dirname(extra_dir)) + 1
338
339    # Also add the corresponding build directory to pick up generated
340    # include files.
341    env.Append(CPPPATH=Dir(joinpath(env['BUILDDIR'], extra_dir[prefix_len:])))
342
343    for root, dirs, files in os.walk(extra_dir, topdown=True):
344        # if build lives in the extras directory, don't walk down it
345        if 'build' in dirs:
346            dirs.remove('build')
347
348        if 'SConscript' in files:
349            build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
350            SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
351
352for opt in export_vars:
353    env.ConfigFile(opt)
354
355def makeTheISA(source, target, env):
356    isas = [ src.get_contents() for src in source ]
357    target_isa = env['TARGET_ISA']
358    def define(isa):
359        return isa.upper() + '_ISA'
360    
361    def namespace(isa):
362        return isa[0].upper() + isa[1:].lower() + 'ISA' 
363
364
365    code = code_formatter()
366    code('''\
367#ifndef __CONFIG_THE_ISA_HH__
368#define __CONFIG_THE_ISA_HH__
369
370''')
371
372    for i,isa in enumerate(isas):
373        code('#define $0 $1', define(isa), i + 1)
374
375    code('''
376
377#define THE_ISA ${{define(target_isa)}}
378#define TheISA ${{namespace(target_isa)}}
379#define THE_ISA_STR "${{target_isa}}"
380
381#endif // __CONFIG_THE_ISA_HH__''')
382
383    code.write(str(target[0]))
384
385env.Command('config/the_isa.hh', map(Value, all_isa_list),
386            MakeAction(makeTheISA, Transform("CFG ISA", 0)))
387
388########################################################################
389#
390# Prevent any SimObjects from being added after this point, they
391# should all have been added in the SConscripts above
392#
393SimObject.fixed = True
394
395class DictImporter(object):
396    '''This importer takes a dictionary of arbitrary module names that
397    map to arbitrary filenames.'''
398    def __init__(self, modules):
399        self.modules = modules
400        self.installed = set()
401
402    def __del__(self):
403        self.unload()
404
405    def unload(self):
406        import sys
407        for module in self.installed:
408            del sys.modules[module]
409        self.installed = set()
410
411    def find_module(self, fullname, path):
412        if fullname == 'm5.defines':
413            return self
414
415        if fullname == 'm5.objects':
416            return self
417
418        if fullname.startswith('m5.internal'):
419            return None
420
421        source = self.modules.get(fullname, None)
422        if source is not None and fullname.startswith('m5.objects'):
423            return self
424
425        return None
426
427    def load_module(self, fullname):
428        mod = imp.new_module(fullname)
429        sys.modules[fullname] = mod
430        self.installed.add(fullname)
431
432        mod.__loader__ = self
433        if fullname == 'm5.objects':
434            mod.__path__ = fullname.split('.')
435            return mod
436
437        if fullname == 'm5.defines':
438            mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
439            return mod
440
441        source = self.modules[fullname]
442        if source.modname == '__init__':
443            mod.__path__ = source.modpath
444        mod.__file__ = source.abspath
445
446        exec file(source.abspath, 'r') in mod.__dict__
447
448        return mod
449
450import m5.SimObject
451import m5.params
452from m5.util import code_formatter
453
454m5.SimObject.clear()
455m5.params.clear()
456
457# install the python importer so we can grab stuff from the source
458# tree itself.  We can't have SimObjects added after this point or
459# else we won't know about them for the rest of the stuff.
460importer = DictImporter(PySource.modules)
461sys.meta_path[0:0] = [ importer ]
462
463# import all sim objects so we can populate the all_objects list
464# make sure that we're working with a list, then let's sort it
465for modname in SimObject.modnames:
466    exec('from m5.objects import %s' % modname)
467
468# we need to unload all of the currently imported modules so that they
469# will be re-imported the next time the sconscript is run
470importer.unload()
471sys.meta_path.remove(importer)
472
473sim_objects = m5.SimObject.allClasses
474all_enums = m5.params.allEnums
475
476if m5.SimObject.noCxxHeader:
477    print >> sys.stderr, \
478        "warning: At least one SimObject lacks a header specification. " \
479        "This can cause unexpected results in the generated SWIG " \
480        "wrappers."
481
482# Find param types that need to be explicitly wrapped with swig.
483# These will be recognized because the ParamDesc will have a
484# swig_decl() method.  Most param types are based on types that don't
485# need this, either because they're based on native types (like Int)
486# or because they're SimObjects (which get swigged independently).
487# For now the only things handled here are VectorParam types.
488params_to_swig = {}
489for name,obj in sorted(sim_objects.iteritems()):
490    for param in obj._params.local.values():
491        # load the ptype attribute now because it depends on the
492        # current version of SimObject.allClasses, but when scons
493        # actually uses the value, all versions of
494        # SimObject.allClasses will have been loaded
495        param.ptype
496
497        if not hasattr(param, 'swig_decl'):
498            continue
499        pname = param.ptype_str
500        if pname not in params_to_swig:
501            params_to_swig[pname] = param
502
503########################################################################
504#
505# calculate extra dependencies
506#
507module_depends = ["m5", "m5.SimObject", "m5.params"]
508depends = [ PySource.modules[dep].snode for dep in module_depends ]
509
510########################################################################
511#
512# Commands for the basic automatically generated python files
513#
514
515# Generate Python file containing a dict specifying the current
516# buildEnv flags.
517def makeDefinesPyFile(target, source, env):
518    build_env = source[0].get_contents()
519
520    code = code_formatter()
521    code("""
522import m5.internal
523import m5.util
524
525buildEnv = m5.util.SmartDict($build_env)
526
527compileDate = m5.internal.core.compileDate
528_globals = globals()
529for key,val in m5.internal.core.__dict__.iteritems():
530    if key.startswith('flag_'):
531        flag = key[5:]
532        _globals[flag] = val
533del _globals
534""")
535    code.write(target[0].abspath)
536
537defines_info = Value(build_env)
538# Generate a file with all of the compile options in it
539env.Command('python/m5/defines.py', defines_info,
540            MakeAction(makeDefinesPyFile, Transform("DEFINES", 0)))
541PySource('m5', 'python/m5/defines.py')
542
543# Generate python file containing info about the M5 source code
544def makeInfoPyFile(target, source, env):
545    code = code_formatter()
546    for src in source:
547        data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
548        code('$src = ${{repr(data)}}')
549    code.write(str(target[0]))
550
551# Generate a file that wraps the basic top level files
552env.Command('python/m5/info.py',
553            [ '#/COPYING', '#/LICENSE', '#/README', ],
554            MakeAction(makeInfoPyFile, Transform("INFO")))
555PySource('m5', 'python/m5/info.py')
556
557########################################################################
558#
559# Create all of the SimObject param headers and enum headers
560#
561
562def createSimObjectParamStruct(target, source, env):
563    assert len(target) == 1 and len(source) == 1
564
565    name = str(source[0].get_contents())
566    obj = sim_objects[name]
567
568    code = code_formatter()
569    obj.cxx_param_decl(code)
570    code.write(target[0].abspath)
571
572def createParamSwigWrapper(target, source, env):
573    assert len(target) == 1 and len(source) == 1
574
575    name = str(source[0].get_contents())
576    param = params_to_swig[name]
577
578    code = code_formatter()
579    param.swig_decl(code)
580    code.write(target[0].abspath)
581
582def createEnumStrings(target, source, env):
583    assert len(target) == 1 and len(source) == 1
584
585    name = str(source[0].get_contents())
586    obj = all_enums[name]
587
588    code = code_formatter()
589    obj.cxx_def(code)
590    code.write(target[0].abspath)
591
592def createEnumDecls(target, source, env):
593    assert len(target) == 1 and len(source) == 1
594
595    name = str(source[0].get_contents())
596    obj = all_enums[name]
597
598    code = code_formatter()
599    obj.cxx_decl(code)
600    code.write(target[0].abspath)
601
602def createEnumSwigWrapper(target, source, env):
603    assert len(target) == 1 and len(source) == 1
604
605    name = str(source[0].get_contents())
606    obj = all_enums[name]
607
608    code = code_formatter()
609    obj.swig_decl(code)
610    code.write(target[0].abspath)
611
612def createSimObjectSwigWrapper(target, source, env):
613    name = source[0].get_contents()
614    obj = sim_objects[name]
615
616    code = code_formatter()
617    obj.swig_decl(code)
618    code.write(target[0].abspath)
619
620# Generate all of the SimObject param C++ struct header files
621params_hh_files = []
622for name,simobj in sorted(sim_objects.iteritems()):
623    py_source = PySource.modules[simobj.__module__]
624    extra_deps = [ py_source.tnode ]
625
626    hh_file = File('params/%s.hh' % name)
627    params_hh_files.append(hh_file)
628    env.Command(hh_file, Value(name),
629                MakeAction(createSimObjectParamStruct, Transform("SO PARAM")))
630    env.Depends(hh_file, depends + extra_deps)
631
632# Generate any needed param SWIG wrapper files
633params_i_files = []
634for name,param in params_to_swig.iteritems():
635    i_file = File('python/m5/internal/%s.i' % (param.swig_module_name()))
636    params_i_files.append(i_file)
637    env.Command(i_file, Value(name),
638                MakeAction(createParamSwigWrapper, Transform("SW PARAM")))
639    env.Depends(i_file, depends)
640    SwigSource('m5.internal', i_file)
641
642# Generate all enum header files
643for name,enum in sorted(all_enums.iteritems()):
644    py_source = PySource.modules[enum.__module__]
645    extra_deps = [ py_source.tnode ]
646
647    cc_file = File('enums/%s.cc' % name)
648    env.Command(cc_file, Value(name),
649                MakeAction(createEnumStrings, Transform("ENUM STR")))
650    env.Depends(cc_file, depends + extra_deps)
651    Source(cc_file)
652
653    hh_file = File('enums/%s.hh' % name)
654    env.Command(hh_file, Value(name),
655                MakeAction(createEnumDecls, Transform("ENUMDECL")))
656    env.Depends(hh_file, depends + extra_deps)
657
658    i_file = File('python/m5/internal/enum_%s.i' % name)
659    env.Command(i_file, Value(name),
660                MakeAction(createEnumSwigWrapper, Transform("ENUMSWIG")))
661    env.Depends(i_file, depends + extra_deps)
662    SwigSource('m5.internal', i_file)
663
664# Generate SimObject SWIG wrapper files
665for name,simobj in sim_objects.iteritems():
666    py_source = PySource.modules[simobj.__module__]
667    extra_deps = [ py_source.tnode ]
668
669    i_file = File('python/m5/internal/param_%s.i' % name)
670    env.Command(i_file, Value(name),
671                MakeAction(createSimObjectSwigWrapper, Transform("SO SWIG")))
672    env.Depends(i_file, depends + extra_deps)
673    SwigSource('m5.internal', i_file)
674
675# Generate the main swig init file
676def makeEmbeddedSwigInit(target, source, env):
677    code = code_formatter()
678    module = source[0].get_contents()
679    code('''\
680#include "sim/init.hh"
681
682extern "C" {
683    void init_${module}();
684}
685
686EmbeddedSwig embed_swig_${module}(init_${module});
687''')
688    code.write(str(target[0]))
689    
690# Build all swig modules
691for swig in SwigSource.all:
692    env.Command([swig.cc_source.tnode, swig.py_source.tnode], swig.tnode,
693                MakeAction('$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} '
694                '-o ${TARGETS[0]} $SOURCES', Transform("SWIG")))
695    cc_file = str(swig.tnode)
696    init_file = '%s/%s_init.cc' % (dirname(cc_file), basename(cc_file))
697    env.Command(init_file, Value(swig.module),
698                MakeAction(makeEmbeddedSwigInit, Transform("EMBED SW")))
699    Source(init_file, **swig.guards)
700
701# Build all protocol buffers if we have got protoc and protobuf available
702if env['HAVE_PROTOBUF']:
703    for proto in ProtoBuf.all:
704        # Use both the source and header as the target, and the .proto
705        # file as the source. When executing the protoc compiler, also
706        # specify the proto_path to avoid having the generated files
707        # include the path.
708        env.Command([proto.cc_file, proto.hh_file], proto.tnode,
709                    MakeAction('$PROTOC --cpp_out ${TARGET.dir} '
710                               '--proto_path ${SOURCE.dir} $SOURCE',
711                               Transform("PROTOC")))
712
713        # Add the C++ source file
714        Source(proto.cc_file, **proto.guards)
715elif ProtoBuf.all:
716    print 'Got protobuf to build, but lacks support!'
717    Exit(1)
718
719#
720# Handle debug flags
721#
722def makeDebugFlagCC(target, source, env):
723    assert(len(target) == 1 and len(source) == 1)
724
725    val = eval(source[0].get_contents())
726    name, compound, desc = val
727    compound = list(sorted(compound))
728
729    code = code_formatter()
730
731    # file header
732    code('''
733/*
734 * DO NOT EDIT THIS FILE! Automatically generated
735 */
736
737#include "base/debug.hh"
738''')
739
740    for flag in compound:
741        code('#include "debug/$flag.hh"')
742    code()
743    code('namespace Debug {')
744    code()
745
746    if not compound:
747        code('SimpleFlag $name("$name", "$desc");')
748    else:
749        code('CompoundFlag $name("$name", "$desc",')
750        code.indent()
751        last = len(compound) - 1
752        for i,flag in enumerate(compound):
753            if i != last:
754                code('$flag,')
755            else:
756                code('$flag);')
757        code.dedent()
758
759    code()
760    code('} // namespace Debug')
761
762    code.write(str(target[0]))
763
764def makeDebugFlagHH(target, source, env):
765    assert(len(target) == 1 and len(source) == 1)
766
767    val = eval(source[0].get_contents())
768    name, compound, desc = val
769
770    code = code_formatter()
771
772    # file header boilerplate
773    code('''\
774/*
775 * DO NOT EDIT THIS FILE!
776 *
777 * Automatically generated by SCons
778 */
779
780#ifndef __DEBUG_${name}_HH__
781#define __DEBUG_${name}_HH__
782
783namespace Debug {
784''')
785
786    if compound:
787        code('class CompoundFlag;')
788    code('class SimpleFlag;')
789
790    if compound:
791        code('extern CompoundFlag $name;')
792        for flag in compound:
793            code('extern SimpleFlag $flag;')
794    else:
795        code('extern SimpleFlag $name;')
796
797    code('''
798}
799
800#endif // __DEBUG_${name}_HH__
801''')
802
803    code.write(str(target[0]))
804
805for name,flag in sorted(debug_flags.iteritems()):
806    n, compound, desc = flag
807    assert n == name
808
809    env.Command('debug/%s.hh' % name, Value(flag),
810                MakeAction(makeDebugFlagHH, Transform("TRACING", 0)))
811    env.Command('debug/%s.cc' % name, Value(flag),
812                MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
813    Source('debug/%s.cc' % name)
814
815# Embed python files.  All .py files that have been indicated by a
816# PySource() call in a SConscript need to be embedded into the M5
817# library.  To do that, we compile the file to byte code, marshal the
818# byte code, compress it, and then generate a c++ file that
819# inserts the result into an array.
820def embedPyFile(target, source, env):
821    def c_str(string):
822        if string is None:
823            return "0"
824        return '"%s"' % string
825
826    '''Action function to compile a .py into a code object, marshal
827    it, compress it, and stick it into an asm file so the code appears
828    as just bytes with a label in the data section'''
829
830    src = file(str(source[0]), 'r').read()
831
832    pysource = PySource.tnodes[source[0]]
833    compiled = compile(src, pysource.abspath, 'exec')
834    marshalled = marshal.dumps(compiled)
835    compressed = zlib.compress(marshalled)
836    data = compressed
837    sym = pysource.symname
838
839    code = code_formatter()
840    code('''\
841#include "sim/init.hh"
842
843namespace {
844
845const uint8_t data_${sym}[] = {
846''')
847    code.indent()
848    step = 16
849    for i in xrange(0, len(data), step):
850        x = array.array('B', data[i:i+step])
851        code(''.join('%d,' % d for d in x))
852    code.dedent()
853    
854    code('''};
855
856EmbeddedPython embedded_${sym}(
857    ${{c_str(pysource.arcname)}},
858    ${{c_str(pysource.abspath)}},
859    ${{c_str(pysource.modpath)}},
860    data_${sym},
861    ${{len(data)}},
862    ${{len(marshalled)}});
863
864} // anonymous namespace
865''')
866    code.write(str(target[0]))
867
868for source in PySource.all:
869    env.Command(source.cpp, source.tnode, 
870                MakeAction(embedPyFile, Transform("EMBED PY")))
871    Source(source.cpp)
872
873########################################################################
874#
875# Define binaries.  Each different build type (debug, opt, etc.) gets
876# a slightly different build environment.
877#
878
879# List of constructed environments to pass back to SConstruct
880envList = []
881
882date_source = Source('base/date.cc', skip_lib=True)
883
884# Function to create a new build environment as clone of current
885# environment 'env' with modified object suffix and optional stripped
886# binary.  Additional keyword arguments are appended to corresponding
887# build environment vars.
888def makeEnv(label, objsfx, strip = False, **kwargs):
889    # SCons doesn't know to append a library suffix when there is a '.' in the
890    # name.  Use '_' instead.
891    libname = 'gem5_' + label
892    exename = 'gem5.' + label
893    secondary_exename = 'm5.' + label
894
895    new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
896    new_env.Label = label
897    new_env.Append(**kwargs)
898
899    swig_env = new_env.Clone()
900
901    # Both gcc and clang have issues with unused labels and values in
902    # the SWIG generated code
903    swig_env.Append(CCFLAGS=['-Wno-unused-label', '-Wno-unused-value'])
904
905    # Add additional warnings here that should not be applied to
906    # the SWIG generated code
907    new_env.Append(CXXFLAGS='-Wmissing-declarations')
908
909    if env['GCC']:
910        # Depending on the SWIG version, we also need to supress
911        # warnings about uninitialized variables and missing field
912        # initializers.
913        swig_env.Append(CCFLAGS=['-Wno-uninitialized',
914                                 '-Wno-missing-field-initializers'])
915
916        if compareVersions(env['GCC_VERSION'], '4.6') >= 0:
917            swig_env.Append(CCFLAGS='-Wno-unused-but-set-variable')
918
919        # If gcc supports it, also warn for deletion of derived
920        # classes with non-virtual desctructors. For gcc >= 4.7 we
921        # also have to disable warnings about the SWIG code having
922        # potentially uninitialized variables.
923        if compareVersions(env['GCC_VERSION'], '4.7') >= 0:
924            new_env.Append(CXXFLAGS='-Wdelete-non-virtual-dtor')
925            swig_env.Append(CCFLAGS='-Wno-maybe-uninitialized')
926    if env['CLANG']:
927        # Always enable the warning for deletion of derived classes
928        # with non-virtual destructors
929        new_env.Append(CXXFLAGS=['-Wdelete-non-virtual-dtor'])
930
931    werror_env = new_env.Clone()
932    werror_env.Append(CCFLAGS='-Werror')
933
934    def make_obj(source, static, extra_deps = None):
935        '''This function adds the specified source to the correct
936        build environment, and returns the corresponding SCons Object
937        nodes'''
938
939        if source.swig:
940            env = swig_env
941        elif source.Werror:
942            env = werror_env
943        else:
944            env = new_env
945
946        if static:
947            obj = env.StaticObject(source.tnode)
948        else:
949            obj = env.SharedObject(source.tnode)
950
951        if extra_deps:
952            env.Depends(obj, extra_deps)
953
954        return obj
955
956    static_objs = \
957        [ make_obj(s, True) for s in Source.get(main=False, skip_lib=False) ]
958    shared_objs = \
959        [ make_obj(s, False) for s in Source.get(main=False, skip_lib=False) ]
960
961    static_date = make_obj(date_source, static=True, extra_deps=static_objs)
962    static_objs.append(static_date)
963    
964    shared_date = make_obj(date_source, static=False, extra_deps=shared_objs)
965    shared_objs.append(shared_date)
966
967    # First make a library of everything but main() so other programs can
968    # link against m5.
969    static_lib = new_env.StaticLibrary(libname, static_objs)
970    shared_lib = new_env.SharedLibrary(libname, shared_objs)
971
972    # Now link a stub with main() and the static library.
973    main_objs = [ make_obj(s, True) for s in Source.get(main=True) ]
974
975    for test in UnitTest.all:
976        flags = { test.target : True }
977        test_sources = Source.get(**flags)
978        test_objs = [ make_obj(s, static=True) for s in test_sources ]
979        if test.main:
980            test_objs += main_objs
981        testname = "unittest/%s.%s" % (test.target, label)
982        new_env.Program(testname, test_objs + static_objs)
983
984    progname = exename
985    if strip:
986        progname += '.unstripped'
987
988    targets = new_env.Program(progname, main_objs + static_objs)
989
990    if strip:
991        if sys.platform == 'sunos5':
992            cmd = 'cp $SOURCE $TARGET; strip $TARGET'
993        else:
994            cmd = 'strip $SOURCE -o $TARGET'
995        targets = new_env.Command(exename, progname,
996                    MakeAction(cmd, Transform("STRIP")))
997
998    new_env.Command(secondary_exename, exename,
999            MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK")))
1000
1001    new_env.M5Binary = targets[0]
1002    envList.append(new_env)
1003
1004# Start out with the compiler flags common to all compilers,
1005# i.e. they all use -g for opt and -g -pg for prof
1006ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'],
1007           'perf' : ['-g']}
1008
1009# Start out with the linker flags common to all linkers, i.e. -pg for
1010# prof, and -lprofiler for perf. The -lprofile flag is surrounded by
1011# no-as-needed and as-needed as the binutils linker is too clever and
1012# simply doesn't link to the library otherwise.
1013ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'],
1014           'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']}
1015
1016# For Link Time Optimization, the optimisation flags used to compile
1017# individual files are decoupled from those used at link time
1018# (i.e. you can compile with -O3 and perform LTO with -O0), so we need
1019# to also update the linker flags based on the target.
1020if env['GCC']:
1021    if sys.platform == 'sunos5':
1022        ccflags['debug'] += ['-gstabs+']
1023    else:
1024        ccflags['debug'] += ['-ggdb3']
1025    ldflags['debug'] += ['-O0']
1026    # opt, fast, prof and perf all share the same cc flags, also add
1027    # the optimization to the ldflags as LTO defers the optimization
1028    # to link time
1029    for target in ['opt', 'fast', 'prof', 'perf']:
1030        ccflags[target] += ['-O3']
1031        ldflags[target] += ['-O3']
1032
1033    ccflags['fast'] += env['LTO_CCFLAGS']
1034    ldflags['fast'] += env['LTO_LDFLAGS']
1035elif env['CLANG']:
1036    ccflags['debug'] += ['-g', '-O0']
1037    # opt, fast, prof and perf all share the same cc flags
1038    for target in ['opt', 'fast', 'prof', 'perf']:
1039        ccflags[target] += ['-O3']
1040else:
1041    print 'Unknown compiler, please fix compiler options'
1042    Exit(1)
1043
1044
1045# To speed things up, we only instantiate the build environments we
1046# need.  We try to identify the needed environment for each target; if
1047# we can't, we fall back on instantiating all the environments just to
1048# be safe.
1049target_types = ['debug', 'opt', 'fast', 'prof', 'perf']
1050obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof',
1051              'gpo' : 'perf'}
1052
1053def identifyTarget(t):
1054    ext = t.split('.')[-1]
1055    if ext in target_types:
1056        return ext
1057    if obj2target.has_key(ext):
1058        return obj2target[ext]
1059    match = re.search(r'/tests/([^/]+)/', t)
1060    if match and match.group(1) in target_types:
1061        return match.group(1)
1062    return 'all'
1063
1064needed_envs = [identifyTarget(target) for target in BUILD_TARGETS]
1065if 'all' in needed_envs:
1066    needed_envs += target_types
1067
1068# Debug binary
1069if 'debug' in needed_envs:
1070    makeEnv('debug', '.do',
1071            CCFLAGS = Split(ccflags['debug']),
1072            CPPDEFINES = ['DEBUG', 'TRACING_ON=1'],
1073            LINKFLAGS = Split(ldflags['debug']))
1074
1075# Optimized binary
1076if 'opt' in needed_envs:
1077    makeEnv('opt', '.o',
1078            CCFLAGS = Split(ccflags['opt']),
1079            CPPDEFINES = ['TRACING_ON=1'],
1080            LINKFLAGS = Split(ldflags['opt']))
1081
1082# "Fast" binary
1083if 'fast' in needed_envs:
1084    makeEnv('fast', '.fo', strip = True,
1085            CCFLAGS = Split(ccflags['fast']),
1086            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1087            LINKFLAGS = Split(ldflags['fast']))
1088
1089# Profiled binary using gprof
1090if 'prof' in needed_envs:
1091    makeEnv('prof', '.po',
1092            CCFLAGS = Split(ccflags['prof']),
1093            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1094            LINKFLAGS = Split(ldflags['prof']))
1095
1096# Profiled binary using google-pprof
1097if 'perf' in needed_envs:
1098    makeEnv('perf', '.gpo',
1099            CCFLAGS = Split(ccflags['perf']),
1100            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1101            LINKFLAGS = Split(ldflags['perf']))
1102
1103Return('envList')
1104