SConscript revision 12389
12139SN/A# -*- mode:python -*-
22139SN/A
32139SN/A# Copyright (c) 2004-2005 The Regents of The University of Michigan
42139SN/A# All rights reserved.
52139SN/A#
62139SN/A# Redistribution and use in source and binary forms, with or without
72139SN/A# modification, are permitted provided that the following conditions are
82139SN/A# met: redistributions of source code must retain the above copyright
92139SN/A# notice, this list of conditions and the following disclaimer;
102139SN/A# redistributions in binary form must reproduce the above copyright
112139SN/A# notice, this list of conditions and the following disclaimer in the
122139SN/A# documentation and/or other materials provided with the distribution;
132139SN/A# neither the name of the copyright holders nor the names of its
142139SN/A# contributors may be used to endorse or promote products derived from
152139SN/A# this software without specific prior written permission.
162139SN/A#
172139SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
182139SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
192139SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
202139SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
212139SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
222139SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
232139SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
242139SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
252139SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
262139SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
272139SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
282665Ssaidi@eecs.umich.edu#
292665Ssaidi@eecs.umich.edu# Authors: Nathan Binkert
302139SN/A
314202Sbinkertn@umich.eduimport array
328961Sgblack@eecs.umich.eduimport bisect
3310196SCurtis.Dunham@arm.comimport functools
342139SN/Aimport imp
354202Sbinkertn@umich.eduimport marshal
362152SN/Aimport os
372152SN/Aimport re
382139SN/Aimport subprocess
392139SN/Aimport sys
402139SN/Aimport zlib
412139SN/A
422139SN/Afrom os.path import basename, dirname, exists, isdir, isfile, join as joinpath
432152SN/A
442152SN/Aimport SCons
452139SN/A
4612015Sgabeblack@google.comfrom gem5_scons import Transform
4712015Sgabeblack@google.com
489020Sgblack@eecs.umich.edu# This file defines how to build a particular configuration of gem5
494781Snate@binkert.org# based on variable settings in the 'env' build environment.
507799Sgblack@eecs.umich.edu
514781Snate@binkert.orgImport('*')
524781Snate@binkert.org
533170Sstever@eecs.umich.edu# Children need to see the environment
545664Sgblack@eecs.umich.eduExport('env')
558105Sgblack@eecs.umich.edu
566179Sksewell@umich.edubuild_env = [(opt, env[opt]) for opt in export_vars]
574781Snate@binkert.org
5810553Salexandru.dutu@amd.comfrom m5.util import code_formatter, compareVersions
596329Sgblack@eecs.umich.edu
604781Snate@binkert.org########################################################################
614781Snate@binkert.org# Code for adding source files of various types
624781Snate@binkert.org#
634781Snate@binkert.org# When specifying a source file of some type, a set of tags can be
644781Snate@binkert.org# specified for that file.
654781Snate@binkert.org
6612015Sgabeblack@google.comclass SourceFilter(object):
6712015Sgabeblack@google.com    def __init__(self, predicate):
682152SN/A        self.predicate = predicate
6911308Santhony.gutierrez@amd.com
7012016Sgabeblack@google.com    def __or__(self, other):
7112016Sgabeblack@google.com        return SourceFilter(lambda tags: self.predicate(tags) or
7211308Santhony.gutierrez@amd.com                                         other.predicate(tags))
7311696Santhony.gutierrez@amd.com
7411308Santhony.gutierrez@amd.com    def __and__(self, other):
7512016Sgabeblack@google.com        return SourceFilter(lambda tags: self.predicate(tags) and
7612016Sgabeblack@google.com                                         other.predicate(tags))
7711308Santhony.gutierrez@amd.com
782152SN/Adef with_tags_that(predicate):
792152SN/A    '''Return a list of sources with tags that satisfy a predicate.'''
802152SN/A    return SourceFilter(predicate)
812152SN/A
822152SN/Adef with_any_tags(*tags):
832152SN/A    '''Return a list of sources with any of the supplied tags.'''
842152SN/A    return SourceFilter(lambda stags: len(set(tags) & stags) > 0)
852152SN/A
862152SN/Adef with_all_tags(*tags):
872152SN/A    '''Return a list of sources with all of the supplied tags.'''
882152SN/A    return SourceFilter(lambda stags: set(tags) <= stags)
892504SN/A
902504SN/Adef with_tag(tag):
912504SN/A    '''Return a list of sources with the supplied tag.'''
922504SN/A    return SourceFilter(lambda stags: tag in stags)
932152SN/A
942504SN/Adef without_tags(*tags):
952152SN/A    '''Return a list of sources without any of the supplied tags.'''
962152SN/A    return SourceFilter(lambda stags: len(set(tags) & stags) == 0)
972152SN/A
982152SN/Adef without_tag(tag):
992152SN/A    '''Return a list of sources with the supplied tag.'''
1002152SN/A    return SourceFilter(lambda stags: tag not in stags)
1018584Sgblack@eecs.umich.edu
1028584Sgblack@eecs.umich.edusource_filter_factories = {
1036993Snate@binkert.org    'with_tags_that': with_tags_that,
1046993Snate@binkert.org    'with_any_tags': with_any_tags,
1056993Snate@binkert.org    'with_all_tags': with_all_tags,
1068584Sgblack@eecs.umich.edu    'with_tag': with_tag,
10710319SAndreas.Sandberg@ARM.com    'without_tags': without_tags,
10810319SAndreas.Sandberg@ARM.com    'without_tag': without_tag,
10910319SAndreas.Sandberg@ARM.com}
11010319SAndreas.Sandberg@ARM.com
1118584Sgblack@eecs.umich.eduExport(source_filter_factories)
11210196SCurtis.Dunham@arm.com
11310196SCurtis.Dunham@arm.comclass SourceList(list):
11410196SCurtis.Dunham@arm.com    def apply_filter(self, f):
11510196SCurtis.Dunham@arm.com        def match(source):
11610196SCurtis.Dunham@arm.com            return f.predicate(source.tags)
11710196SCurtis.Dunham@arm.com        return SourceList(filter(match, self))
11810196SCurtis.Dunham@arm.com
11910196SCurtis.Dunham@arm.com    def __getattr__(self, name):
12010196SCurtis.Dunham@arm.com        func = source_filter_factories.get(name, None)
12110196SCurtis.Dunham@arm.com        if not func:
12210196SCurtis.Dunham@arm.com            raise AttributeError
12310196SCurtis.Dunham@arm.com
12410196SCurtis.Dunham@arm.com        @functools.wraps(func)
12510196SCurtis.Dunham@arm.com        def wrapper(*args, **kwargs):
12610196SCurtis.Dunham@arm.com            return self.apply_filter(func(*args, **kwargs))
12710196SCurtis.Dunham@arm.com        return wrapper
12810196SCurtis.Dunham@arm.com
12910196SCurtis.Dunham@arm.comclass SourceMeta(type):
13010196SCurtis.Dunham@arm.com    '''Meta class for source files that keeps track of all files of a
13110196SCurtis.Dunham@arm.com    particular type.'''
13210196SCurtis.Dunham@arm.com    def __init__(cls, name, bases, dict):
1336993Snate@binkert.org        super(SourceMeta, cls).__init__(name, bases, dict)
1346993Snate@binkert.org        cls.all = SourceList()
1356993Snate@binkert.org
1366998Snate@binkert.orgclass SourceFile(object):
1376998Snate@binkert.org    '''Base object that encapsulates the notion of a source file.
1386998Snate@binkert.org    This includes, the source node, target node, various manipulations
1397756SAli.Saidi@ARM.com    of those.  A source file also specifies a set of tags which
1406993Snate@binkert.org    describing arbitrary properties of the source file.'''
1416993Snate@binkert.org    __metaclass__ = SourceMeta
1426993Snate@binkert.org
1436993Snate@binkert.org    static_objs = {}
1448585Sgblack@eecs.umich.edu    shared_objs = {}
1458584Sgblack@eecs.umich.edu
14610319SAndreas.Sandberg@ARM.com    def __init__(self, source, tags=None, add_tags=None):
1476993Snate@binkert.org        if tags is None:
1487816Ssteve.reinhardt@amd.com            tags='gem5 lib'
1492152SN/A        if isinstance(tags, basestring):
1502766Sktlim@umich.edu            tags = set([tags])
1512766Sktlim@umich.edu        if not isinstance(tags, set):
1526993Snate@binkert.org            tags = set(tags)
1532152SN/A        self.tags = tags
1542152SN/A
1555944Sgblack@eecs.umich.edu        if add_tags:
15610196SCurtis.Dunham@arm.com            if isinstance(add_tags, basestring):
15710196SCurtis.Dunham@arm.com                add_tags = set([add_tags])
15810196SCurtis.Dunham@arm.com            if not isinstance(add_tags, set):
15910196SCurtis.Dunham@arm.com                add_tags = set(add_tags)
16010196SCurtis.Dunham@arm.com            self.tags |= add_tags
16110196SCurtis.Dunham@arm.com
16210196SCurtis.Dunham@arm.com        tnode = source
16310196SCurtis.Dunham@arm.com        if not isinstance(source, SCons.Node.FS.File):
16410196SCurtis.Dunham@arm.com            tnode = File(source)
16510196SCurtis.Dunham@arm.com
16610196SCurtis.Dunham@arm.com        self.tnode = tnode
16710196SCurtis.Dunham@arm.com        self.snode = tnode.srcnode()
16810196SCurtis.Dunham@arm.com
16910196SCurtis.Dunham@arm.com        for base in type(self).__mro__:
17010196SCurtis.Dunham@arm.com            if issubclass(base, SourceFile):
17110196SCurtis.Dunham@arm.com                base.all.append(self)
17210196SCurtis.Dunham@arm.com
17310196SCurtis.Dunham@arm.com    def static(self, env):
17410196SCurtis.Dunham@arm.com        key = (self.tnode, env['OBJSUFFIX'])
17510196SCurtis.Dunham@arm.com        if not key in self.static_objs:
17610196SCurtis.Dunham@arm.com            self.static_objs[key] = env.StaticObject(self.tnode)
17710196SCurtis.Dunham@arm.com        return self.static_objs[key]
17810196SCurtis.Dunham@arm.com
17910196SCurtis.Dunham@arm.com    def shared(self, env):
18010196SCurtis.Dunham@arm.com        key = (self.tnode, env['OBJSUFFIX'])
18110196SCurtis.Dunham@arm.com        if not key in self.shared_objs:
18210196SCurtis.Dunham@arm.com            self.shared_objs[key] = env.SharedObject(self.tnode)
18310196SCurtis.Dunham@arm.com        return self.shared_objs[key]
18410196SCurtis.Dunham@arm.com
18510196SCurtis.Dunham@arm.com    @property
18610196SCurtis.Dunham@arm.com    def filename(self):
18710196SCurtis.Dunham@arm.com        return str(self.tnode)
18810196SCurtis.Dunham@arm.com
18910196SCurtis.Dunham@arm.com    @property
19010196SCurtis.Dunham@arm.com    def dirname(self):
19110196SCurtis.Dunham@arm.com        return dirname(self.filename)
19210196SCurtis.Dunham@arm.com
19310196SCurtis.Dunham@arm.com    @property
19410196SCurtis.Dunham@arm.com    def basename(self):
19510196SCurtis.Dunham@arm.com        return basename(self.filename)
19610196SCurtis.Dunham@arm.com
19710196SCurtis.Dunham@arm.com    @property
19810196SCurtis.Dunham@arm.com    def extname(self):
19910196SCurtis.Dunham@arm.com        index = self.basename.rfind('.')
20010196SCurtis.Dunham@arm.com        if index <= 0:
20110196SCurtis.Dunham@arm.com            # dot files aren't extensions
20210196SCurtis.Dunham@arm.com            return self.basename, None
2038335Snate@binkert.org
2048335Snate@binkert.org        return self.basename[:index], self.basename[index+1:]
2059920Syasuko.eckert@amd.com
2068335Snate@binkert.org    def __lt__(self, other): return self.filename < other.filename
20710935Snilay@cs.wisc.edu    def __le__(self, other): return self.filename <= other.filename
208    def __gt__(self, other): return self.filename > other.filename
209    def __ge__(self, other): return self.filename >= other.filename
210    def __eq__(self, other): return self.filename == other.filename
211    def __ne__(self, other): return self.filename != other.filename
212
213class Source(SourceFile):
214    ungrouped_tag = 'No link group'
215    source_groups = set()
216
217    _current_group_tag = ungrouped_tag
218
219    @staticmethod
220    def link_group_tag(group):
221        return 'link group: %s' % group
222
223    @classmethod
224    def set_group(cls, group):
225        new_tag = Source.link_group_tag(group)
226        Source._current_group_tag = new_tag
227        Source.source_groups.add(group)
228
229    def _add_link_group_tag(self):
230        self.tags.add(Source._current_group_tag)
231
232    '''Add a c/c++ source file to the build'''
233    def __init__(self, source, tags=None, add_tags=None):
234        '''specify the source file, and any tags'''
235        super(Source, self).__init__(source, tags, add_tags)
236        self._add_link_group_tag()
237
238class PySource(SourceFile):
239    '''Add a python source file to the named package'''
240    invalid_sym_char = re.compile('[^A-z0-9_]')
241    modules = {}
242    tnodes = {}
243    symnames = {}
244
245    def __init__(self, package, source, tags=None, add_tags=None):
246        '''specify the python package, the source file, and any tags'''
247        super(PySource, self).__init__(source, tags, add_tags)
248
249        modname,ext = self.extname
250        assert ext == 'py'
251
252        if package:
253            path = package.split('.')
254        else:
255            path = []
256
257        modpath = path[:]
258        if modname != '__init__':
259            modpath += [ modname ]
260        modpath = '.'.join(modpath)
261
262        arcpath = path + [ self.basename ]
263        abspath = self.snode.abspath
264        if not exists(abspath):
265            abspath = self.tnode.abspath
266
267        self.package = package
268        self.modname = modname
269        self.modpath = modpath
270        self.arcname = joinpath(*arcpath)
271        self.abspath = abspath
272        self.compiled = File(self.filename + 'c')
273        self.cpp = File(self.filename + '.cc')
274        self.symname = PySource.invalid_sym_char.sub('_', modpath)
275
276        PySource.modules[modpath] = self
277        PySource.tnodes[self.tnode] = self
278        PySource.symnames[self.symname] = self
279
280class SimObject(PySource):
281    '''Add a SimObject python file as a python source object and add
282    it to a list of sim object modules'''
283
284    fixed = False
285    modnames = []
286
287    def __init__(self, source, tags=None, add_tags=None):
288        '''Specify the source file and any tags (automatically in
289        the m5.objects package)'''
290        super(SimObject, self).__init__('m5.objects', source, tags, add_tags)
291        if self.fixed:
292            raise AttributeError, "Too late to call SimObject now."
293
294        bisect.insort_right(SimObject.modnames, self.modname)
295
296class ProtoBuf(SourceFile):
297    '''Add a Protocol Buffer to build'''
298
299    def __init__(self, source, tags=None, add_tags=None):
300        '''Specify the source file, and any tags'''
301        super(ProtoBuf, self).__init__(source, tags, add_tags)
302
303        # Get the file name and the extension
304        modname,ext = self.extname
305        assert ext == 'proto'
306
307        # Currently, we stick to generating the C++ headers, so we
308        # only need to track the source and header.
309        self.cc_file = File(modname + '.pb.cc')
310        self.hh_file = File(modname + '.pb.h')
311
312class UnitTest(object):
313    '''Create a UnitTest'''
314
315    all = []
316    def __init__(self, target, *sources, **kwargs):
317        '''Specify the target name and any sources.  Sources that are
318        not SourceFiles are evalued with Source().  All files are
319        tagged with the name of the UnitTest target.'''
320
321        srcs = SourceList()
322        for src in sources:
323            if not isinstance(src, SourceFile):
324                src = Source(src, tags=str(target))
325            srcs.append(src)
326
327        self.sources = srcs
328        self.target = target
329        self.main = kwargs.get('main', False)
330        self.all.append(self)
331
332class GTest(UnitTest):
333    '''Create a unit test based on the google test framework.'''
334    all = []
335    def __init__(self, *args, **kwargs):
336        isFilter = lambda arg: isinstance(arg, SourceFilter)
337        self.filters = filter(isFilter, args)
338        args = filter(lambda a: not isFilter(a), args)
339        super(GTest, self).__init__(*args, **kwargs)
340        self.dir = Dir('.')
341        self.skip_lib = kwargs.pop('skip_lib', False)
342
343# Children should have access
344Export('Source')
345Export('PySource')
346Export('SimObject')
347Export('ProtoBuf')
348Export('UnitTest')
349Export('GTest')
350
351########################################################################
352#
353# Debug Flags
354#
355debug_flags = {}
356def DebugFlag(name, desc=None):
357    if name in debug_flags:
358        raise AttributeError, "Flag %s already specified" % name
359    debug_flags[name] = (name, (), desc)
360
361def CompoundFlag(name, flags, desc=None):
362    if name in debug_flags:
363        raise AttributeError, "Flag %s already specified" % name
364
365    compound = tuple(flags)
366    debug_flags[name] = (name, compound, desc)
367
368Export('DebugFlag')
369Export('CompoundFlag')
370
371########################################################################
372#
373# Set some compiler variables
374#
375
376# Include file paths are rooted in this directory.  SCons will
377# automatically expand '.' to refer to both the source directory and
378# the corresponding build directory to pick up generated include
379# files.
380env.Append(CPPPATH=Dir('.'))
381
382for extra_dir in extras_dir_list:
383    env.Append(CPPPATH=Dir(extra_dir))
384
385# Workaround for bug in SCons version > 0.97d20071212
386# Scons bug id: 2006 gem5 Bug id: 308
387for root, dirs, files in os.walk(base_dir, topdown=True):
388    Dir(root[len(base_dir) + 1:])
389
390########################################################################
391#
392# Walk the tree and execute all SConscripts in subdirectories
393#
394
395here = Dir('.').srcnode().abspath
396for root, dirs, files in os.walk(base_dir, topdown=True):
397    if root == here:
398        # we don't want to recurse back into this SConscript
399        continue
400
401    if 'SConscript' in files:
402        build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
403        Source.set_group(build_dir)
404        SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
405
406for extra_dir in extras_dir_list:
407    prefix_len = len(dirname(extra_dir)) + 1
408
409    # Also add the corresponding build directory to pick up generated
410    # include files.
411    env.Append(CPPPATH=Dir(joinpath(env['BUILDDIR'], extra_dir[prefix_len:])))
412
413    for root, dirs, files in os.walk(extra_dir, topdown=True):
414        # if build lives in the extras directory, don't walk down it
415        if 'build' in dirs:
416            dirs.remove('build')
417
418        if 'SConscript' in files:
419            build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
420            SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
421
422for opt in export_vars:
423    env.ConfigFile(opt)
424
425def makeTheISA(source, target, env):
426    isas = [ src.get_contents() for src in source ]
427    target_isa = env['TARGET_ISA']
428    def define(isa):
429        return isa.upper() + '_ISA'
430
431    def namespace(isa):
432        return isa[0].upper() + isa[1:].lower() + 'ISA'
433
434
435    code = code_formatter()
436    code('''\
437#ifndef __CONFIG_THE_ISA_HH__
438#define __CONFIG_THE_ISA_HH__
439
440''')
441
442    # create defines for the preprocessing and compile-time determination
443    for i,isa in enumerate(isas):
444        code('#define $0 $1', define(isa), i + 1)
445    code()
446
447    # create an enum for any run-time determination of the ISA, we
448    # reuse the same name as the namespaces
449    code('enum class Arch {')
450    for i,isa in enumerate(isas):
451        if i + 1 == len(isas):
452            code('  $0 = $1', namespace(isa), define(isa))
453        else:
454            code('  $0 = $1,', namespace(isa), define(isa))
455    code('};')
456
457    code('''
458
459#define THE_ISA ${{define(target_isa)}}
460#define TheISA ${{namespace(target_isa)}}
461#define THE_ISA_STR "${{target_isa}}"
462
463#endif // __CONFIG_THE_ISA_HH__''')
464
465    code.write(str(target[0]))
466
467env.Command('config/the_isa.hh', map(Value, all_isa_list),
468            MakeAction(makeTheISA, Transform("CFG ISA", 0)))
469
470def makeTheGPUISA(source, target, env):
471    isas = [ src.get_contents() for src in source ]
472    target_gpu_isa = env['TARGET_GPU_ISA']
473    def define(isa):
474        return isa.upper() + '_ISA'
475
476    def namespace(isa):
477        return isa[0].upper() + isa[1:].lower() + 'ISA'
478
479
480    code = code_formatter()
481    code('''\
482#ifndef __CONFIG_THE_GPU_ISA_HH__
483#define __CONFIG_THE_GPU_ISA_HH__
484
485''')
486
487    # create defines for the preprocessing and compile-time determination
488    for i,isa in enumerate(isas):
489        code('#define $0 $1', define(isa), i + 1)
490    code()
491
492    # create an enum for any run-time determination of the ISA, we
493    # reuse the same name as the namespaces
494    code('enum class GPUArch {')
495    for i,isa in enumerate(isas):
496        if i + 1 == len(isas):
497            code('  $0 = $1', namespace(isa), define(isa))
498        else:
499            code('  $0 = $1,', namespace(isa), define(isa))
500    code('};')
501
502    code('''
503
504#define THE_GPU_ISA ${{define(target_gpu_isa)}}
505#define TheGpuISA ${{namespace(target_gpu_isa)}}
506#define THE_GPU_ISA_STR "${{target_gpu_isa}}"
507
508#endif // __CONFIG_THE_GPU_ISA_HH__''')
509
510    code.write(str(target[0]))
511
512env.Command('config/the_gpu_isa.hh', map(Value, all_gpu_isa_list),
513            MakeAction(makeTheGPUISA, Transform("CFG ISA", 0)))
514
515########################################################################
516#
517# Prevent any SimObjects from being added after this point, they
518# should all have been added in the SConscripts above
519#
520SimObject.fixed = True
521
522class DictImporter(object):
523    '''This importer takes a dictionary of arbitrary module names that
524    map to arbitrary filenames.'''
525    def __init__(self, modules):
526        self.modules = modules
527        self.installed = set()
528
529    def __del__(self):
530        self.unload()
531
532    def unload(self):
533        import sys
534        for module in self.installed:
535            del sys.modules[module]
536        self.installed = set()
537
538    def find_module(self, fullname, path):
539        if fullname == 'm5.defines':
540            return self
541
542        if fullname == 'm5.objects':
543            return self
544
545        if fullname.startswith('_m5'):
546            return None
547
548        source = self.modules.get(fullname, None)
549        if source is not None and fullname.startswith('m5.objects'):
550            return self
551
552        return None
553
554    def load_module(self, fullname):
555        mod = imp.new_module(fullname)
556        sys.modules[fullname] = mod
557        self.installed.add(fullname)
558
559        mod.__loader__ = self
560        if fullname == 'm5.objects':
561            mod.__path__ = fullname.split('.')
562            return mod
563
564        if fullname == 'm5.defines':
565            mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
566            return mod
567
568        source = self.modules[fullname]
569        if source.modname == '__init__':
570            mod.__path__ = source.modpath
571        mod.__file__ = source.abspath
572
573        exec file(source.abspath, 'r') in mod.__dict__
574
575        return mod
576
577import m5.SimObject
578import m5.params
579from m5.util import code_formatter
580
581m5.SimObject.clear()
582m5.params.clear()
583
584# install the python importer so we can grab stuff from the source
585# tree itself.  We can't have SimObjects added after this point or
586# else we won't know about them for the rest of the stuff.
587importer = DictImporter(PySource.modules)
588sys.meta_path[0:0] = [ importer ]
589
590# import all sim objects so we can populate the all_objects list
591# make sure that we're working with a list, then let's sort it
592for modname in SimObject.modnames:
593    exec('from m5.objects import %s' % modname)
594
595# we need to unload all of the currently imported modules so that they
596# will be re-imported the next time the sconscript is run
597importer.unload()
598sys.meta_path.remove(importer)
599
600sim_objects = m5.SimObject.allClasses
601all_enums = m5.params.allEnums
602
603for name,obj in sorted(sim_objects.iteritems()):
604    for param in obj._params.local.values():
605        # load the ptype attribute now because it depends on the
606        # current version of SimObject.allClasses, but when scons
607        # actually uses the value, all versions of
608        # SimObject.allClasses will have been loaded
609        param.ptype
610
611########################################################################
612#
613# calculate extra dependencies
614#
615module_depends = ["m5", "m5.SimObject", "m5.params"]
616depends = [ PySource.modules[dep].snode for dep in module_depends ]
617depends.sort(key = lambda x: x.name)
618
619########################################################################
620#
621# Commands for the basic automatically generated python files
622#
623
624# Generate Python file containing a dict specifying the current
625# buildEnv flags.
626def makeDefinesPyFile(target, source, env):
627    build_env = source[0].get_contents()
628
629    code = code_formatter()
630    code("""
631import _m5.core
632import m5.util
633
634buildEnv = m5.util.SmartDict($build_env)
635
636compileDate = _m5.core.compileDate
637_globals = globals()
638for key,val in _m5.core.__dict__.iteritems():
639    if key.startswith('flag_'):
640        flag = key[5:]
641        _globals[flag] = val
642del _globals
643""")
644    code.write(target[0].abspath)
645
646defines_info = Value(build_env)
647# Generate a file with all of the compile options in it
648env.Command('python/m5/defines.py', defines_info,
649            MakeAction(makeDefinesPyFile, Transform("DEFINES", 0)))
650PySource('m5', 'python/m5/defines.py')
651
652# Generate python file containing info about the M5 source code
653def makeInfoPyFile(target, source, env):
654    code = code_formatter()
655    for src in source:
656        data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
657        code('$src = ${{repr(data)}}')
658    code.write(str(target[0]))
659
660# Generate a file that wraps the basic top level files
661env.Command('python/m5/info.py',
662            [ '#/COPYING', '#/LICENSE', '#/README', ],
663            MakeAction(makeInfoPyFile, Transform("INFO")))
664PySource('m5', 'python/m5/info.py')
665
666########################################################################
667#
668# Create all of the SimObject param headers and enum headers
669#
670
671def createSimObjectParamStruct(target, source, env):
672    assert len(target) == 1 and len(source) == 1
673
674    name = source[0].get_text_contents()
675    obj = sim_objects[name]
676
677    code = code_formatter()
678    obj.cxx_param_decl(code)
679    code.write(target[0].abspath)
680
681def createSimObjectCxxConfig(is_header):
682    def body(target, source, env):
683        assert len(target) == 1 and len(source) == 1
684
685        name = str(source[0].get_contents())
686        obj = sim_objects[name]
687
688        code = code_formatter()
689        obj.cxx_config_param_file(code, is_header)
690        code.write(target[0].abspath)
691    return body
692
693def createEnumStrings(target, source, env):
694    assert len(target) == 1 and len(source) == 2
695
696    name = source[0].get_text_contents()
697    use_python = source[1].read()
698    obj = all_enums[name]
699
700    code = code_formatter()
701    obj.cxx_def(code)
702    if use_python:
703        obj.pybind_def(code)
704    code.write(target[0].abspath)
705
706def createEnumDecls(target, source, env):
707    assert len(target) == 1 and len(source) == 1
708
709    name = source[0].get_text_contents()
710    obj = all_enums[name]
711
712    code = code_formatter()
713    obj.cxx_decl(code)
714    code.write(target[0].abspath)
715
716def createSimObjectPyBindWrapper(target, source, env):
717    name = source[0].get_text_contents()
718    obj = sim_objects[name]
719
720    code = code_formatter()
721    obj.pybind_decl(code)
722    code.write(target[0].abspath)
723
724# Generate all of the SimObject param C++ struct header files
725params_hh_files = []
726for name,simobj in sorted(sim_objects.iteritems()):
727    py_source = PySource.modules[simobj.__module__]
728    extra_deps = [ py_source.tnode ]
729
730    hh_file = File('params/%s.hh' % name)
731    params_hh_files.append(hh_file)
732    env.Command(hh_file, Value(name),
733                MakeAction(createSimObjectParamStruct, Transform("SO PARAM")))
734    env.Depends(hh_file, depends + extra_deps)
735
736# C++ parameter description files
737if GetOption('with_cxx_config'):
738    for name,simobj in sorted(sim_objects.iteritems()):
739        py_source = PySource.modules[simobj.__module__]
740        extra_deps = [ py_source.tnode ]
741
742        cxx_config_hh_file = File('cxx_config/%s.hh' % name)
743        cxx_config_cc_file = File('cxx_config/%s.cc' % name)
744        env.Command(cxx_config_hh_file, Value(name),
745                    MakeAction(createSimObjectCxxConfig(True),
746                    Transform("CXXCPRHH")))
747        env.Command(cxx_config_cc_file, Value(name),
748                    MakeAction(createSimObjectCxxConfig(False),
749                    Transform("CXXCPRCC")))
750        env.Depends(cxx_config_hh_file, depends + extra_deps +
751                    [File('params/%s.hh' % name), File('sim/cxx_config.hh')])
752        env.Depends(cxx_config_cc_file, depends + extra_deps +
753                    [cxx_config_hh_file])
754        Source(cxx_config_cc_file)
755
756    cxx_config_init_cc_file = File('cxx_config/init.cc')
757
758    def createCxxConfigInitCC(target, source, env):
759        assert len(target) == 1 and len(source) == 1
760
761        code = code_formatter()
762
763        for name,simobj in sorted(sim_objects.iteritems()):
764            if not hasattr(simobj, 'abstract') or not simobj.abstract:
765                code('#include "cxx_config/${name}.hh"')
766        code()
767        code('void cxxConfigInit()')
768        code('{')
769        code.indent()
770        for name,simobj in sorted(sim_objects.iteritems()):
771            not_abstract = not hasattr(simobj, 'abstract') or \
772                not simobj.abstract
773            if not_abstract and 'type' in simobj.__dict__:
774                code('cxx_config_directory["${name}"] = '
775                     '${name}CxxConfigParams::makeDirectoryEntry();')
776        code.dedent()
777        code('}')
778        code.write(target[0].abspath)
779
780    py_source = PySource.modules[simobj.__module__]
781    extra_deps = [ py_source.tnode ]
782    env.Command(cxx_config_init_cc_file, Value(name),
783        MakeAction(createCxxConfigInitCC, Transform("CXXCINIT")))
784    cxx_param_hh_files = ["cxx_config/%s.hh" % simobj
785        for name,simobj in sorted(sim_objects.iteritems())
786        if not hasattr(simobj, 'abstract') or not simobj.abstract]
787    Depends(cxx_config_init_cc_file, cxx_param_hh_files +
788            [File('sim/cxx_config.hh')])
789    Source(cxx_config_init_cc_file)
790
791# Generate all enum header files
792for name,enum in sorted(all_enums.iteritems()):
793    py_source = PySource.modules[enum.__module__]
794    extra_deps = [ py_source.tnode ]
795
796    cc_file = File('enums/%s.cc' % name)
797    env.Command(cc_file, [Value(name), Value(env['USE_PYTHON'])],
798                MakeAction(createEnumStrings, Transform("ENUM STR")))
799    env.Depends(cc_file, depends + extra_deps)
800    Source(cc_file)
801
802    hh_file = File('enums/%s.hh' % name)
803    env.Command(hh_file, Value(name),
804                MakeAction(createEnumDecls, Transform("ENUMDECL")))
805    env.Depends(hh_file, depends + extra_deps)
806
807# Generate SimObject Python bindings wrapper files
808if env['USE_PYTHON']:
809    for name,simobj in sorted(sim_objects.iteritems()):
810        py_source = PySource.modules[simobj.__module__]
811        extra_deps = [ py_source.tnode ]
812        cc_file = File('python/_m5/param_%s.cc' % name)
813        env.Command(cc_file, Value(name),
814                    MakeAction(createSimObjectPyBindWrapper,
815                               Transform("SO PyBind")))
816        env.Depends(cc_file, depends + extra_deps)
817        Source(cc_file)
818
819# Build all protocol buffers if we have got protoc and protobuf available
820if env['HAVE_PROTOBUF']:
821    for proto in ProtoBuf.all:
822        # Use both the source and header as the target, and the .proto
823        # file as the source. When executing the protoc compiler, also
824        # specify the proto_path to avoid having the generated files
825        # include the path.
826        env.Command([proto.cc_file, proto.hh_file], proto.tnode,
827                    MakeAction('$PROTOC --cpp_out ${TARGET.dir} '
828                               '--proto_path ${SOURCE.dir} $SOURCE',
829                               Transform("PROTOC")))
830
831        # Add the C++ source file
832        Source(proto.cc_file, tags=proto.tags)
833elif ProtoBuf.all:
834    print 'Got protobuf to build, but lacks support!'
835    Exit(1)
836
837#
838# Handle debug flags
839#
840def makeDebugFlagCC(target, source, env):
841    assert(len(target) == 1 and len(source) == 1)
842
843    code = code_formatter()
844
845    # delay definition of CompoundFlags until after all the definition
846    # of all constituent SimpleFlags
847    comp_code = code_formatter()
848
849    # file header
850    code('''
851/*
852 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
853 */
854
855#include "base/debug.hh"
856
857namespace Debug {
858
859''')
860
861    for name, flag in sorted(source[0].read().iteritems()):
862        n, compound, desc = flag
863        assert n == name
864
865        if not compound:
866            code('SimpleFlag $name("$name", "$desc");')
867        else:
868            comp_code('CompoundFlag $name("$name", "$desc",')
869            comp_code.indent()
870            last = len(compound) - 1
871            for i,flag in enumerate(compound):
872                if i != last:
873                    comp_code('&$flag,')
874                else:
875                    comp_code('&$flag);')
876            comp_code.dedent()
877
878    code.append(comp_code)
879    code()
880    code('} // namespace Debug')
881
882    code.write(str(target[0]))
883
884def makeDebugFlagHH(target, source, env):
885    assert(len(target) == 1 and len(source) == 1)
886
887    val = eval(source[0].get_contents())
888    name, compound, desc = val
889
890    code = code_formatter()
891
892    # file header boilerplate
893    code('''\
894/*
895 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
896 */
897
898#ifndef __DEBUG_${name}_HH__
899#define __DEBUG_${name}_HH__
900
901namespace Debug {
902''')
903
904    if compound:
905        code('class CompoundFlag;')
906    code('class SimpleFlag;')
907
908    if compound:
909        code('extern CompoundFlag $name;')
910        for flag in compound:
911            code('extern SimpleFlag $flag;')
912    else:
913        code('extern SimpleFlag $name;')
914
915    code('''
916}
917
918#endif // __DEBUG_${name}_HH__
919''')
920
921    code.write(str(target[0]))
922
923for name,flag in sorted(debug_flags.iteritems()):
924    n, compound, desc = flag
925    assert n == name
926
927    hh_file = 'debug/%s.hh' % name
928    env.Command(hh_file, Value(flag),
929                MakeAction(makeDebugFlagHH, Transform("TRACING", 0)))
930
931env.Command('debug/flags.cc', Value(debug_flags),
932            MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
933Source('debug/flags.cc')
934
935# version tags
936tags = \
937env.Command('sim/tags.cc', None,
938            MakeAction('util/cpt_upgrader.py --get-cc-file > $TARGET',
939                       Transform("VER TAGS")))
940env.AlwaysBuild(tags)
941
942# Embed python files.  All .py files that have been indicated by a
943# PySource() call in a SConscript need to be embedded into the M5
944# library.  To do that, we compile the file to byte code, marshal the
945# byte code, compress it, and then generate a c++ file that
946# inserts the result into an array.
947def embedPyFile(target, source, env):
948    def c_str(string):
949        if string is None:
950            return "0"
951        return '"%s"' % string
952
953    '''Action function to compile a .py into a code object, marshal
954    it, compress it, and stick it into an asm file so the code appears
955    as just bytes with a label in the data section'''
956
957    src = file(str(source[0]), 'r').read()
958
959    pysource = PySource.tnodes[source[0]]
960    compiled = compile(src, pysource.abspath, 'exec')
961    marshalled = marshal.dumps(compiled)
962    compressed = zlib.compress(marshalled)
963    data = compressed
964    sym = pysource.symname
965
966    code = code_formatter()
967    code('''\
968#include "sim/init.hh"
969
970namespace {
971
972const uint8_t data_${sym}[] = {
973''')
974    code.indent()
975    step = 16
976    for i in xrange(0, len(data), step):
977        x = array.array('B', data[i:i+step])
978        code(''.join('%d,' % d for d in x))
979    code.dedent()
980
981    code('''};
982
983EmbeddedPython embedded_${sym}(
984    ${{c_str(pysource.arcname)}},
985    ${{c_str(pysource.abspath)}},
986    ${{c_str(pysource.modpath)}},
987    data_${sym},
988    ${{len(data)}},
989    ${{len(marshalled)}});
990
991} // anonymous namespace
992''')
993    code.write(str(target[0]))
994
995for source in PySource.all:
996    env.Command(source.cpp, source.tnode,
997                MakeAction(embedPyFile, Transform("EMBED PY")))
998    Source(source.cpp, tags=source.tags, add_tags='python')
999
1000########################################################################
1001#
1002# Define binaries.  Each different build type (debug, opt, etc.) gets
1003# a slightly different build environment.
1004#
1005
1006# List of constructed environments to pass back to SConstruct
1007date_source = Source('base/date.cc', tags=[])
1008
1009# Function to create a new build environment as clone of current
1010# environment 'env' with modified object suffix and optional stripped
1011# binary.  Additional keyword arguments are appended to corresponding
1012# build environment vars.
1013def makeEnv(env, label, objsfx, strip=False, disable_partial=False, **kwargs):
1014    # SCons doesn't know to append a library suffix when there is a '.' in the
1015    # name.  Use '_' instead.
1016    libname = 'gem5_' + label
1017    exename = 'gem5.' + label
1018    secondary_exename = 'm5.' + label
1019
1020    new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
1021    new_env.Label = label
1022    new_env.Append(**kwargs)
1023
1024    lib_sources = Source.all.with_tag('gem5 lib')
1025
1026    # Without Python, leave out all Python content from the library
1027    # builds.  The option doesn't affect gem5 built as a program
1028    if GetOption('without_python'):
1029        lib_sources = lib_sources.without_tag('python')
1030
1031    static_objs = []
1032    shared_objs = []
1033
1034    for s in lib_sources.with_tag(Source.ungrouped_tag):
1035        static_objs.append(s.static(new_env))
1036        shared_objs.append(s.shared(new_env))
1037
1038    for group in Source.source_groups:
1039        srcs = lib_sources.with_tag(Source.link_group_tag(group))
1040        if not srcs:
1041            continue
1042
1043        group_static = [ s.static(new_env) for s in srcs ]
1044        group_shared = [ s.shared(new_env) for s in srcs ]
1045
1046        # If partial linking is disabled, add these sources to the build
1047        # directly, and short circuit this loop.
1048        if disable_partial:
1049            static_objs.extend(group_static)
1050            shared_objs.extend(group_shared)
1051            continue
1052
1053        # Set up the static partially linked objects.
1054        file_name = new_env.subst("${OBJPREFIX}lib${OBJSUFFIX}.partial")
1055        target = File(joinpath(group, file_name))
1056        partial = env.PartialStatic(target=target, source=group_static)
1057        static_objs.extend(partial)
1058
1059        # Set up the shared partially linked objects.
1060        file_name = new_env.subst("${SHOBJPREFIX}lib${SHOBJSUFFIX}.partial")
1061        target = File(joinpath(group, file_name))
1062        partial = env.PartialShared(target=target, source=group_shared)
1063        shared_objs.extend(partial)
1064
1065    static_date = date_source.static(new_env)
1066    new_env.Depends(static_date, static_objs)
1067    static_objs.extend(static_date)
1068
1069    shared_date = date_source.shared(new_env)
1070    new_env.Depends(shared_date, shared_objs)
1071    shared_objs.extend(shared_date)
1072
1073    # First make a library of everything but main() so other programs can
1074    # link against m5.
1075    static_lib = new_env.StaticLibrary(libname, static_objs)
1076    shared_lib = new_env.SharedLibrary(libname, shared_objs)
1077
1078    # Now link a stub with main() and the static library.
1079    main_objs = [ s.static(new_env) for s in Source.all.with_tag('main') ]
1080
1081    for test in UnitTest.all:
1082        test_sources = Source.all.with_tag(str(test.target))
1083        test_objs = [ s.static(new_env) for s in test_sources ]
1084        if test.main:
1085            test_objs += main_objs
1086        path = 'unittest/%s.%s' % (test.target, label)
1087        new_env.Program(path, test_objs + static_objs)
1088
1089    gtest_env = new_env.Clone()
1090    gtest_env.Append(LIBS=gtest_env['GTEST_LIBS'])
1091    gtest_env.Append(CPPFLAGS=gtest_env['GTEST_CPPFLAGS'])
1092    gtestlib_sources = Source.all.with_tag('gtest lib')
1093    gtest_out_dir = Dir(new_env['BUILDDIR']).Dir('unittests.%s' % label)
1094    for test in GTest.all:
1095        test_sources = list(test.sources)
1096        if not test.skip_lib:
1097            test_sources += gtestlib_sources
1098        for f in test.filters:
1099            test_sources += Source.all.apply_filter(f)
1100        test_objs = [ s.static(gtest_env) for s in test_sources ]
1101        test_binary = gtest_env.Program(
1102            test.dir.File('%s.%s' % (test.target, label)), test_objs)
1103
1104        AlwaysBuild(gtest_env.Command(
1105            gtest_out_dir.File("%s/%s.xml" % (test.dir, test.target)),
1106            test_binary, "${SOURCES[0]} --gtest_output=xml:${TARGETS[0]}"))
1107
1108    progname = exename
1109    if strip:
1110        progname += '.unstripped'
1111
1112    targets = new_env.Program(progname, main_objs + static_objs)
1113
1114    if strip:
1115        if sys.platform == 'sunos5':
1116            cmd = 'cp $SOURCE $TARGET; strip $TARGET'
1117        else:
1118            cmd = 'strip $SOURCE -o $TARGET'
1119        targets = new_env.Command(exename, progname,
1120                    MakeAction(cmd, Transform("STRIP")))
1121
1122    new_env.Command(secondary_exename, exename,
1123            MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK")))
1124
1125    new_env.M5Binary = targets[0]
1126
1127    # Set up regression tests.
1128    SConscript(os.path.join(env.root.abspath, 'tests', 'SConscript'),
1129               variant_dir=Dir('tests').Dir(new_env.Label),
1130               exports={ 'env' : new_env }, duplicate=False)
1131
1132# Start out with the compiler flags common to all compilers,
1133# i.e. they all use -g for opt and -g -pg for prof
1134ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'],
1135           'perf' : ['-g']}
1136
1137# Start out with the linker flags common to all linkers, i.e. -pg for
1138# prof, and -lprofiler for perf. The -lprofile flag is surrounded by
1139# no-as-needed and as-needed as the binutils linker is too clever and
1140# simply doesn't link to the library otherwise.
1141ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'],
1142           'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']}
1143
1144# For Link Time Optimization, the optimisation flags used to compile
1145# individual files are decoupled from those used at link time
1146# (i.e. you can compile with -O3 and perform LTO with -O0), so we need
1147# to also update the linker flags based on the target.
1148if env['GCC']:
1149    if sys.platform == 'sunos5':
1150        ccflags['debug'] += ['-gstabs+']
1151    else:
1152        ccflags['debug'] += ['-ggdb3']
1153    ldflags['debug'] += ['-O0']
1154    # opt, fast, prof and perf all share the same cc flags, also add
1155    # the optimization to the ldflags as LTO defers the optimization
1156    # to link time
1157    for target in ['opt', 'fast', 'prof', 'perf']:
1158        ccflags[target] += ['-O3']
1159        ldflags[target] += ['-O3']
1160
1161    ccflags['fast'] += env['LTO_CCFLAGS']
1162    ldflags['fast'] += env['LTO_LDFLAGS']
1163elif env['CLANG']:
1164    ccflags['debug'] += ['-g', '-O0']
1165    # opt, fast, prof and perf all share the same cc flags
1166    for target in ['opt', 'fast', 'prof', 'perf']:
1167        ccflags[target] += ['-O3']
1168else:
1169    print 'Unknown compiler, please fix compiler options'
1170    Exit(1)
1171
1172
1173# To speed things up, we only instantiate the build environments we
1174# need.  We try to identify the needed environment for each target; if
1175# we can't, we fall back on instantiating all the environments just to
1176# be safe.
1177target_types = ['debug', 'opt', 'fast', 'prof', 'perf']
1178obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof',
1179              'gpo' : 'perf'}
1180
1181def identifyTarget(t):
1182    ext = t.split('.')[-1]
1183    if ext in target_types:
1184        return ext
1185    if obj2target.has_key(ext):
1186        return obj2target[ext]
1187    match = re.search(r'/tests/([^/]+)/', t)
1188    if match and match.group(1) in target_types:
1189        return match.group(1)
1190    return 'all'
1191
1192needed_envs = [identifyTarget(target) for target in BUILD_TARGETS]
1193if 'all' in needed_envs:
1194    needed_envs += target_types
1195
1196# Debug binary
1197if 'debug' in needed_envs:
1198    makeEnv(env, 'debug', '.do',
1199            CCFLAGS = Split(ccflags['debug']),
1200            CPPDEFINES = ['DEBUG', 'TRACING_ON=1'],
1201            LINKFLAGS = Split(ldflags['debug']))
1202
1203# Optimized binary
1204if 'opt' in needed_envs:
1205    makeEnv(env, 'opt', '.o',
1206            CCFLAGS = Split(ccflags['opt']),
1207            CPPDEFINES = ['TRACING_ON=1'],
1208            LINKFLAGS = Split(ldflags['opt']))
1209
1210# "Fast" binary
1211if 'fast' in needed_envs:
1212    disable_partial = \
1213            env.get('BROKEN_INCREMENTAL_LTO', False) and \
1214            GetOption('force_lto')
1215    makeEnv(env, 'fast', '.fo', strip = True,
1216            CCFLAGS = Split(ccflags['fast']),
1217            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1218            LINKFLAGS = Split(ldflags['fast']),
1219            disable_partial=disable_partial)
1220
1221# Profiled binary using gprof
1222if 'prof' in needed_envs:
1223    makeEnv(env, 'prof', '.po',
1224            CCFLAGS = Split(ccflags['prof']),
1225            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1226            LINKFLAGS = Split(ldflags['prof']))
1227
1228# Profiled binary using google-pprof
1229if 'perf' in needed_envs:
1230    makeEnv(env, 'perf', '.gpo',
1231            CCFLAGS = Split(ccflags['perf']),
1232            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1233            LINKFLAGS = Split(ldflags['perf']))
1234