SConscript revision 12370
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 subprocess
38import sys
39import zlib
40
41from os.path import basename, dirname, exists, isdir, isfile, join as joinpath
42
43import SCons
44
45from gem5_scons import Transform
46
47# This file defines how to build a particular configuration of gem5
48# based on variable settings in the 'env' build environment.
49
50Import('*')
51
52# Children need to see the environment
53Export('env')
54
55build_env = [(opt, env[opt]) for opt in export_vars]
56
57from m5.util import code_formatter, compareVersions
58
59########################################################################
60# Code for adding source files of various types
61#
62# When specifying a source file of some type, a set of tags can be
63# specified for that file.
64
65class SourceList(list):
66    def with_tags_that(self, predicate):
67        '''Return a list of sources with tags that satisfy a predicate.'''
68        def match(source):
69            return predicate(source.tags)
70        return SourceList(filter(match, self))
71
72    def with_any_tags(self, *tags):
73        '''Return a list of sources with any of the supplied tags.'''
74        return self.with_tags_that(lambda stags: len(set(tags) & stags) > 0)
75
76    def with_all_tags(self, *tags):
77        '''Return a list of sources with all of the supplied tags.'''
78        return self.with_tags_that(lambda stags: set(tags) <= stags)
79
80    def with_tag(self, tag):
81        '''Return a list of sources with the supplied tag.'''
82        return self.with_tags_that(lambda stags: tag in stags)
83
84    def without_tags(self, *tags):
85        '''Return a list of sources without any of the supplied tags.'''
86        return self.with_tags_that(lambda stags: len(set(tags) & stags) == 0)
87
88    def without_tag(self, tag):
89        '''Return a list of sources with the supplied tag.'''
90        return self.with_tags_that(lambda stags: tag not in stags)
91
92class SourceMeta(type):
93    '''Meta class for source files that keeps track of all files of a
94    particular type.'''
95    def __init__(cls, name, bases, dict):
96        super(SourceMeta, cls).__init__(name, bases, dict)
97        cls.all = SourceList()
98
99class SourceFile(object):
100    '''Base object that encapsulates the notion of a source file.
101    This includes, the source node, target node, various manipulations
102    of those.  A source file also specifies a set of tags which
103    describing arbitrary properties of the source file.'''
104    __metaclass__ = SourceMeta
105
106    static_objs = {}
107    shared_objs = {}
108
109    def __init__(self, source, tags=None, add_tags=None):
110        if tags is None:
111            tags='gem5 lib'
112        if isinstance(tags, basestring):
113            tags = set([tags])
114        if not isinstance(tags, set):
115            tags = set(tags)
116        self.tags = tags
117
118        if add_tags:
119            if isinstance(add_tags, basestring):
120                add_tags = set([add_tags])
121            if not isinstance(add_tags, set):
122                add_tags = set(add_tags)
123            self.tags |= add_tags
124
125        tnode = source
126        if not isinstance(source, SCons.Node.FS.File):
127            tnode = File(source)
128
129        self.tnode = tnode
130        self.snode = tnode.srcnode()
131
132        for base in type(self).__mro__:
133            if issubclass(base, SourceFile):
134                base.all.append(self)
135
136    def static(self, env):
137        key = (self.tnode, env['OBJSUFFIX'])
138        if not key in self.static_objs:
139            self.static_objs[key] = env.StaticObject(self.tnode)
140        return self.static_objs[key]
141
142    def shared(self, env):
143        key = (self.tnode, env['OBJSUFFIX'])
144        if not key in self.shared_objs:
145            self.shared_objs[key] = env.SharedObject(self.tnode)
146        return self.shared_objs[key]
147
148    @property
149    def filename(self):
150        return str(self.tnode)
151
152    @property
153    def dirname(self):
154        return dirname(self.filename)
155
156    @property
157    def basename(self):
158        return basename(self.filename)
159
160    @property
161    def extname(self):
162        index = self.basename.rfind('.')
163        if index <= 0:
164            # dot files aren't extensions
165            return self.basename, None
166
167        return self.basename[:index], self.basename[index+1:]
168
169    def __lt__(self, other): return self.filename < other.filename
170    def __le__(self, other): return self.filename <= other.filename
171    def __gt__(self, other): return self.filename > other.filename
172    def __ge__(self, other): return self.filename >= other.filename
173    def __eq__(self, other): return self.filename == other.filename
174    def __ne__(self, other): return self.filename != other.filename
175
176class Source(SourceFile):
177    ungrouped_tag = 'No link group'
178    source_groups = set()
179
180    _current_group_tag = ungrouped_tag
181
182    @staticmethod
183    def link_group_tag(group):
184        return 'link group: %s' % group
185
186    @classmethod
187    def set_group(cls, group):
188        new_tag = Source.link_group_tag(group)
189        Source._current_group_tag = new_tag
190        Source.source_groups.add(group)
191
192    def _add_link_group_tag(self):
193        self.tags.add(Source._current_group_tag)
194
195    '''Add a c/c++ source file to the build'''
196    def __init__(self, source, tags=None, add_tags=None):
197        '''specify the source file, and any tags'''
198        super(Source, self).__init__(source, tags, add_tags)
199        self._add_link_group_tag()
200
201class PySource(SourceFile):
202    '''Add a python source file to the named package'''
203    invalid_sym_char = re.compile('[^A-z0-9_]')
204    modules = {}
205    tnodes = {}
206    symnames = {}
207
208    def __init__(self, package, source, tags=None, add_tags=None):
209        '''specify the python package, the source file, and any tags'''
210        super(PySource, self).__init__(source, tags, add_tags)
211
212        modname,ext = self.extname
213        assert ext == 'py'
214
215        if package:
216            path = package.split('.')
217        else:
218            path = []
219
220        modpath = path[:]
221        if modname != '__init__':
222            modpath += [ modname ]
223        modpath = '.'.join(modpath)
224
225        arcpath = path + [ self.basename ]
226        abspath = self.snode.abspath
227        if not exists(abspath):
228            abspath = self.tnode.abspath
229
230        self.package = package
231        self.modname = modname
232        self.modpath = modpath
233        self.arcname = joinpath(*arcpath)
234        self.abspath = abspath
235        self.compiled = File(self.filename + 'c')
236        self.cpp = File(self.filename + '.cc')
237        self.symname = PySource.invalid_sym_char.sub('_', modpath)
238
239        PySource.modules[modpath] = self
240        PySource.tnodes[self.tnode] = self
241        PySource.symnames[self.symname] = self
242
243class SimObject(PySource):
244    '''Add a SimObject python file as a python source object and add
245    it to a list of sim object modules'''
246
247    fixed = False
248    modnames = []
249
250    def __init__(self, source, tags=None, add_tags=None):
251        '''Specify the source file and any tags (automatically in
252        the m5.objects package)'''
253        super(SimObject, self).__init__('m5.objects', source, tags, add_tags)
254        if self.fixed:
255            raise AttributeError, "Too late to call SimObject now."
256
257        bisect.insort_right(SimObject.modnames, self.modname)
258
259class ProtoBuf(SourceFile):
260    '''Add a Protocol Buffer to build'''
261
262    def __init__(self, source, tags=None, add_tags=None):
263        '''Specify the source file, and any tags'''
264        super(ProtoBuf, self).__init__(source, tags, add_tags)
265
266        # Get the file name and the extension
267        modname,ext = self.extname
268        assert ext == 'proto'
269
270        # Currently, we stick to generating the C++ headers, so we
271        # only need to track the source and header.
272        self.cc_file = File(modname + '.pb.cc')
273        self.hh_file = File(modname + '.pb.h')
274
275class UnitTest(object):
276    '''Create a UnitTest'''
277
278    all = []
279    def __init__(self, target, *sources, **kwargs):
280        '''Specify the target name and any sources.  Sources that are
281        not SourceFiles are evalued with Source().  All files are
282        tagged with the name of the UnitTest target.'''
283
284        srcs = SourceList()
285        for src in sources:
286            if not isinstance(src, SourceFile):
287                src = Source(src, tags=str(target))
288            srcs.append(src)
289
290        self.sources = srcs
291        self.target = target
292        self.main = kwargs.get('main', False)
293        self.all.append(self)
294
295class GTest(UnitTest):
296    '''Create a unit test based on the google test framework.'''
297
298    all = []
299    def __init__(self, *args, **kwargs):
300        super(GTest, self).__init__(*args, **kwargs)
301        self.dir = Dir('.')
302
303# Children should have access
304Export('Source')
305Export('PySource')
306Export('SimObject')
307Export('ProtoBuf')
308Export('UnitTest')
309Export('GTest')
310
311########################################################################
312#
313# Debug Flags
314#
315debug_flags = {}
316def DebugFlag(name, desc=None):
317    if name in debug_flags:
318        raise AttributeError, "Flag %s already specified" % name
319    debug_flags[name] = (name, (), desc)
320
321def CompoundFlag(name, flags, desc=None):
322    if name in debug_flags:
323        raise AttributeError, "Flag %s already specified" % name
324
325    compound = tuple(flags)
326    debug_flags[name] = (name, compound, desc)
327
328Export('DebugFlag')
329Export('CompoundFlag')
330
331########################################################################
332#
333# Set some compiler variables
334#
335
336# Include file paths are rooted in this directory.  SCons will
337# automatically expand '.' to refer to both the source directory and
338# the corresponding build directory to pick up generated include
339# files.
340env.Append(CPPPATH=Dir('.'))
341
342for extra_dir in extras_dir_list:
343    env.Append(CPPPATH=Dir(extra_dir))
344
345# Workaround for bug in SCons version > 0.97d20071212
346# Scons bug id: 2006 gem5 Bug id: 308
347for root, dirs, files in os.walk(base_dir, topdown=True):
348    Dir(root[len(base_dir) + 1:])
349
350########################################################################
351#
352# Walk the tree and execute all SConscripts in subdirectories
353#
354
355here = Dir('.').srcnode().abspath
356for root, dirs, files in os.walk(base_dir, topdown=True):
357    if root == here:
358        # we don't want to recurse back into this SConscript
359        continue
360
361    if 'SConscript' in files:
362        build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
363        Source.set_group(build_dir)
364        SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
365
366for extra_dir in extras_dir_list:
367    prefix_len = len(dirname(extra_dir)) + 1
368
369    # Also add the corresponding build directory to pick up generated
370    # include files.
371    env.Append(CPPPATH=Dir(joinpath(env['BUILDDIR'], extra_dir[prefix_len:])))
372
373    for root, dirs, files in os.walk(extra_dir, topdown=True):
374        # if build lives in the extras directory, don't walk down it
375        if 'build' in dirs:
376            dirs.remove('build')
377
378        if 'SConscript' in files:
379            build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
380            SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
381
382for opt in export_vars:
383    env.ConfigFile(opt)
384
385def makeTheISA(source, target, env):
386    isas = [ src.get_contents() for src in source ]
387    target_isa = env['TARGET_ISA']
388    def define(isa):
389        return isa.upper() + '_ISA'
390
391    def namespace(isa):
392        return isa[0].upper() + isa[1:].lower() + 'ISA'
393
394
395    code = code_formatter()
396    code('''\
397#ifndef __CONFIG_THE_ISA_HH__
398#define __CONFIG_THE_ISA_HH__
399
400''')
401
402    # create defines for the preprocessing and compile-time determination
403    for i,isa in enumerate(isas):
404        code('#define $0 $1', define(isa), i + 1)
405    code()
406
407    # create an enum for any run-time determination of the ISA, we
408    # reuse the same name as the namespaces
409    code('enum class Arch {')
410    for i,isa in enumerate(isas):
411        if i + 1 == len(isas):
412            code('  $0 = $1', namespace(isa), define(isa))
413        else:
414            code('  $0 = $1,', namespace(isa), define(isa))
415    code('};')
416
417    code('''
418
419#define THE_ISA ${{define(target_isa)}}
420#define TheISA ${{namespace(target_isa)}}
421#define THE_ISA_STR "${{target_isa}}"
422
423#endif // __CONFIG_THE_ISA_HH__''')
424
425    code.write(str(target[0]))
426
427env.Command('config/the_isa.hh', map(Value, all_isa_list),
428            MakeAction(makeTheISA, Transform("CFG ISA", 0)))
429
430def makeTheGPUISA(source, target, env):
431    isas = [ src.get_contents() for src in source ]
432    target_gpu_isa = env['TARGET_GPU_ISA']
433    def define(isa):
434        return isa.upper() + '_ISA'
435
436    def namespace(isa):
437        return isa[0].upper() + isa[1:].lower() + 'ISA'
438
439
440    code = code_formatter()
441    code('''\
442#ifndef __CONFIG_THE_GPU_ISA_HH__
443#define __CONFIG_THE_GPU_ISA_HH__
444
445''')
446
447    # create defines for the preprocessing and compile-time determination
448    for i,isa in enumerate(isas):
449        code('#define $0 $1', define(isa), i + 1)
450    code()
451
452    # create an enum for any run-time determination of the ISA, we
453    # reuse the same name as the namespaces
454    code('enum class GPUArch {')
455    for i,isa in enumerate(isas):
456        if i + 1 == len(isas):
457            code('  $0 = $1', namespace(isa), define(isa))
458        else:
459            code('  $0 = $1,', namespace(isa), define(isa))
460    code('};')
461
462    code('''
463
464#define THE_GPU_ISA ${{define(target_gpu_isa)}}
465#define TheGpuISA ${{namespace(target_gpu_isa)}}
466#define THE_GPU_ISA_STR "${{target_gpu_isa}}"
467
468#endif // __CONFIG_THE_GPU_ISA_HH__''')
469
470    code.write(str(target[0]))
471
472env.Command('config/the_gpu_isa.hh', map(Value, all_gpu_isa_list),
473            MakeAction(makeTheGPUISA, Transform("CFG ISA", 0)))
474
475########################################################################
476#
477# Prevent any SimObjects from being added after this point, they
478# should all have been added in the SConscripts above
479#
480SimObject.fixed = True
481
482class DictImporter(object):
483    '''This importer takes a dictionary of arbitrary module names that
484    map to arbitrary filenames.'''
485    def __init__(self, modules):
486        self.modules = modules
487        self.installed = set()
488
489    def __del__(self):
490        self.unload()
491
492    def unload(self):
493        import sys
494        for module in self.installed:
495            del sys.modules[module]
496        self.installed = set()
497
498    def find_module(self, fullname, path):
499        if fullname == 'm5.defines':
500            return self
501
502        if fullname == 'm5.objects':
503            return self
504
505        if fullname.startswith('_m5'):
506            return None
507
508        source = self.modules.get(fullname, None)
509        if source is not None and fullname.startswith('m5.objects'):
510            return self
511
512        return None
513
514    def load_module(self, fullname):
515        mod = imp.new_module(fullname)
516        sys.modules[fullname] = mod
517        self.installed.add(fullname)
518
519        mod.__loader__ = self
520        if fullname == 'm5.objects':
521            mod.__path__ = fullname.split('.')
522            return mod
523
524        if fullname == 'm5.defines':
525            mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
526            return mod
527
528        source = self.modules[fullname]
529        if source.modname == '__init__':
530            mod.__path__ = source.modpath
531        mod.__file__ = source.abspath
532
533        exec file(source.abspath, 'r') in mod.__dict__
534
535        return mod
536
537import m5.SimObject
538import m5.params
539from m5.util import code_formatter
540
541m5.SimObject.clear()
542m5.params.clear()
543
544# install the python importer so we can grab stuff from the source
545# tree itself.  We can't have SimObjects added after this point or
546# else we won't know about them for the rest of the stuff.
547importer = DictImporter(PySource.modules)
548sys.meta_path[0:0] = [ importer ]
549
550# import all sim objects so we can populate the all_objects list
551# make sure that we're working with a list, then let's sort it
552for modname in SimObject.modnames:
553    exec('from m5.objects import %s' % modname)
554
555# we need to unload all of the currently imported modules so that they
556# will be re-imported the next time the sconscript is run
557importer.unload()
558sys.meta_path.remove(importer)
559
560sim_objects = m5.SimObject.allClasses
561all_enums = m5.params.allEnums
562
563for name,obj in sorted(sim_objects.iteritems()):
564    for param in obj._params.local.values():
565        # load the ptype attribute now because it depends on the
566        # current version of SimObject.allClasses, but when scons
567        # actually uses the value, all versions of
568        # SimObject.allClasses will have been loaded
569        param.ptype
570
571########################################################################
572#
573# calculate extra dependencies
574#
575module_depends = ["m5", "m5.SimObject", "m5.params"]
576depends = [ PySource.modules[dep].snode for dep in module_depends ]
577depends.sort(key = lambda x: x.name)
578
579########################################################################
580#
581# Commands for the basic automatically generated python files
582#
583
584# Generate Python file containing a dict specifying the current
585# buildEnv flags.
586def makeDefinesPyFile(target, source, env):
587    build_env = source[0].get_contents()
588
589    code = code_formatter()
590    code("""
591import _m5.core
592import m5.util
593
594buildEnv = m5.util.SmartDict($build_env)
595
596compileDate = _m5.core.compileDate
597_globals = globals()
598for key,val in _m5.core.__dict__.iteritems():
599    if key.startswith('flag_'):
600        flag = key[5:]
601        _globals[flag] = val
602del _globals
603""")
604    code.write(target[0].abspath)
605
606defines_info = Value(build_env)
607# Generate a file with all of the compile options in it
608env.Command('python/m5/defines.py', defines_info,
609            MakeAction(makeDefinesPyFile, Transform("DEFINES", 0)))
610PySource('m5', 'python/m5/defines.py')
611
612# Generate python file containing info about the M5 source code
613def makeInfoPyFile(target, source, env):
614    code = code_formatter()
615    for src in source:
616        data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
617        code('$src = ${{repr(data)}}')
618    code.write(str(target[0]))
619
620# Generate a file that wraps the basic top level files
621env.Command('python/m5/info.py',
622            [ '#/COPYING', '#/LICENSE', '#/README', ],
623            MakeAction(makeInfoPyFile, Transform("INFO")))
624PySource('m5', 'python/m5/info.py')
625
626########################################################################
627#
628# Create all of the SimObject param headers and enum headers
629#
630
631def createSimObjectParamStruct(target, source, env):
632    assert len(target) == 1 and len(source) == 1
633
634    name = source[0].get_text_contents()
635    obj = sim_objects[name]
636
637    code = code_formatter()
638    obj.cxx_param_decl(code)
639    code.write(target[0].abspath)
640
641def createSimObjectCxxConfig(is_header):
642    def body(target, source, env):
643        assert len(target) == 1 and len(source) == 1
644
645        name = str(source[0].get_contents())
646        obj = sim_objects[name]
647
648        code = code_formatter()
649        obj.cxx_config_param_file(code, is_header)
650        code.write(target[0].abspath)
651    return body
652
653def createEnumStrings(target, source, env):
654    assert len(target) == 1 and len(source) == 2
655
656    name = source[0].get_text_contents()
657    use_python = source[1].read()
658    obj = all_enums[name]
659
660    code = code_formatter()
661    obj.cxx_def(code)
662    if use_python:
663        obj.pybind_def(code)
664    code.write(target[0].abspath)
665
666def createEnumDecls(target, source, env):
667    assert len(target) == 1 and len(source) == 1
668
669    name = source[0].get_text_contents()
670    obj = all_enums[name]
671
672    code = code_formatter()
673    obj.cxx_decl(code)
674    code.write(target[0].abspath)
675
676def createSimObjectPyBindWrapper(target, source, env):
677    name = source[0].get_text_contents()
678    obj = sim_objects[name]
679
680    code = code_formatter()
681    obj.pybind_decl(code)
682    code.write(target[0].abspath)
683
684# Generate all of the SimObject param C++ struct header files
685params_hh_files = []
686for name,simobj in sorted(sim_objects.iteritems()):
687    py_source = PySource.modules[simobj.__module__]
688    extra_deps = [ py_source.tnode ]
689
690    hh_file = File('params/%s.hh' % name)
691    params_hh_files.append(hh_file)
692    env.Command(hh_file, Value(name),
693                MakeAction(createSimObjectParamStruct, Transform("SO PARAM")))
694    env.Depends(hh_file, depends + extra_deps)
695
696# C++ parameter description files
697if GetOption('with_cxx_config'):
698    for name,simobj in sorted(sim_objects.iteritems()):
699        py_source = PySource.modules[simobj.__module__]
700        extra_deps = [ py_source.tnode ]
701
702        cxx_config_hh_file = File('cxx_config/%s.hh' % name)
703        cxx_config_cc_file = File('cxx_config/%s.cc' % name)
704        env.Command(cxx_config_hh_file, Value(name),
705                    MakeAction(createSimObjectCxxConfig(True),
706                    Transform("CXXCPRHH")))
707        env.Command(cxx_config_cc_file, Value(name),
708                    MakeAction(createSimObjectCxxConfig(False),
709                    Transform("CXXCPRCC")))
710        env.Depends(cxx_config_hh_file, depends + extra_deps +
711                    [File('params/%s.hh' % name), File('sim/cxx_config.hh')])
712        env.Depends(cxx_config_cc_file, depends + extra_deps +
713                    [cxx_config_hh_file])
714        Source(cxx_config_cc_file)
715
716    cxx_config_init_cc_file = File('cxx_config/init.cc')
717
718    def createCxxConfigInitCC(target, source, env):
719        assert len(target) == 1 and len(source) == 1
720
721        code = code_formatter()
722
723        for name,simobj in sorted(sim_objects.iteritems()):
724            if not hasattr(simobj, 'abstract') or not simobj.abstract:
725                code('#include "cxx_config/${name}.hh"')
726        code()
727        code('void cxxConfigInit()')
728        code('{')
729        code.indent()
730        for name,simobj in sorted(sim_objects.iteritems()):
731            not_abstract = not hasattr(simobj, 'abstract') or \
732                not simobj.abstract
733            if not_abstract and 'type' in simobj.__dict__:
734                code('cxx_config_directory["${name}"] = '
735                     '${name}CxxConfigParams::makeDirectoryEntry();')
736        code.dedent()
737        code('}')
738        code.write(target[0].abspath)
739
740    py_source = PySource.modules[simobj.__module__]
741    extra_deps = [ py_source.tnode ]
742    env.Command(cxx_config_init_cc_file, Value(name),
743        MakeAction(createCxxConfigInitCC, Transform("CXXCINIT")))
744    cxx_param_hh_files = ["cxx_config/%s.hh" % simobj
745        for name,simobj in sorted(sim_objects.iteritems())
746        if not hasattr(simobj, 'abstract') or not simobj.abstract]
747    Depends(cxx_config_init_cc_file, cxx_param_hh_files +
748            [File('sim/cxx_config.hh')])
749    Source(cxx_config_init_cc_file)
750
751# Generate all enum header files
752for name,enum in sorted(all_enums.iteritems()):
753    py_source = PySource.modules[enum.__module__]
754    extra_deps = [ py_source.tnode ]
755
756    cc_file = File('enums/%s.cc' % name)
757    env.Command(cc_file, [Value(name), Value(env['USE_PYTHON'])],
758                MakeAction(createEnumStrings, Transform("ENUM STR")))
759    env.Depends(cc_file, depends + extra_deps)
760    Source(cc_file)
761
762    hh_file = File('enums/%s.hh' % name)
763    env.Command(hh_file, Value(name),
764                MakeAction(createEnumDecls, Transform("ENUMDECL")))
765    env.Depends(hh_file, depends + extra_deps)
766
767# Generate SimObject Python bindings wrapper files
768if env['USE_PYTHON']:
769    for name,simobj in sorted(sim_objects.iteritems()):
770        py_source = PySource.modules[simobj.__module__]
771        extra_deps = [ py_source.tnode ]
772        cc_file = File('python/_m5/param_%s.cc' % name)
773        env.Command(cc_file, Value(name),
774                    MakeAction(createSimObjectPyBindWrapper,
775                               Transform("SO PyBind")))
776        env.Depends(cc_file, depends + extra_deps)
777        Source(cc_file)
778
779# Build all protocol buffers if we have got protoc and protobuf available
780if env['HAVE_PROTOBUF']:
781    for proto in ProtoBuf.all:
782        # Use both the source and header as the target, and the .proto
783        # file as the source. When executing the protoc compiler, also
784        # specify the proto_path to avoid having the generated files
785        # include the path.
786        env.Command([proto.cc_file, proto.hh_file], proto.tnode,
787                    MakeAction('$PROTOC --cpp_out ${TARGET.dir} '
788                               '--proto_path ${SOURCE.dir} $SOURCE',
789                               Transform("PROTOC")))
790
791        # Add the C++ source file
792        Source(proto.cc_file, tags=proto.tags)
793elif ProtoBuf.all:
794    print 'Got protobuf to build, but lacks support!'
795    Exit(1)
796
797#
798# Handle debug flags
799#
800def makeDebugFlagCC(target, source, env):
801    assert(len(target) == 1 and len(source) == 1)
802
803    code = code_formatter()
804
805    # delay definition of CompoundFlags until after all the definition
806    # of all constituent SimpleFlags
807    comp_code = code_formatter()
808
809    # file header
810    code('''
811/*
812 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
813 */
814
815#include "base/debug.hh"
816
817namespace Debug {
818
819''')
820
821    for name, flag in sorted(source[0].read().iteritems()):
822        n, compound, desc = flag
823        assert n == name
824
825        if not compound:
826            code('SimpleFlag $name("$name", "$desc");')
827        else:
828            comp_code('CompoundFlag $name("$name", "$desc",')
829            comp_code.indent()
830            last = len(compound) - 1
831            for i,flag in enumerate(compound):
832                if i != last:
833                    comp_code('&$flag,')
834                else:
835                    comp_code('&$flag);')
836            comp_code.dedent()
837
838    code.append(comp_code)
839    code()
840    code('} // namespace Debug')
841
842    code.write(str(target[0]))
843
844def makeDebugFlagHH(target, source, env):
845    assert(len(target) == 1 and len(source) == 1)
846
847    val = eval(source[0].get_contents())
848    name, compound, desc = val
849
850    code = code_formatter()
851
852    # file header boilerplate
853    code('''\
854/*
855 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
856 */
857
858#ifndef __DEBUG_${name}_HH__
859#define __DEBUG_${name}_HH__
860
861namespace Debug {
862''')
863
864    if compound:
865        code('class CompoundFlag;')
866    code('class SimpleFlag;')
867
868    if compound:
869        code('extern CompoundFlag $name;')
870        for flag in compound:
871            code('extern SimpleFlag $flag;')
872    else:
873        code('extern SimpleFlag $name;')
874
875    code('''
876}
877
878#endif // __DEBUG_${name}_HH__
879''')
880
881    code.write(str(target[0]))
882
883for name,flag in sorted(debug_flags.iteritems()):
884    n, compound, desc = flag
885    assert n == name
886
887    hh_file = 'debug/%s.hh' % name
888    env.Command(hh_file, Value(flag),
889                MakeAction(makeDebugFlagHH, Transform("TRACING", 0)))
890
891env.Command('debug/flags.cc', Value(debug_flags),
892            MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
893Source('debug/flags.cc')
894
895# version tags
896tags = \
897env.Command('sim/tags.cc', None,
898            MakeAction('util/cpt_upgrader.py --get-cc-file > $TARGET',
899                       Transform("VER TAGS")))
900env.AlwaysBuild(tags)
901
902# Embed python files.  All .py files that have been indicated by a
903# PySource() call in a SConscript need to be embedded into the M5
904# library.  To do that, we compile the file to byte code, marshal the
905# byte code, compress it, and then generate a c++ file that
906# inserts the result into an array.
907def embedPyFile(target, source, env):
908    def c_str(string):
909        if string is None:
910            return "0"
911        return '"%s"' % string
912
913    '''Action function to compile a .py into a code object, marshal
914    it, compress it, and stick it into an asm file so the code appears
915    as just bytes with a label in the data section'''
916
917    src = file(str(source[0]), 'r').read()
918
919    pysource = PySource.tnodes[source[0]]
920    compiled = compile(src, pysource.abspath, 'exec')
921    marshalled = marshal.dumps(compiled)
922    compressed = zlib.compress(marshalled)
923    data = compressed
924    sym = pysource.symname
925
926    code = code_formatter()
927    code('''\
928#include "sim/init.hh"
929
930namespace {
931
932const uint8_t data_${sym}[] = {
933''')
934    code.indent()
935    step = 16
936    for i in xrange(0, len(data), step):
937        x = array.array('B', data[i:i+step])
938        code(''.join('%d,' % d for d in x))
939    code.dedent()
940
941    code('''};
942
943EmbeddedPython embedded_${sym}(
944    ${{c_str(pysource.arcname)}},
945    ${{c_str(pysource.abspath)}},
946    ${{c_str(pysource.modpath)}},
947    data_${sym},
948    ${{len(data)}},
949    ${{len(marshalled)}});
950
951} // anonymous namespace
952''')
953    code.write(str(target[0]))
954
955for source in PySource.all:
956    env.Command(source.cpp, source.tnode,
957                MakeAction(embedPyFile, Transform("EMBED PY")))
958    Source(source.cpp, tags=source.tags, add_tags='python')
959
960########################################################################
961#
962# Define binaries.  Each different build type (debug, opt, etc.) gets
963# a slightly different build environment.
964#
965
966# List of constructed environments to pass back to SConstruct
967date_source = Source('base/date.cc', tags=[])
968
969# Function to create a new build environment as clone of current
970# environment 'env' with modified object suffix and optional stripped
971# binary.  Additional keyword arguments are appended to corresponding
972# build environment vars.
973def makeEnv(env, label, objsfx, strip=False, disable_partial=False, **kwargs):
974    # SCons doesn't know to append a library suffix when there is a '.' in the
975    # name.  Use '_' instead.
976    libname = 'gem5_' + label
977    exename = 'gem5.' + label
978    secondary_exename = 'm5.' + label
979
980    new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
981    new_env.Label = label
982    new_env.Append(**kwargs)
983
984    lib_sources = Source.all.with_tag('gem5 lib')
985
986    # Without Python, leave out all Python content from the library
987    # builds.  The option doesn't affect gem5 built as a program
988    if GetOption('without_python'):
989        lib_sources = lib_sources.without_tag('python')
990
991    static_objs = []
992    shared_objs = []
993
994    for s in lib_sources.with_tag(Source.ungrouped_tag):
995        static_objs.append(s.static(new_env))
996        shared_objs.append(s.shared(new_env))
997
998    for group in Source.source_groups:
999        srcs = lib_sources.with_tag(Source.link_group_tag(group))
1000        if not srcs:
1001            continue
1002
1003        group_static = [ s.static(new_env) for s in srcs ]
1004        group_shared = [ s.shared(new_env) for s in srcs ]
1005
1006        # If partial linking is disabled, add these sources to the build
1007        # directly, and short circuit this loop.
1008        if disable_partial:
1009            static_objs.extend(group_static)
1010            shared_objs.extend(group_shared)
1011            continue
1012
1013        # Set up the static partially linked objects.
1014        file_name = new_env.subst("${OBJPREFIX}lib${OBJSUFFIX}.partial")
1015        target = File(joinpath(group, file_name))
1016        partial = env.PartialStatic(target=target, source=group_static)
1017        static_objs.extend(partial)
1018
1019        # Set up the shared partially linked objects.
1020        file_name = new_env.subst("${SHOBJPREFIX}lib${SHOBJSUFFIX}.partial")
1021        target = File(joinpath(group, file_name))
1022        partial = env.PartialShared(target=target, source=group_shared)
1023        shared_objs.extend(partial)
1024
1025    static_date = date_source.static(new_env)
1026    new_env.Depends(static_date, static_objs)
1027    static_objs.extend(static_date)
1028
1029    shared_date = date_source.shared(new_env)
1030    new_env.Depends(shared_date, shared_objs)
1031    shared_objs.extend(shared_date)
1032
1033    # First make a library of everything but main() so other programs can
1034    # link against m5.
1035    static_lib = new_env.StaticLibrary(libname, static_objs)
1036    shared_lib = new_env.SharedLibrary(libname, shared_objs)
1037
1038    # Now link a stub with main() and the static library.
1039    main_objs = [ s.static(new_env) for s in Source.all.with_tag('main') ]
1040
1041    for test in UnitTest.all:
1042        test_sources = Source.all.with_tag(str(test.target))
1043        test_objs = [ s.static(new_env) for s in test_sources ]
1044        if test.main:
1045            test_objs += main_objs
1046        path = 'unittest/%s.%s' % (test.target, label)
1047        new_env.Program(path, test_objs + static_objs)
1048
1049    gtest_env = new_env.Clone()
1050    gtest_env.Append(LIBS=gtest_env['GTEST_LIBS'])
1051    gtest_env.Append(CPPFLAGS=gtest_env['GTEST_CPPFLAGS'])
1052    gtests = []
1053    for test in GTest.all:
1054        test_sources = Source.all.with_tag(str(test.target))
1055        test_objs = [ s.static(gtest_env) for s in test_sources ]
1056        gtests.append(gtest_env.Program(
1057            test.dir.File('%s.%s' % (test.target, label)), test_objs))
1058
1059    gtest_target = Dir(new_env['BUILDDIR']).File('unittests.%s' % label)
1060    AlwaysBuild(Command(gtest_target, gtests, gtests))
1061
1062    progname = exename
1063    if strip:
1064        progname += '.unstripped'
1065
1066    targets = new_env.Program(progname, main_objs + static_objs)
1067
1068    if strip:
1069        if sys.platform == 'sunos5':
1070            cmd = 'cp $SOURCE $TARGET; strip $TARGET'
1071        else:
1072            cmd = 'strip $SOURCE -o $TARGET'
1073        targets = new_env.Command(exename, progname,
1074                    MakeAction(cmd, Transform("STRIP")))
1075
1076    new_env.Command(secondary_exename, exename,
1077            MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK")))
1078
1079    new_env.M5Binary = targets[0]
1080
1081    # Set up regression tests.
1082    SConscript(os.path.join(env.root.abspath, 'tests', 'SConscript'),
1083               variant_dir=Dir('tests').Dir(new_env.Label),
1084               exports={ 'env' : new_env }, duplicate=False)
1085
1086# Start out with the compiler flags common to all compilers,
1087# i.e. they all use -g for opt and -g -pg for prof
1088ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'],
1089           'perf' : ['-g']}
1090
1091# Start out with the linker flags common to all linkers, i.e. -pg for
1092# prof, and -lprofiler for perf. The -lprofile flag is surrounded by
1093# no-as-needed and as-needed as the binutils linker is too clever and
1094# simply doesn't link to the library otherwise.
1095ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'],
1096           'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']}
1097
1098# For Link Time Optimization, the optimisation flags used to compile
1099# individual files are decoupled from those used at link time
1100# (i.e. you can compile with -O3 and perform LTO with -O0), so we need
1101# to also update the linker flags based on the target.
1102if env['GCC']:
1103    if sys.platform == 'sunos5':
1104        ccflags['debug'] += ['-gstabs+']
1105    else:
1106        ccflags['debug'] += ['-ggdb3']
1107    ldflags['debug'] += ['-O0']
1108    # opt, fast, prof and perf all share the same cc flags, also add
1109    # the optimization to the ldflags as LTO defers the optimization
1110    # to link time
1111    for target in ['opt', 'fast', 'prof', 'perf']:
1112        ccflags[target] += ['-O3']
1113        ldflags[target] += ['-O3']
1114
1115    ccflags['fast'] += env['LTO_CCFLAGS']
1116    ldflags['fast'] += env['LTO_LDFLAGS']
1117elif env['CLANG']:
1118    ccflags['debug'] += ['-g', '-O0']
1119    # opt, fast, prof and perf all share the same cc flags
1120    for target in ['opt', 'fast', 'prof', 'perf']:
1121        ccflags[target] += ['-O3']
1122else:
1123    print 'Unknown compiler, please fix compiler options'
1124    Exit(1)
1125
1126
1127# To speed things up, we only instantiate the build environments we
1128# need.  We try to identify the needed environment for each target; if
1129# we can't, we fall back on instantiating all the environments just to
1130# be safe.
1131target_types = ['debug', 'opt', 'fast', 'prof', 'perf']
1132obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof',
1133              'gpo' : 'perf'}
1134
1135def identifyTarget(t):
1136    ext = t.split('.')[-1]
1137    if ext in target_types:
1138        return ext
1139    if obj2target.has_key(ext):
1140        return obj2target[ext]
1141    match = re.search(r'/tests/([^/]+)/', t)
1142    if match and match.group(1) in target_types:
1143        return match.group(1)
1144    return 'all'
1145
1146needed_envs = [identifyTarget(target) for target in BUILD_TARGETS]
1147if 'all' in needed_envs:
1148    needed_envs += target_types
1149
1150# Debug binary
1151if 'debug' in needed_envs:
1152    makeEnv(env, 'debug', '.do',
1153            CCFLAGS = Split(ccflags['debug']),
1154            CPPDEFINES = ['DEBUG', 'TRACING_ON=1'],
1155            LINKFLAGS = Split(ldflags['debug']))
1156
1157# Optimized binary
1158if 'opt' in needed_envs:
1159    makeEnv(env, 'opt', '.o',
1160            CCFLAGS = Split(ccflags['opt']),
1161            CPPDEFINES = ['TRACING_ON=1'],
1162            LINKFLAGS = Split(ldflags['opt']))
1163
1164# "Fast" binary
1165if 'fast' in needed_envs:
1166    disable_partial = \
1167            env.get('BROKEN_INCREMENTAL_LTO', False) and \
1168            GetOption('force_lto')
1169    makeEnv(env, 'fast', '.fo', strip = True,
1170            CCFLAGS = Split(ccflags['fast']),
1171            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1172            LINKFLAGS = Split(ldflags['fast']),
1173            disable_partial=disable_partial)
1174
1175# Profiled binary using gprof
1176if 'prof' in needed_envs:
1177    makeEnv(env, 'prof', '.po',
1178            CCFLAGS = Split(ccflags['prof']),
1179            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1180            LINKFLAGS = Split(ldflags['prof']))
1181
1182# Profiled binary using google-pprof
1183if 'perf' in needed_envs:
1184    makeEnv(env, 'perf', '.gpo',
1185            CCFLAGS = Split(ccflags['perf']),
1186            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1187            LINKFLAGS = Split(ldflags['perf']))
1188