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