SConscript revision 12305
1955SN/A# -*- mode:python -*-
2955SN/A
31762SN/A# Copyright (c) 2004-2005 The Regents of The University of Michigan
4955SN/A# All rights reserved.
5955SN/A#
6955SN/A# Redistribution and use in source and binary forms, with or without
7955SN/A# modification, are permitted provided that the following conditions are
8955SN/A# met: redistributions of source code must retain the above copyright
9955SN/A# notice, this list of conditions and the following disclaimer;
10955SN/A# redistributions in binary form must reproduce the above copyright
11955SN/A# notice, this list of conditions and the following disclaimer in the
12955SN/A# documentation and/or other materials provided with the distribution;
13955SN/A# neither the name of the copyright holders nor the names of its
14955SN/A# contributors may be used to endorse or promote products derived from
15955SN/A# this software without specific prior written permission.
16955SN/A#
17955SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18955SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19955SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20955SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21955SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22955SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23955SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24955SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25955SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26955SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27955SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28955SN/A#
29955SN/A# Authors: Nathan Binkert
30955SN/A
311608SN/Aimport array
32955SN/Aimport bisect
33955SN/Aimport imp
34955SN/Aimport marshal
35955SN/Aimport os
36955SN/Aimport re
37955SN/Aimport subprocess
38955SN/Aimport sys
39955SN/Aimport zlib
40955SN/A
41955SN/Afrom os.path import basename, dirname, exists, isdir, isfile, join as joinpath
42955SN/A
43955SN/Aimport SCons
44955SN/A
45955SN/Afrom gem5_scons import Transform
462023SN/A
47955SN/A# This file defines how to build a particular configuration of gem5
48955SN/A# based on variable settings in the 'env' build environment.
49955SN/A
50955SN/AImport('*')
51955SN/A
52955SN/A# Children need to see the environment
53955SN/AExport('env')
54955SN/A
55955SN/Abuild_env = [(opt, env[opt]) for opt in export_vars]
561031SN/A
57955SN/Afrom m5.util import code_formatter, compareVersions
581388SN/A
59955SN/A########################################################################
60955SN/A# Code for adding source files of various types
611296SN/A#
62955SN/A# When specifying a source file of some type, a set of tags can be
632609SN/A# specified for that file.
64955SN/A
65955SN/Aclass SourceList(list):
66955SN/A    def with_tags_that(self, predicate):
67955SN/A        '''Return a list of sources with tags that satisfy a predicate.'''
68955SN/A        def match(source):
69955SN/A            return predicate(source.tags)
70955SN/A        return SourceList(filter(match, self))
71955SN/A
72955SN/A    def with_any_tags(self, *tags):
73955SN/A        '''Return a list of sources with any of the supplied tags.'''
74955SN/A        return self.with_tags_that(lambda stags: len(tags & stags) > 0)
75955SN/A
76955SN/A    def with_all_tags(self, *tags):
77955SN/A        '''Return a list of sources with all of the supplied tags.'''
78955SN/A        return self.with_tags_that(lambda stags: tags <= stags)
79955SN/A
80955SN/A    def with_tag(self, tag):
81955SN/A        '''Return a list of sources with the supplied tag.'''
821717SN/A        return self.with_tags_that(lambda stags: tag in stags)
832190SN/A
842652Ssaidi@eecs.umich.edu    def without_tags(self, *tags):
85955SN/A        '''Return a list of sources without any of the supplied tags.'''
862410SN/A        return self.with_tags_that(lambda stags: len(tags & stags) == 0)
87955SN/A
88955SN/A    def without_tag(self, tag):
891717SN/A        '''Return a list of sources with the supplied tag.'''
902568SN/A        return self.with_tags_that(lambda stags: tag not in stags)
912568SN/A
922568SN/Aclass SourceMeta(type):
932499SN/A    '''Meta class for source files that keeps track of all files of a
942462SN/A    particular type.'''
952568SN/A    def __init__(cls, name, bases, dict):
962395SN/A        super(SourceMeta, cls).__init__(name, bases, dict)
972405SN/A        cls.all = SourceList()
982568SN/A
99955SN/Aclass SourceFile(object):
100955SN/A    '''Base object that encapsulates the notion of a source file.
101955SN/A    This includes, the source node, target node, various manipulations
102955SN/A    of those.  A source file also specifies a set of tags which
103955SN/A    describing arbitrary properties of the source file.'''
1042090SN/A    __metaclass__ = SourceMeta
105955SN/A    def __init__(self, source, tags=None, add_tags=None):
106955SN/A        if tags is None:
107955SN/A            tags='gem5 lib'
1081696SN/A        if isinstance(tags, basestring):
109955SN/A            tags = set([tags])
110955SN/A        if isinstance(add_tags, basestring):
111955SN/A            add_tags = set([add_tags])
112955SN/A        if add_tags:
1131127SN/A            tags = tags | add_tags
114955SN/A        self.tags = set(tags)
115955SN/A
1162379SN/A        tnode = source
117955SN/A        if not isinstance(source, SCons.Node.FS.File):
118955SN/A            tnode = File(source)
119955SN/A
1202155SN/A        self.tnode = tnode
1212155SN/A        self.snode = tnode.srcnode()
1222155SN/A
1232155SN/A        for base in type(self).__mro__:
1242155SN/A            if issubclass(base, SourceFile):
1252155SN/A                base.all.append(self)
1262155SN/A
1272155SN/A    @property
1282155SN/A    def filename(self):
1292155SN/A        return str(self.tnode)
1302155SN/A
1312155SN/A    @property
1322155SN/A    def dirname(self):
1332155SN/A        return dirname(self.filename)
1342155SN/A
1352155SN/A    @property
1362155SN/A    def basename(self):
1372155SN/A        return basename(self.filename)
1382155SN/A
1392155SN/A    @property
1402155SN/A    def extname(self):
1412155SN/A        index = self.basename.rfind('.')
1422155SN/A        if index <= 0:
1432155SN/A            # dot files aren't extensions
1442155SN/A            return self.basename, None
1452155SN/A
1462155SN/A        return self.basename[:index], self.basename[index+1:]
1472155SN/A
1482155SN/A    def __lt__(self, other): return self.filename < other.filename
1492155SN/A    def __le__(self, other): return self.filename <= other.filename
1502155SN/A    def __gt__(self, other): return self.filename > other.filename
1512155SN/A    def __ge__(self, other): return self.filename >= other.filename
1522155SN/A    def __eq__(self, other): return self.filename == other.filename
1532155SN/A    def __ne__(self, other): return self.filename != other.filename
1542155SN/A
1552155SN/Aclass Source(SourceFile):
1562155SN/A    ungrouped_tag = 'No link group'
1572155SN/A    source_groups = set()
1582155SN/A
1592422SN/A    _current_group_tag = ungrouped_tag
1602422SN/A
1612422SN/A    @staticmethod
1622422SN/A    def link_group_tag(group):
1632422SN/A        return 'link group: %s' % group
1642422SN/A
1652422SN/A    @classmethod
1662397SN/A    def set_group(cls, group):
1672397SN/A        new_tag = Source.link_group_tag(group)
1682422SN/A        Source._current_group_tag = new_tag
1692422SN/A        Source.source_groups.add(group)
170955SN/A
171955SN/A    def _add_link_group_tag(self):
172955SN/A        self.tags.add(Source._current_group_tag)
173955SN/A
174955SN/A    '''Add a c/c++ source file to the build'''
175955SN/A    def __init__(self, source, tags=None, add_tags=None):
176955SN/A        '''specify the source file, and any tags'''
177955SN/A        super(Source, self).__init__(source, tags, add_tags)
1781078SN/A        self._add_link_group_tag()
179955SN/A
180955SN/Aclass PySource(SourceFile):
181955SN/A    '''Add a python source file to the named package'''
182955SN/A    invalid_sym_char = re.compile('[^A-z0-9_]')
1831917SN/A    modules = {}
184955SN/A    tnodes = {}
185955SN/A    symnames = {}
186955SN/A
187955SN/A    def __init__(self, package, source, tags=None, add_tags=None):
188974SN/A        '''specify the python package, the source file, and any tags'''
189955SN/A        super(PySource, self).__init__(source, tags, add_tags)
190955SN/A
191955SN/A        modname,ext = self.extname
192955SN/A        assert ext == 'py'
1932566SN/A
1942566SN/A        if package:
195955SN/A            path = package.split('.')
196955SN/A        else:
1972539SN/A            path = []
198955SN/A
199955SN/A        modpath = path[:]
200955SN/A        if modname != '__init__':
2011817SN/A            modpath += [ modname ]
2021154SN/A        modpath = '.'.join(modpath)
2031840SN/A
2042522SN/A        arcpath = path + [ self.basename ]
2052522SN/A        abspath = self.snode.abspath
2062629SN/A        if not exists(abspath):
207955SN/A            abspath = self.tnode.abspath
208955SN/A
209955SN/A        self.package = package
2102539SN/A        self.modname = modname
211955SN/A        self.modpath = modpath
2122539SN/A        self.arcname = joinpath(*arcpath)
213955SN/A        self.abspath = abspath
2141730SN/A        self.compiled = File(self.filename + 'c')
215955SN/A        self.cpp = File(self.filename + '.cc')
2161070SN/A        self.symname = PySource.invalid_sym_char.sub('_', modpath)
217955SN/A
218955SN/A        PySource.modules[modpath] = self
2192212SN/A        PySource.tnodes[self.tnode] = self
220955SN/A        PySource.symnames[self.symname] = self
2211040SN/A
2222507SN/Aclass SimObject(PySource):
2232521SN/A    '''Add a SimObject python file as a python source object and add
2242521SN/A    it to a list of sim object modules'''
2252507SN/A
2262507SN/A    fixed = False
2272507SN/A    modnames = []
2282521SN/A
2292507SN/A    def __init__(self, source, tags=None, add_tags=None):
2302507SN/A        '''Specify the source file and any tags (automatically in
231955SN/A        the m5.objects package)'''
232955SN/A        super(SimObject, self).__init__('m5.objects', source, tags, add_tags)
233955SN/A        if self.fixed:
234955SN/A            raise AttributeError, "Too late to call SimObject now."
235955SN/A
236955SN/A        bisect.insort_right(SimObject.modnames, self.modname)
2371742SN/A
2381742SN/Aclass ProtoBuf(SourceFile):
2391742SN/A    '''Add a Protocol Buffer to build'''
2401742SN/A
2411742SN/A    def __init__(self, source, tags=None, add_tags=None):
2421742SN/A        '''Specify the source file, and any tags'''
2431742SN/A        super(ProtoBuf, self).__init__(source, tags, add_tags)
2441742SN/A
2451742SN/A        # Get the file name and the extension
2461742SN/A        modname,ext = self.extname
2471742SN/A        assert ext == 'proto'
2481742SN/A
2491742SN/A        # Currently, we stick to generating the C++ headers, so we
2501742SN/A        # only need to track the source and header.
2511742SN/A        self.cc_file = File(modname + '.pb.cc')
2521742SN/A        self.hh_file = File(modname + '.pb.h')
2531742SN/A
2541742SN/Aclass UnitTest(object):
2551742SN/A    '''Create a UnitTest'''
2561742SN/A
257955SN/A    all = []
258955SN/A    def __init__(self, target, *sources, **kwargs):
2592520SN/A        '''Specify the target name and any sources.  Sources that are
2602517SN/A        not SourceFiles are evalued with Source().  All files are
2612253SN/A        tagged with the name of the UnitTest target.'''
2622253SN/A
2632253SN/A        srcs = SourceList()
2642253SN/A        for src in sources:
2652553SN/A            if not isinstance(src, SourceFile):
2662553SN/A                src = Source(src, tags=str(target))
2672553SN/A            srcs.append(src)
2682553SN/A
2692507SN/A        self.sources = srcs
2702470SN/A        self.target = target
2711744SN/A        self.main = kwargs.get('main', False)
2721744SN/A        UnitTest.all.append(self)
2732470SN/A
2742470SN/A# Children should have access
2752470SN/AExport('Source')
2762470SN/AExport('PySource')
2772470SN/AExport('SimObject')
2782470SN/AExport('ProtoBuf')
2792400SN/AExport('UnitTest')
2802400SN/A
281955SN/A########################################################################
282955SN/A#
2832037SN/A# Debug Flags
2842037SN/A#
2852037SN/Adebug_flags = {}
2862152SN/Adef DebugFlag(name, desc=None):
2872152SN/A    if name in debug_flags:
2882139SN/A        raise AttributeError, "Flag %s already specified" % name
2892155SN/A    debug_flags[name] = (name, (), desc)
2902155SN/A
2912155SN/Adef CompoundFlag(name, flags, desc=None):
2922155SN/A    if name in debug_flags:
2932155SN/A        raise AttributeError, "Flag %s already specified" % name
2942155SN/A
2952155SN/A    compound = tuple(flags)
2962155SN/A    debug_flags[name] = (name, compound, desc)
297955SN/A
2982155SN/AExport('DebugFlag')
299955SN/AExport('CompoundFlag')
300955SN/A
301955SN/A########################################################################
3021742SN/A#
3031742SN/A# Set some compiler variables
304955SN/A#
305955SN/A
306955SN/A# Include file paths are rooted in this directory.  SCons will
3071858SN/A# automatically expand '.' to refer to both the source directory and
308955SN/A# the corresponding build directory to pick up generated include
3091858SN/A# files.
3101858SN/Aenv.Append(CPPPATH=Dir('.'))
3111858SN/A
3121085SN/Afor extra_dir in extras_dir_list:
313955SN/A    env.Append(CPPPATH=Dir(extra_dir))
314955SN/A
315955SN/A# Workaround for bug in SCons version > 0.97d20071212
316955SN/A# Scons bug id: 2006 gem5 Bug id: 308
317955SN/Afor root, dirs, files in os.walk(base_dir, topdown=True):
318955SN/A    Dir(root[len(base_dir) + 1:])
319955SN/A
320955SN/A########################################################################
321955SN/A#
322955SN/A# Walk the tree and execute all SConscripts in subdirectories
323955SN/A#
324955SN/A
3251511SN/Ahere = Dir('.').srcnode().abspath
3261045SN/Afor root, dirs, files in os.walk(base_dir, topdown=True):
327955SN/A    if root == here:
328955SN/A        # we don't want to recurse back into this SConscript
329955SN/A        continue
330955SN/A
3311108SN/A    if 'SConscript' in files:
332955SN/A        build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
333955SN/A        Source.set_group(build_dir)
334955SN/A        SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
335955SN/A
336955SN/Afor extra_dir in extras_dir_list:
337955SN/A    prefix_len = len(dirname(extra_dir)) + 1
338955SN/A
339955SN/A    # Also add the corresponding build directory to pick up generated
340955SN/A    # include files.
341955SN/A    env.Append(CPPPATH=Dir(joinpath(env['BUILDDIR'], extra_dir[prefix_len:])))
342955SN/A
343955SN/A    for root, dirs, files in os.walk(extra_dir, topdown=True):
344955SN/A        # if build lives in the extras directory, don't walk down it
345955SN/A        if 'build' in dirs:
346955SN/A            dirs.remove('build')
347955SN/A
348955SN/A        if 'SConscript' in files:
349955SN/A            build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
350955SN/A            SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
351955SN/A
352955SN/Afor opt in export_vars:
353955SN/A    env.ConfigFile(opt)
3542655Sstever@eecs.umich.edu
3552655Sstever@eecs.umich.edudef makeTheISA(source, target, env):
3562655Sstever@eecs.umich.edu    isas = [ src.get_contents() for src in source ]
3572655Sstever@eecs.umich.edu    target_isa = env['TARGET_ISA']
3582655Sstever@eecs.umich.edu    def define(isa):
3592655Sstever@eecs.umich.edu        return isa.upper() + '_ISA'
3602655Sstever@eecs.umich.edu
3612655Sstever@eecs.umich.edu    def namespace(isa):
3622655Sstever@eecs.umich.edu        return isa[0].upper() + isa[1:].lower() + 'ISA'
3632655Sstever@eecs.umich.edu
3642655Sstever@eecs.umich.edu
3652655Sstever@eecs.umich.edu    code = code_formatter()
3662655Sstever@eecs.umich.edu    code('''\
3672655Sstever@eecs.umich.edu#ifndef __CONFIG_THE_ISA_HH__
3682655Sstever@eecs.umich.edu#define __CONFIG_THE_ISA_HH__
3692655Sstever@eecs.umich.edu
3702655Sstever@eecs.umich.edu''')
3712655Sstever@eecs.umich.edu
3722655Sstever@eecs.umich.edu    # create defines for the preprocessing and compile-time determination
3732655Sstever@eecs.umich.edu    for i,isa in enumerate(isas):
3742655Sstever@eecs.umich.edu        code('#define $0 $1', define(isa), i + 1)
3752655Sstever@eecs.umich.edu    code()
376955SN/A
3772655Sstever@eecs.umich.edu    # create an enum for any run-time determination of the ISA, we
3782655Sstever@eecs.umich.edu    # reuse the same name as the namespaces
3792655Sstever@eecs.umich.edu    code('enum class Arch {')
380955SN/A    for i,isa in enumerate(isas):
381955SN/A        if i + 1 == len(isas):
3822655Sstever@eecs.umich.edu            code('  $0 = $1', namespace(isa), define(isa))
3832655Sstever@eecs.umich.edu        else:
384955SN/A            code('  $0 = $1,', namespace(isa), define(isa))
385955SN/A    code('};')
3862655Sstever@eecs.umich.edu
3872655Sstever@eecs.umich.edu    code('''
3882655Sstever@eecs.umich.edu
389955SN/A#define THE_ISA ${{define(target_isa)}}
390955SN/A#define TheISA ${{namespace(target_isa)}}
3912655Sstever@eecs.umich.edu#define THE_ISA_STR "${{target_isa}}"
3922655Sstever@eecs.umich.edu
3932655Sstever@eecs.umich.edu#endif // __CONFIG_THE_ISA_HH__''')
3941869SN/A
3951869SN/A    code.write(str(target[0]))
396
397env.Command('config/the_isa.hh', map(Value, all_isa_list),
398            MakeAction(makeTheISA, Transform("CFG ISA", 0)))
399
400def makeTheGPUISA(source, target, env):
401    isas = [ src.get_contents() for src in source ]
402    target_gpu_isa = env['TARGET_GPU_ISA']
403    def define(isa):
404        return isa.upper() + '_ISA'
405
406    def namespace(isa):
407        return isa[0].upper() + isa[1:].lower() + 'ISA'
408
409
410    code = code_formatter()
411    code('''\
412#ifndef __CONFIG_THE_GPU_ISA_HH__
413#define __CONFIG_THE_GPU_ISA_HH__
414
415''')
416
417    # create defines for the preprocessing and compile-time determination
418    for i,isa in enumerate(isas):
419        code('#define $0 $1', define(isa), i + 1)
420    code()
421
422    # create an enum for any run-time determination of the ISA, we
423    # reuse the same name as the namespaces
424    code('enum class GPUArch {')
425    for i,isa in enumerate(isas):
426        if i + 1 == len(isas):
427            code('  $0 = $1', namespace(isa), define(isa))
428        else:
429            code('  $0 = $1,', namespace(isa), define(isa))
430    code('};')
431
432    code('''
433
434#define THE_GPU_ISA ${{define(target_gpu_isa)}}
435#define TheGpuISA ${{namespace(target_gpu_isa)}}
436#define THE_GPU_ISA_STR "${{target_gpu_isa}}"
437
438#endif // __CONFIG_THE_GPU_ISA_HH__''')
439
440    code.write(str(target[0]))
441
442env.Command('config/the_gpu_isa.hh', map(Value, all_gpu_isa_list),
443            MakeAction(makeTheGPUISA, Transform("CFG ISA", 0)))
444
445########################################################################
446#
447# Prevent any SimObjects from being added after this point, they
448# should all have been added in the SConscripts above
449#
450SimObject.fixed = True
451
452class DictImporter(object):
453    '''This importer takes a dictionary of arbitrary module names that
454    map to arbitrary filenames.'''
455    def __init__(self, modules):
456        self.modules = modules
457        self.installed = set()
458
459    def __del__(self):
460        self.unload()
461
462    def unload(self):
463        import sys
464        for module in self.installed:
465            del sys.modules[module]
466        self.installed = set()
467
468    def find_module(self, fullname, path):
469        if fullname == 'm5.defines':
470            return self
471
472        if fullname == 'm5.objects':
473            return self
474
475        if fullname.startswith('_m5'):
476            return None
477
478        source = self.modules.get(fullname, None)
479        if source is not None and fullname.startswith('m5.objects'):
480            return self
481
482        return None
483
484    def load_module(self, fullname):
485        mod = imp.new_module(fullname)
486        sys.modules[fullname] = mod
487        self.installed.add(fullname)
488
489        mod.__loader__ = self
490        if fullname == 'm5.objects':
491            mod.__path__ = fullname.split('.')
492            return mod
493
494        if fullname == 'm5.defines':
495            mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
496            return mod
497
498        source = self.modules[fullname]
499        if source.modname == '__init__':
500            mod.__path__ = source.modpath
501        mod.__file__ = source.abspath
502
503        exec file(source.abspath, 'r') in mod.__dict__
504
505        return mod
506
507import m5.SimObject
508import m5.params
509from m5.util import code_formatter
510
511m5.SimObject.clear()
512m5.params.clear()
513
514# install the python importer so we can grab stuff from the source
515# tree itself.  We can't have SimObjects added after this point or
516# else we won't know about them for the rest of the stuff.
517importer = DictImporter(PySource.modules)
518sys.meta_path[0:0] = [ importer ]
519
520# import all sim objects so we can populate the all_objects list
521# make sure that we're working with a list, then let's sort it
522for modname in SimObject.modnames:
523    exec('from m5.objects import %s' % modname)
524
525# we need to unload all of the currently imported modules so that they
526# will be re-imported the next time the sconscript is run
527importer.unload()
528sys.meta_path.remove(importer)
529
530sim_objects = m5.SimObject.allClasses
531all_enums = m5.params.allEnums
532
533for name,obj in sorted(sim_objects.iteritems()):
534    for param in obj._params.local.values():
535        # load the ptype attribute now because it depends on the
536        # current version of SimObject.allClasses, but when scons
537        # actually uses the value, all versions of
538        # SimObject.allClasses will have been loaded
539        param.ptype
540
541########################################################################
542#
543# calculate extra dependencies
544#
545module_depends = ["m5", "m5.SimObject", "m5.params"]
546depends = [ PySource.modules[dep].snode for dep in module_depends ]
547depends.sort(key = lambda x: x.name)
548
549########################################################################
550#
551# Commands for the basic automatically generated python files
552#
553
554# Generate Python file containing a dict specifying the current
555# buildEnv flags.
556def makeDefinesPyFile(target, source, env):
557    build_env = source[0].get_contents()
558
559    code = code_formatter()
560    code("""
561import _m5.core
562import m5.util
563
564buildEnv = m5.util.SmartDict($build_env)
565
566compileDate = _m5.core.compileDate
567_globals = globals()
568for key,val in _m5.core.__dict__.iteritems():
569    if key.startswith('flag_'):
570        flag = key[5:]
571        _globals[flag] = val
572del _globals
573""")
574    code.write(target[0].abspath)
575
576defines_info = Value(build_env)
577# Generate a file with all of the compile options in it
578env.Command('python/m5/defines.py', defines_info,
579            MakeAction(makeDefinesPyFile, Transform("DEFINES", 0)))
580PySource('m5', 'python/m5/defines.py')
581
582# Generate python file containing info about the M5 source code
583def makeInfoPyFile(target, source, env):
584    code = code_formatter()
585    for src in source:
586        data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
587        code('$src = ${{repr(data)}}')
588    code.write(str(target[0]))
589
590# Generate a file that wraps the basic top level files
591env.Command('python/m5/info.py',
592            [ '#/COPYING', '#/LICENSE', '#/README', ],
593            MakeAction(makeInfoPyFile, Transform("INFO")))
594PySource('m5', 'python/m5/info.py')
595
596########################################################################
597#
598# Create all of the SimObject param headers and enum headers
599#
600
601def createSimObjectParamStruct(target, source, env):
602    assert len(target) == 1 and len(source) == 1
603
604    name = source[0].get_text_contents()
605    obj = sim_objects[name]
606
607    code = code_formatter()
608    obj.cxx_param_decl(code)
609    code.write(target[0].abspath)
610
611def createSimObjectCxxConfig(is_header):
612    def body(target, source, env):
613        assert len(target) == 1 and len(source) == 1
614
615        name = str(source[0].get_contents())
616        obj = sim_objects[name]
617
618        code = code_formatter()
619        obj.cxx_config_param_file(code, is_header)
620        code.write(target[0].abspath)
621    return body
622
623def createEnumStrings(target, source, env):
624    assert len(target) == 1 and len(source) == 2
625
626    name = source[0].get_text_contents()
627    use_python = source[1].read()
628    obj = all_enums[name]
629
630    code = code_formatter()
631    obj.cxx_def(code)
632    if use_python:
633        obj.pybind_def(code)
634    code.write(target[0].abspath)
635
636def createEnumDecls(target, source, env):
637    assert len(target) == 1 and len(source) == 1
638
639    name = source[0].get_text_contents()
640    obj = all_enums[name]
641
642    code = code_formatter()
643    obj.cxx_decl(code)
644    code.write(target[0].abspath)
645
646def createSimObjectPyBindWrapper(target, source, env):
647    name = source[0].get_text_contents()
648    obj = sim_objects[name]
649
650    code = code_formatter()
651    obj.pybind_decl(code)
652    code.write(target[0].abspath)
653
654# Generate all of the SimObject param C++ struct header files
655params_hh_files = []
656for name,simobj in sorted(sim_objects.iteritems()):
657    py_source = PySource.modules[simobj.__module__]
658    extra_deps = [ py_source.tnode ]
659
660    hh_file = File('params/%s.hh' % name)
661    params_hh_files.append(hh_file)
662    env.Command(hh_file, Value(name),
663                MakeAction(createSimObjectParamStruct, Transform("SO PARAM")))
664    env.Depends(hh_file, depends + extra_deps)
665
666# C++ parameter description files
667if GetOption('with_cxx_config'):
668    for name,simobj in sorted(sim_objects.iteritems()):
669        py_source = PySource.modules[simobj.__module__]
670        extra_deps = [ py_source.tnode ]
671
672        cxx_config_hh_file = File('cxx_config/%s.hh' % name)
673        cxx_config_cc_file = File('cxx_config/%s.cc' % name)
674        env.Command(cxx_config_hh_file, Value(name),
675                    MakeAction(createSimObjectCxxConfig(True),
676                    Transform("CXXCPRHH")))
677        env.Command(cxx_config_cc_file, Value(name),
678                    MakeAction(createSimObjectCxxConfig(False),
679                    Transform("CXXCPRCC")))
680        env.Depends(cxx_config_hh_file, depends + extra_deps +
681                    [File('params/%s.hh' % name), File('sim/cxx_config.hh')])
682        env.Depends(cxx_config_cc_file, depends + extra_deps +
683                    [cxx_config_hh_file])
684        Source(cxx_config_cc_file)
685
686    cxx_config_init_cc_file = File('cxx_config/init.cc')
687
688    def createCxxConfigInitCC(target, source, env):
689        assert len(target) == 1 and len(source) == 1
690
691        code = code_formatter()
692
693        for name,simobj in sorted(sim_objects.iteritems()):
694            if not hasattr(simobj, 'abstract') or not simobj.abstract:
695                code('#include "cxx_config/${name}.hh"')
696        code()
697        code('void cxxConfigInit()')
698        code('{')
699        code.indent()
700        for name,simobj in sorted(sim_objects.iteritems()):
701            not_abstract = not hasattr(simobj, 'abstract') or \
702                not simobj.abstract
703            if not_abstract and 'type' in simobj.__dict__:
704                code('cxx_config_directory["${name}"] = '
705                     '${name}CxxConfigParams::makeDirectoryEntry();')
706        code.dedent()
707        code('}')
708        code.write(target[0].abspath)
709
710    py_source = PySource.modules[simobj.__module__]
711    extra_deps = [ py_source.tnode ]
712    env.Command(cxx_config_init_cc_file, Value(name),
713        MakeAction(createCxxConfigInitCC, Transform("CXXCINIT")))
714    cxx_param_hh_files = ["cxx_config/%s.hh" % simobj
715        for name,simobj in sorted(sim_objects.iteritems())
716        if not hasattr(simobj, 'abstract') or not simobj.abstract]
717    Depends(cxx_config_init_cc_file, cxx_param_hh_files +
718            [File('sim/cxx_config.hh')])
719    Source(cxx_config_init_cc_file)
720
721# Generate all enum header files
722for name,enum in sorted(all_enums.iteritems()):
723    py_source = PySource.modules[enum.__module__]
724    extra_deps = [ py_source.tnode ]
725
726    cc_file = File('enums/%s.cc' % name)
727    env.Command(cc_file, [Value(name), Value(env['USE_PYTHON'])],
728                MakeAction(createEnumStrings, Transform("ENUM STR")))
729    env.Depends(cc_file, depends + extra_deps)
730    Source(cc_file)
731
732    hh_file = File('enums/%s.hh' % name)
733    env.Command(hh_file, Value(name),
734                MakeAction(createEnumDecls, Transform("ENUMDECL")))
735    env.Depends(hh_file, depends + extra_deps)
736
737# Generate SimObject Python bindings wrapper files
738if env['USE_PYTHON']:
739    for name,simobj in sorted(sim_objects.iteritems()):
740        py_source = PySource.modules[simobj.__module__]
741        extra_deps = [ py_source.tnode ]
742        cc_file = File('python/_m5/param_%s.cc' % name)
743        env.Command(cc_file, Value(name),
744                    MakeAction(createSimObjectPyBindWrapper,
745                               Transform("SO PyBind")))
746        env.Depends(cc_file, depends + extra_deps)
747        Source(cc_file)
748
749# Build all protocol buffers if we have got protoc and protobuf available
750if env['HAVE_PROTOBUF']:
751    for proto in ProtoBuf.all:
752        # Use both the source and header as the target, and the .proto
753        # file as the source. When executing the protoc compiler, also
754        # specify the proto_path to avoid having the generated files
755        # include the path.
756        env.Command([proto.cc_file, proto.hh_file], proto.tnode,
757                    MakeAction('$PROTOC --cpp_out ${TARGET.dir} '
758                               '--proto_path ${SOURCE.dir} $SOURCE',
759                               Transform("PROTOC")))
760
761        # Add the C++ source file
762        Source(proto.cc_file, tags=proto.tags)
763elif ProtoBuf.all:
764    print 'Got protobuf to build, but lacks support!'
765    Exit(1)
766
767#
768# Handle debug flags
769#
770def makeDebugFlagCC(target, source, env):
771    assert(len(target) == 1 and len(source) == 1)
772
773    code = code_formatter()
774
775    # delay definition of CompoundFlags until after all the definition
776    # of all constituent SimpleFlags
777    comp_code = code_formatter()
778
779    # file header
780    code('''
781/*
782 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
783 */
784
785#include "base/debug.hh"
786
787namespace Debug {
788
789''')
790
791    for name, flag in sorted(source[0].read().iteritems()):
792        n, compound, desc = flag
793        assert n == name
794
795        if not compound:
796            code('SimpleFlag $name("$name", "$desc");')
797        else:
798            comp_code('CompoundFlag $name("$name", "$desc",')
799            comp_code.indent()
800            last = len(compound) - 1
801            for i,flag in enumerate(compound):
802                if i != last:
803                    comp_code('&$flag,')
804                else:
805                    comp_code('&$flag);')
806            comp_code.dedent()
807
808    code.append(comp_code)
809    code()
810    code('} // namespace Debug')
811
812    code.write(str(target[0]))
813
814def makeDebugFlagHH(target, source, env):
815    assert(len(target) == 1 and len(source) == 1)
816
817    val = eval(source[0].get_contents())
818    name, compound, desc = val
819
820    code = code_formatter()
821
822    # file header boilerplate
823    code('''\
824/*
825 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
826 */
827
828#ifndef __DEBUG_${name}_HH__
829#define __DEBUG_${name}_HH__
830
831namespace Debug {
832''')
833
834    if compound:
835        code('class CompoundFlag;')
836    code('class SimpleFlag;')
837
838    if compound:
839        code('extern CompoundFlag $name;')
840        for flag in compound:
841            code('extern SimpleFlag $flag;')
842    else:
843        code('extern SimpleFlag $name;')
844
845    code('''
846}
847
848#endif // __DEBUG_${name}_HH__
849''')
850
851    code.write(str(target[0]))
852
853for name,flag in sorted(debug_flags.iteritems()):
854    n, compound, desc = flag
855    assert n == name
856
857    hh_file = 'debug/%s.hh' % name
858    env.Command(hh_file, Value(flag),
859                MakeAction(makeDebugFlagHH, Transform("TRACING", 0)))
860
861env.Command('debug/flags.cc', Value(debug_flags),
862            MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
863Source('debug/flags.cc')
864
865# version tags
866tags = \
867env.Command('sim/tags.cc', None,
868            MakeAction('util/cpt_upgrader.py --get-cc-file > $TARGET',
869                       Transform("VER TAGS")))
870env.AlwaysBuild(tags)
871
872# Embed python files.  All .py files that have been indicated by a
873# PySource() call in a SConscript need to be embedded into the M5
874# library.  To do that, we compile the file to byte code, marshal the
875# byte code, compress it, and then generate a c++ file that
876# inserts the result into an array.
877def embedPyFile(target, source, env):
878    def c_str(string):
879        if string is None:
880            return "0"
881        return '"%s"' % string
882
883    '''Action function to compile a .py into a code object, marshal
884    it, compress it, and stick it into an asm file so the code appears
885    as just bytes with a label in the data section'''
886
887    src = file(str(source[0]), 'r').read()
888
889    pysource = PySource.tnodes[source[0]]
890    compiled = compile(src, pysource.abspath, 'exec')
891    marshalled = marshal.dumps(compiled)
892    compressed = zlib.compress(marshalled)
893    data = compressed
894    sym = pysource.symname
895
896    code = code_formatter()
897    code('''\
898#include "sim/init.hh"
899
900namespace {
901
902const uint8_t data_${sym}[] = {
903''')
904    code.indent()
905    step = 16
906    for i in xrange(0, len(data), step):
907        x = array.array('B', data[i:i+step])
908        code(''.join('%d,' % d for d in x))
909    code.dedent()
910
911    code('''};
912
913EmbeddedPython embedded_${sym}(
914    ${{c_str(pysource.arcname)}},
915    ${{c_str(pysource.abspath)}},
916    ${{c_str(pysource.modpath)}},
917    data_${sym},
918    ${{len(data)}},
919    ${{len(marshalled)}});
920
921} // anonymous namespace
922''')
923    code.write(str(target[0]))
924
925for source in PySource.all:
926    env.Command(source.cpp, source.tnode,
927                MakeAction(embedPyFile, Transform("EMBED PY")))
928    Source(source.cpp, tags=source.tags, add_tags='python')
929
930########################################################################
931#
932# Define binaries.  Each different build type (debug, opt, etc.) gets
933# a slightly different build environment.
934#
935
936# List of constructed environments to pass back to SConstruct
937date_source = Source('base/date.cc', tags=[])
938
939# Function to create a new build environment as clone of current
940# environment 'env' with modified object suffix and optional stripped
941# binary.  Additional keyword arguments are appended to corresponding
942# build environment vars.
943def makeEnv(env, label, objsfx, strip=False, disable_partial=False, **kwargs):
944    # SCons doesn't know to append a library suffix when there is a '.' in the
945    # name.  Use '_' instead.
946    libname = 'gem5_' + label
947    exename = 'gem5.' + label
948    secondary_exename = 'm5.' + label
949
950    new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
951    new_env.Label = label
952    new_env.Append(**kwargs)
953
954    def make_obj(source, static, extra_deps=None):
955        '''This function creates a scons node of the requested type, and sets
956        up any additional dependencies.'''
957
958        if static:
959            obj = new_env.StaticObject(source.tnode)
960        else:
961            obj = new_env.SharedObject(source.tnode)
962
963        if extra_deps:
964            new_env.Depends(obj, extra_deps)
965
966        return obj
967
968    lib_sources = Source.all.with_tag('gem5 lib')
969
970    # Without Python, leave out all Python content from the library
971    # builds.  The option doesn't affect gem5 built as a program
972    if GetOption('without_python'):
973        lib_sources = lib_sources.without_tag('python')
974
975    static_objs = []
976    shared_objs = []
977
978    for s in lib_sources.with_tag(Source.ungrouped_tag):
979        static_objs.append(make_obj(s, True))
980        shared_objs.append(make_obj(s, False))
981
982    partial_objs = []
983
984    for group in Source.source_groups:
985        srcs = lib_sources.with_tag(Source.link_group_tag(group))
986        if not srcs:
987            continue
988
989        # If partial linking is disabled, add these sources to the build
990        # directly, and short circuit this loop.
991        if disable_partial:
992            for s in srcs:
993                static_objs.append(make_obj(s, True))
994                shared_objs.append(make_obj(s, False))
995            continue
996
997        # Set up the static partially linked objects.
998        source_objs = [ make_obj(s, True) for s in srcs ]
999        file_name = new_env.subst("${OBJPREFIX}lib${OBJSUFFIX}.partial")
1000        target = File(joinpath(group, file_name))
1001        partial = env.PartialStatic(target=target, source=source_objs)
1002        static_objs.append(partial)
1003
1004        # Set up the shared partially linked objects.
1005        source_objs = [ make_obj(s, False) for s in srcs ]
1006        file_name = new_env.subst("${SHOBJPREFIX}lib${SHOBJSUFFIX}.partial")
1007        target = File(joinpath(group, file_name))
1008        partial = env.PartialShared(target=target, source=source_objs)
1009        shared_objs.append(partial)
1010
1011    static_date = make_obj(date_source, static=True, extra_deps=static_objs)
1012    static_objs.append(static_date)
1013
1014    shared_date = make_obj(date_source, static=False, extra_deps=shared_objs)
1015    shared_objs.append(shared_date)
1016
1017    # First make a library of everything but main() so other programs can
1018    # link against m5.
1019    static_lib = new_env.StaticLibrary(libname, static_objs)
1020    shared_lib = new_env.SharedLibrary(libname, shared_objs)
1021
1022    # Now link a stub with main() and the static library.
1023    main_objs = [ make_obj(s, True) for s in Source.all.with_tag('main') ]
1024
1025    for test in UnitTest.all:
1026        test_sources = Source.all.with_tag(str(test.target))
1027        test_objs = [ make_obj(s, static=True) for s in test_sources ]
1028        if test.main:
1029            test_objs += main_objs
1030        path = 'unittest/%s.%s' % (test.target, label)
1031        new_env.Program(path, test_objs + static_objs)
1032
1033    progname = exename
1034    if strip:
1035        progname += '.unstripped'
1036
1037    targets = new_env.Program(progname, main_objs + static_objs)
1038
1039    if strip:
1040        if sys.platform == 'sunos5':
1041            cmd = 'cp $SOURCE $TARGET; strip $TARGET'
1042        else:
1043            cmd = 'strip $SOURCE -o $TARGET'
1044        targets = new_env.Command(exename, progname,
1045                    MakeAction(cmd, Transform("STRIP")))
1046
1047    new_env.Command(secondary_exename, exename,
1048            MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK")))
1049
1050    new_env.M5Binary = targets[0]
1051
1052    # Set up regression tests.
1053    SConscript(os.path.join(env.root.abspath, 'tests', 'SConscript'),
1054               variant_dir=Dir('tests').Dir(new_env.Label),
1055               exports={ 'env' : new_env }, duplicate=False)
1056
1057# Start out with the compiler flags common to all compilers,
1058# i.e. they all use -g for opt and -g -pg for prof
1059ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'],
1060           'perf' : ['-g']}
1061
1062# Start out with the linker flags common to all linkers, i.e. -pg for
1063# prof, and -lprofiler for perf. The -lprofile flag is surrounded by
1064# no-as-needed and as-needed as the binutils linker is too clever and
1065# simply doesn't link to the library otherwise.
1066ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'],
1067           'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']}
1068
1069# For Link Time Optimization, the optimisation flags used to compile
1070# individual files are decoupled from those used at link time
1071# (i.e. you can compile with -O3 and perform LTO with -O0), so we need
1072# to also update the linker flags based on the target.
1073if env['GCC']:
1074    if sys.platform == 'sunos5':
1075        ccflags['debug'] += ['-gstabs+']
1076    else:
1077        ccflags['debug'] += ['-ggdb3']
1078    ldflags['debug'] += ['-O0']
1079    # opt, fast, prof and perf all share the same cc flags, also add
1080    # the optimization to the ldflags as LTO defers the optimization
1081    # to link time
1082    for target in ['opt', 'fast', 'prof', 'perf']:
1083        ccflags[target] += ['-O3']
1084        ldflags[target] += ['-O3']
1085
1086    ccflags['fast'] += env['LTO_CCFLAGS']
1087    ldflags['fast'] += env['LTO_LDFLAGS']
1088elif env['CLANG']:
1089    ccflags['debug'] += ['-g', '-O0']
1090    # opt, fast, prof and perf all share the same cc flags
1091    for target in ['opt', 'fast', 'prof', 'perf']:
1092        ccflags[target] += ['-O3']
1093else:
1094    print 'Unknown compiler, please fix compiler options'
1095    Exit(1)
1096
1097
1098# To speed things up, we only instantiate the build environments we
1099# need.  We try to identify the needed environment for each target; if
1100# we can't, we fall back on instantiating all the environments just to
1101# be safe.
1102target_types = ['debug', 'opt', 'fast', 'prof', 'perf']
1103obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof',
1104              'gpo' : 'perf'}
1105
1106def identifyTarget(t):
1107    ext = t.split('.')[-1]
1108    if ext in target_types:
1109        return ext
1110    if obj2target.has_key(ext):
1111        return obj2target[ext]
1112    match = re.search(r'/tests/([^/]+)/', t)
1113    if match and match.group(1) in target_types:
1114        return match.group(1)
1115    return 'all'
1116
1117needed_envs = [identifyTarget(target) for target in BUILD_TARGETS]
1118if 'all' in needed_envs:
1119    needed_envs += target_types
1120
1121# Debug binary
1122if 'debug' in needed_envs:
1123    makeEnv(env, 'debug', '.do',
1124            CCFLAGS = Split(ccflags['debug']),
1125            CPPDEFINES = ['DEBUG', 'TRACING_ON=1'],
1126            LINKFLAGS = Split(ldflags['debug']))
1127
1128# Optimized binary
1129if 'opt' in needed_envs:
1130    makeEnv(env, 'opt', '.o',
1131            CCFLAGS = Split(ccflags['opt']),
1132            CPPDEFINES = ['TRACING_ON=1'],
1133            LINKFLAGS = Split(ldflags['opt']))
1134
1135# "Fast" binary
1136if 'fast' in needed_envs:
1137    disable_partial = \
1138            env.get('BROKEN_INCREMENTAL_LTO', False) and \
1139            GetOption('force_lto')
1140    makeEnv(env, 'fast', '.fo', strip = True,
1141            CCFLAGS = Split(ccflags['fast']),
1142            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1143            LINKFLAGS = Split(ldflags['fast']),
1144            disable_partial=disable_partial)
1145
1146# Profiled binary using gprof
1147if 'prof' in needed_envs:
1148    makeEnv(env, 'prof', '.po',
1149            CCFLAGS = Split(ccflags['prof']),
1150            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1151            LINKFLAGS = Split(ldflags['prof']))
1152
1153# Profiled binary using google-pprof
1154if 'perf' in needed_envs:
1155    makeEnv(env, 'perf', '.gpo',
1156            CCFLAGS = Split(ccflags['perf']),
1157            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1158            LINKFLAGS = Split(ldflags['perf']))
1159