SConscript revision 13576
12689Sktlim@umich.edu# -*- mode:python -*-
22689Sktlim@umich.edu
32689Sktlim@umich.edu# Copyright (c) 2018 ARM Limited
42689Sktlim@umich.edu#
52689Sktlim@umich.edu# The license below extends only to copyright in the software and shall
62689Sktlim@umich.edu# not be construed as granting a license to any other intellectual
72689Sktlim@umich.edu# property including but not limited to intellectual property relating
82689Sktlim@umich.edu# to a hardware implementation of the functionality of the software
92689Sktlim@umich.edu# licensed hereunder.  You may use the software subject to the license
102689Sktlim@umich.edu# terms below provided that you ensure that this notice is replicated
112689Sktlim@umich.edu# unmodified and in its entirety in all distributions of the software,
122689Sktlim@umich.edu# modified or unmodified, in source code or in binary form.
132689Sktlim@umich.edu#
142689Sktlim@umich.edu# Copyright (c) 2004-2005 The Regents of The University of Michigan
152689Sktlim@umich.edu# All rights reserved.
162689Sktlim@umich.edu#
172689Sktlim@umich.edu# Redistribution and use in source and binary forms, with or without
182689Sktlim@umich.edu# modification, are permitted provided that the following conditions are
192689Sktlim@umich.edu# met: redistributions of source code must retain the above copyright
202689Sktlim@umich.edu# notice, this list of conditions and the following disclaimer;
212689Sktlim@umich.edu# redistributions in binary form must reproduce the above copyright
222689Sktlim@umich.edu# notice, this list of conditions and the following disclaimer in the
232689Sktlim@umich.edu# documentation and/or other materials provided with the distribution;
242689Sktlim@umich.edu# neither the name of the copyright holders nor the names of its
252689Sktlim@umich.edu# contributors may be used to endorse or promote products derived from
262689Sktlim@umich.edu# this software without specific prior written permission.
272689Sktlim@umich.edu#
282689Sktlim@umich.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
292689Sktlim@umich.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
302689Sktlim@umich.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
312689Sktlim@umich.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
322689Sktlim@umich.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
332689Sktlim@umich.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
342521SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
351070SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
361070SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
372521SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
382680Sktlim@umich.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
392521SN/A#
402522SN/A# Authors: Nathan Binkert
411711SN/A
422037SN/Afrom __future__ import print_function
4356SN/A
442378SN/Aimport array
452521SN/Aimport bisect
463536Sgblack@eecs.umich.eduimport functools
472378SN/Aimport imp
482378SN/Aimport marshal
492SN/Aimport os
502SN/Aimport re
512107SN/Aimport subprocess
522SN/Aimport sys
532SN/Aimport zlib
542SN/A
552SN/Afrom os.path import basename, dirname, exists, isdir, isfile, join as joinpath
562SN/A
571070SN/Aimport SCons
582378SN/A
592378SN/Afrom gem5_scons import Transform
602521SN/A
612640Sstever@eecs.umich.edu# This file defines how to build a particular configuration of gem5
622640Sstever@eecs.umich.edu# based on variable settings in the 'env' build environment.
632378SN/A
642378SN/AImport('*')
652378SN/A
662902Ssaidi@eecs.umich.edu# Children need to see the environment
672SN/AExport('env')
681070SN/A
691070SN/Abuild_env = [(opt, env[opt]) for opt in export_vars]
701070SN/A
712378SN/Afrom m5.util import code_formatter, compareVersions
721070SN/A
731074SN/A########################################################################
741070SN/A# Code for adding source files of various types
752520SN/A#
762520SN/A# When specifying a source file of some type, a set of tags can be
772520SN/A# specified for that file.
782520SN/A
792520SN/Aclass SourceFilter(object):
802520SN/A    def __init__(self, predicate):
812520SN/A        self.predicate = predicate
822520SN/A
832520SN/A    def __or__(self, other):
842521SN/A        return SourceFilter(lambda tags: self.predicate(tags) or
852521SN/A                                         other.predicate(tags))
862521SN/A
872521SN/A    def __and__(self, other):
882520SN/A        return SourceFilter(lambda tags: self.predicate(tags) and
891070SN/A                                         other.predicate(tags))
902158SN/A
911070SN/Adef with_tags_that(predicate):
921070SN/A    '''Return a list of sources with tags that satisfy a predicate.'''
932158SN/A    return SourceFilter(predicate)
941070SN/A
952158SN/Adef with_any_tags(*tags):
961070SN/A    '''Return a list of sources with any of the supplied tags.'''
971070SN/A    return SourceFilter(lambda stags: len(set(tags) & stags) > 0)
982520SN/A
991070SN/Adef with_all_tags(*tags):
1001070SN/A    '''Return a list of sources with all of the supplied tags.'''
1011070SN/A    return SourceFilter(lambda stags: set(tags) <= stags)
1021070SN/A
1031070SN/Adef with_tag(tag):
1041070SN/A    '''Return a list of sources with the supplied tag.'''
1051070SN/A    return SourceFilter(lambda stags: tag in stags)
1061070SN/A
1071070SN/Adef without_tags(*tags):
1081070SN/A    '''Return a list of sources without any of the supplied tags.'''
1091070SN/A    return SourceFilter(lambda stags: len(set(tags) & stags) == 0)
1101070SN/A
1111070SN/Adef without_tag(tag):
1121082SN/A    '''Return a list of sources with the supplied tag.'''
1131074SN/A    return SourceFilter(lambda stags: tag not in stags)
1141074SN/A
1151074SN/Asource_filter_factories = {
1161074SN/A    'with_tags_that': with_tags_that,
1171074SN/A    'with_any_tags': with_any_tags,
1181070SN/A    'with_all_tags': with_all_tags,
1191070SN/A    'with_tag': with_tag,
1201070SN/A    'without_tags': without_tags,
1211070SN/A    'without_tag': without_tag,
1222378SN/A}
1232378SN/A
1241070SN/AExport(source_filter_factories)
125878SN/A
1262SN/Aclass SourceList(list):
1272SN/A    def apply_filter(self, f):
1282SN/A        def match(source):
1292SN/A            return f.predicate(source.tags)
1302378SN/A        return SourceList(filter(match, self))
1311070SN/A
1321070SN/A    def __getattr__(self, name):
1332378SN/A        func = source_filter_factories.get(name, None)
1342378SN/A        if not func:
1352378SN/A            raise AttributeError
1362378SN/A
1372SN/A        @functools.wraps(func)
1382SN/A        def wrapper(*args, **kwargs):
1392378SN/A            return self.apply_filter(func(*args, **kwargs))
1401885SN/A        return wrapper
1412SN/A
1421808SN/Aclass SourceMeta(type):
1431808SN/A    '''Meta class for source files that keeps track of all files of a
1442378SN/A    particular type.'''
1452378SN/A    def __init__(cls, name, bases, dict):
1462901Ssaidi@eecs.umich.edu        super(SourceMeta, cls).__init__(name, bases, dict)
1472901Ssaidi@eecs.umich.edu        cls.all = SourceList()
1482901Ssaidi@eecs.umich.edu
1492901Ssaidi@eecs.umich.educlass SourceFile(object):
1502901Ssaidi@eecs.umich.edu    '''Base object that encapsulates the notion of a source file.
1512901Ssaidi@eecs.umich.edu    This includes, the source node, target node, various manipulations
1522901Ssaidi@eecs.umich.edu    of those.  A source file also specifies a set of tags which
1532901Ssaidi@eecs.umich.edu    describing arbitrary properties of the source file.'''
154180SN/A    __metaclass__ = SourceMeta
1552680Sktlim@umich.edu
1562SN/A    static_objs = {}
1571806SN/A    shared_objs = {}
1582680Sktlim@umich.edu
1592680Sktlim@umich.edu    def __init__(self, source, tags=None, add_tags=None):
1601806SN/A        if tags is None:
1611806SN/A            tags='gem5 lib'
1621806SN/A        if isinstance(tags, basestring):
1631806SN/A            tags = set([tags])
1642680Sktlim@umich.edu        if not isinstance(tags, set):
1652680Sktlim@umich.edu            tags = set(tags)
1661806SN/A        self.tags = tags
1672680Sktlim@umich.edu
1681806SN/A        if add_tags:
1691806SN/A            if isinstance(add_tags, basestring):
1702680Sktlim@umich.edu                add_tags = set([add_tags])
1711806SN/A            if not isinstance(add_tags, set):
1721070SN/A                add_tags = set(add_tags)
1732378SN/A            self.tags |= add_tags
1742680Sktlim@umich.edu
1751806SN/A        tnode = source
1761070SN/A        if not isinstance(source, SCons.Node.FS.File):
1771070SN/A            tnode = File(source)
1781070SN/A
1791070SN/A        self.tnode = tnode
1801070SN/A        self.snode = tnode.srcnode()
1811808SN/A
1821808SN/A        for base in type(self).__mro__:
1831070SN/A            if issubclass(base, SourceFile):
1841806SN/A                base.all.append(self)
1851806SN/A
1861070SN/A    def static(self, env):
1871070SN/A        key = (self.tnode, env['OBJSUFFIX'])
1881806SN/A        if not key in self.static_objs:
1892378SN/A            self.static_objs[key] = env.StaticObject(self.tnode)
1901070SN/A        return self.static_objs[key]
1911806SN/A
192180SN/A    def shared(self, env):
19375SN/A        key = (self.tnode, env['OBJSUFFIX'])
194180SN/A        if not key in self.shared_objs:
1951129SN/A            self.shared_objs[key] = env.SharedObject(self.tnode)
1961129SN/A        return self.shared_objs[key]
1972114SN/A
1982680Sktlim@umich.edu    @property
1992680Sktlim@umich.edu    def filename(self):
2001129SN/A        return str(self.tnode)
2011129SN/A
2021129SN/A    @property
2032680Sktlim@umich.edu    def dirname(self):
204180SN/A        return dirname(self.filename)
2052680Sktlim@umich.edu
2062680Sktlim@umich.edu    @property
2072680Sktlim@umich.edu    def basename(self):
208180SN/A        return basename(self.filename)
209180SN/A
2102680Sktlim@umich.edu    @property
2112378SN/A    def extname(self):
2122680Sktlim@umich.edu        index = self.basename.rfind('.')
2132378SN/A        if index <= 0:
2142SN/A            # dot files aren't extensions
2152SN/A            return self.basename, None
2162378SN/A
2172378SN/A        return self.basename[:index], self.basename[index+1:]
2182378SN/A
2192378SN/A    def __lt__(self, other): return self.filename < other.filename
2202378SN/A    def __le__(self, other): return self.filename <= other.filename
2212378SN/A    def __gt__(self, other): return self.filename > other.filename
2223162Ssaidi@eecs.umich.edu    def __ge__(self, other): return self.filename >= other.filename
2233162Ssaidi@eecs.umich.edu    def __eq__(self, other): return self.filename == other.filename
2242378SN/A    def __ne__(self, other): return self.filename != other.filename
2252378SN/A
2262378SN/Adef blobToCpp(data, symbol, cpp_code, hpp_code=None, namespace=None):
2272378SN/A    '''
2281070SN/A    Convert bytes data into C++ .cpp and .hh uint8_t byte array
2291070SN/A    code containing that binary data.
2301070SN/A
2312378SN/A    :param data: binary data to be converted to C++
2321984SN/A    :param symbol: name of the symbol
2332378SN/A    :param cpp_code: append the generated cpp_code to this object
2341070SN/A    :param hpp_code: append the generated hpp_code to this object
2351070SN/A                     If None, ignore it. Otherwise, also include it
2361070SN/A                     in the .cpp file.
2371070SN/A    :param namespace: namespace to put the symbol into. If None,
2381070SN/A                      don't put the symbols into any namespace.
2391070SN/A    '''
2402378SN/A    symbol_len_declaration = 'const std::size_t {}_len'.format(symbol)
2411984SN/A    symbol_declaration = 'const std::uint8_t {}[]'.format(symbol)
2422378SN/A    if hpp_code is not None:
2431070SN/A        cpp_code('''\
2442SN/A#include "blobs/{}.hh"
2452SN/A'''.format(symbol))
2462SN/A        hpp_code('''\
2472SN/A#include <cstddef>
2482SN/A#include <cstdint>
2492SN/A''')
2502SN/A        if namespace is not None:
2512SN/A            hpp_code('namespace {} {{'.format(namespace))
2522SN/A        hpp_code('extern ' + symbol_len_declaration + ';')
2532SN/A        hpp_code('extern ' + symbol_declaration + ';')
2542SN/A        if namespace is not None:
2552SN/A            hpp_code('}')
2562SN/A    if namespace is not None:
2572SN/A        cpp_code('namespace {} {{'.format(namespace))
2582SN/A    cpp_code(symbol_len_declaration + ' = {};'.format(len(data)))
2592SN/A    cpp_code(symbol_declaration + ' = {')
2602SN/A    cpp_code.indent()
2612SN/A    step = 16
2622902Ssaidi@eecs.umich.edu    for i in xrange(0, len(data), step):
2632902Ssaidi@eecs.umich.edu        x = array.array('B', data[i:i+step])
2642902Ssaidi@eecs.umich.edu        cpp_code(''.join('%d,' % d for d in x))
2652424SN/A    cpp_code.dedent()
2662424SN/A    cpp_code('};')
2672424SN/A    if namespace is not None:
2682424SN/A        cpp_code('}')
2692424SN/A
2702158SN/Adef Blob(blob_path, symbol):
2712SN/A    '''
2722424SN/A    Embed an arbitrary blob into the gem5 executable,
2732424SN/A    and make it accessible to C++ as a byte array.
2742424SN/A    '''
2752424SN/A    blob_path = os.path.abspath(blob_path)
2762522SN/A    blob_out_dir = os.path.join(env['BUILDDIR'], 'blobs')
2772902Ssaidi@eecs.umich.edu    path_noext = joinpath(blob_out_dir, symbol)
2782424SN/A    cpp_path = path_noext + '.cc'
2792424SN/A    hpp_path = path_noext + '.hh'
2802424SN/A    def embedBlob(target, source, env):
2812424SN/A        data = file(str(source[0]), 'r').read()
2822424SN/A        cpp_code = code_formatter()
2832902Ssaidi@eecs.umich.edu        hpp_code = code_formatter()
2842902Ssaidi@eecs.umich.edu        blobToCpp(data, symbol, cpp_code, hpp_code, namespace='Blobs')
2852902Ssaidi@eecs.umich.edu        cpp_path = str(target[0])
2862424SN/A        hpp_path = str(target[1])
2872424SN/A        cpp_dir = os.path.split(cpp_path)[0]
2882424SN/A        if not os.path.exists(cpp_dir):
2892424SN/A            os.makedirs(cpp_dir)
2902424SN/A        cpp_code.write(cpp_path)
2912424SN/A        hpp_code.write(hpp_path)
2922424SN/A    env.Command([cpp_path, hpp_path], blob_path,
2932424SN/A                MakeAction(embedBlob, Transform("EMBED BLOB")))
2942902Ssaidi@eecs.umich.edu    Source(cpp_path)
2952424SN/A
2962424SN/Aclass Source(SourceFile):
2972424SN/A    ungrouped_tag = 'No link group'
2982424SN/A    source_groups = set()
2992424SN/A
3002424SN/A    _current_group_tag = ungrouped_tag
301
302    @staticmethod
303    def link_group_tag(group):
304        return 'link group: %s' % group
305
306    @classmethod
307    def set_group(cls, group):
308        new_tag = Source.link_group_tag(group)
309        Source._current_group_tag = new_tag
310        Source.source_groups.add(group)
311
312    def _add_link_group_tag(self):
313        self.tags.add(Source._current_group_tag)
314
315    '''Add a c/c++ source file to the build'''
316    def __init__(self, source, tags=None, add_tags=None):
317        '''specify the source file, and any tags'''
318        super(Source, self).__init__(source, tags, add_tags)
319        self._add_link_group_tag()
320
321class PySource(SourceFile):
322    '''Add a python source file to the named package'''
323    invalid_sym_char = re.compile('[^A-z0-9_]')
324    modules = {}
325    tnodes = {}
326    symnames = {}
327
328    def __init__(self, package, source, tags=None, add_tags=None):
329        '''specify the python package, the source file, and any tags'''
330        super(PySource, self).__init__(source, tags, add_tags)
331
332        modname,ext = self.extname
333        assert ext == 'py'
334
335        if package:
336            path = package.split('.')
337        else:
338            path = []
339
340        modpath = path[:]
341        if modname != '__init__':
342            modpath += [ modname ]
343        modpath = '.'.join(modpath)
344
345        arcpath = path + [ self.basename ]
346        abspath = self.snode.abspath
347        if not exists(abspath):
348            abspath = self.tnode.abspath
349
350        self.package = package
351        self.modname = modname
352        self.modpath = modpath
353        self.arcname = joinpath(*arcpath)
354        self.abspath = abspath
355        self.compiled = File(self.filename + 'c')
356        self.cpp = File(self.filename + '.cc')
357        self.symname = PySource.invalid_sym_char.sub('_', modpath)
358
359        PySource.modules[modpath] = self
360        PySource.tnodes[self.tnode] = self
361        PySource.symnames[self.symname] = self
362
363class SimObject(PySource):
364    '''Add a SimObject python file as a python source object and add
365    it to a list of sim object modules'''
366
367    fixed = False
368    modnames = []
369
370    def __init__(self, source, tags=None, add_tags=None):
371        '''Specify the source file and any tags (automatically in
372        the m5.objects package)'''
373        super(SimObject, self).__init__('m5.objects', source, tags, add_tags)
374        if self.fixed:
375            raise AttributeError, "Too late to call SimObject now."
376
377        bisect.insort_right(SimObject.modnames, self.modname)
378
379class ProtoBuf(SourceFile):
380    '''Add a Protocol Buffer to build'''
381
382    def __init__(self, source, tags=None, add_tags=None):
383        '''Specify the source file, and any tags'''
384        super(ProtoBuf, self).__init__(source, tags, add_tags)
385
386        # Get the file name and the extension
387        modname,ext = self.extname
388        assert ext == 'proto'
389
390        # Currently, we stick to generating the C++ headers, so we
391        # only need to track the source and header.
392        self.cc_file = File(modname + '.pb.cc')
393        self.hh_file = File(modname + '.pb.h')
394
395
396exectuable_classes = []
397class ExecutableMeta(type):
398    '''Meta class for Executables.'''
399    all = []
400
401    def __init__(cls, name, bases, d):
402        if not d.pop('abstract', False):
403            ExecutableMeta.all.append(cls)
404        super(ExecutableMeta, cls).__init__(name, bases, d)
405
406        cls.all = []
407
408class Executable(object):
409    '''Base class for creating an executable from sources.'''
410    __metaclass__ = ExecutableMeta
411
412    abstract = True
413
414    def __init__(self, target, *srcs_and_filts):
415        '''Specify the target name and any sources. Sources that are
416        not SourceFiles are evalued with Source().'''
417        super(Executable, self).__init__()
418        self.all.append(self)
419        self.target = target
420
421        isFilter = lambda arg: isinstance(arg, SourceFilter)
422        self.filters = filter(isFilter, srcs_and_filts)
423        sources = filter(lambda a: not isFilter(a), srcs_and_filts)
424
425        srcs = SourceList()
426        for src in sources:
427            if not isinstance(src, SourceFile):
428                src = Source(src, tags=[])
429            srcs.append(src)
430
431        self.sources = srcs
432        self.dir = Dir('.')
433
434    def path(self, env):
435        return self.dir.File(self.target + '.' + env['EXE_SUFFIX'])
436
437    def srcs_to_objs(self, env, sources):
438        return list([ s.static(env) for s in sources ])
439
440    @classmethod
441    def declare_all(cls, env):
442        return list([ instance.declare(env) for instance in cls.all ])
443
444    def declare(self, env, objs=None):
445        if objs is None:
446            objs = self.srcs_to_objs(env, self.sources)
447
448        if env['STRIP_EXES']:
449            stripped = self.path(env)
450            unstripped = env.File(str(stripped) + '.unstripped')
451            if sys.platform == 'sunos5':
452                cmd = 'cp $SOURCE $TARGET; strip $TARGET'
453            else:
454                cmd = 'strip $SOURCE -o $TARGET'
455            env.Program(unstripped, objs)
456            return env.Command(stripped, unstripped,
457                               MakeAction(cmd, Transform("STRIP")))
458        else:
459            return env.Program(self.path(env), objs)
460
461class UnitTest(Executable):
462    '''Create a UnitTest'''
463    def __init__(self, target, *srcs_and_filts, **kwargs):
464        super(UnitTest, self).__init__(target, *srcs_and_filts)
465
466        self.main = kwargs.get('main', False)
467
468    def declare(self, env):
469        sources = list(self.sources)
470        for f in self.filters:
471            sources = Source.all.apply_filter(f)
472        objs = self.srcs_to_objs(env, sources) + env['STATIC_OBJS']
473        if self.main:
474            objs += env['MAIN_OBJS']
475        return super(UnitTest, self).declare(env, objs)
476
477class GTest(Executable):
478    '''Create a unit test based on the google test framework.'''
479    all = []
480    def __init__(self, *srcs_and_filts, **kwargs):
481        super(GTest, self).__init__(*srcs_and_filts)
482
483        self.skip_lib = kwargs.pop('skip_lib', False)
484
485    @classmethod
486    def declare_all(cls, env):
487        env = env.Clone()
488        env.Append(LIBS=env['GTEST_LIBS'])
489        env.Append(CPPFLAGS=env['GTEST_CPPFLAGS'])
490        env['GTEST_LIB_SOURCES'] = Source.all.with_tag('gtest lib')
491        env['GTEST_OUT_DIR'] = \
492            Dir(env['BUILDDIR']).Dir('unittests.' + env['EXE_SUFFIX'])
493        return super(GTest, cls).declare_all(env)
494
495    def declare(self, env):
496        sources = list(self.sources)
497        if not self.skip_lib:
498            sources += env['GTEST_LIB_SOURCES']
499        for f in self.filters:
500            sources += Source.all.apply_filter(f)
501        objs = self.srcs_to_objs(env, sources)
502
503        binary = super(GTest, self).declare(env, objs)
504
505        out_dir = env['GTEST_OUT_DIR']
506        xml_file = out_dir.Dir(str(self.dir)).File(self.target + '.xml')
507        AlwaysBuild(env.Command(xml_file, binary,
508            "${SOURCES[0]} --gtest_output=xml:${TARGETS[0]}"))
509
510        return binary
511
512class Gem5(Executable):
513    '''Create a gem5 executable.'''
514
515    def __init__(self, target):
516        super(Gem5, self).__init__(target)
517
518    def declare(self, env):
519        objs = env['MAIN_OBJS'] + env['STATIC_OBJS']
520        return super(Gem5, self).declare(env, objs)
521
522
523# Children should have access
524Export('Blob')
525Export('Source')
526Export('PySource')
527Export('SimObject')
528Export('ProtoBuf')
529Export('Executable')
530Export('UnitTest')
531Export('GTest')
532
533########################################################################
534#
535# Debug Flags
536#
537debug_flags = {}
538def DebugFlag(name, desc=None):
539    if name in debug_flags:
540        raise AttributeError, "Flag %s already specified" % name
541    debug_flags[name] = (name, (), desc)
542
543def CompoundFlag(name, flags, desc=None):
544    if name in debug_flags:
545        raise AttributeError, "Flag %s already specified" % name
546
547    compound = tuple(flags)
548    debug_flags[name] = (name, compound, desc)
549
550Export('DebugFlag')
551Export('CompoundFlag')
552
553########################################################################
554#
555# Set some compiler variables
556#
557
558# Include file paths are rooted in this directory.  SCons will
559# automatically expand '.' to refer to both the source directory and
560# the corresponding build directory to pick up generated include
561# files.
562env.Append(CPPPATH=Dir('.'))
563
564for extra_dir in extras_dir_list:
565    env.Append(CPPPATH=Dir(extra_dir))
566
567# Workaround for bug in SCons version > 0.97d20071212
568# Scons bug id: 2006 gem5 Bug id: 308
569for root, dirs, files in os.walk(base_dir, topdown=True):
570    Dir(root[len(base_dir) + 1:])
571
572########################################################################
573#
574# Walk the tree and execute all SConscripts in subdirectories
575#
576
577here = Dir('.').srcnode().abspath
578for root, dirs, files in os.walk(base_dir, topdown=True):
579    if root == here:
580        # we don't want to recurse back into this SConscript
581        continue
582
583    if 'SConscript' in files:
584        build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
585        Source.set_group(build_dir)
586        SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
587
588for extra_dir in extras_dir_list:
589    prefix_len = len(dirname(extra_dir)) + 1
590
591    # Also add the corresponding build directory to pick up generated
592    # include files.
593    env.Append(CPPPATH=Dir(joinpath(env['BUILDDIR'], extra_dir[prefix_len:])))
594
595    for root, dirs, files in os.walk(extra_dir, topdown=True):
596        # if build lives in the extras directory, don't walk down it
597        if 'build' in dirs:
598            dirs.remove('build')
599
600        if 'SConscript' in files:
601            build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
602            SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
603
604for opt in export_vars:
605    env.ConfigFile(opt)
606
607def makeTheISA(source, target, env):
608    isas = [ src.get_contents() for src in source ]
609    target_isa = env['TARGET_ISA']
610    def define(isa):
611        return isa.upper() + '_ISA'
612
613    def namespace(isa):
614        return isa[0].upper() + isa[1:].lower() + 'ISA'
615
616
617    code = code_formatter()
618    code('''\
619#ifndef __CONFIG_THE_ISA_HH__
620#define __CONFIG_THE_ISA_HH__
621
622''')
623
624    # create defines for the preprocessing and compile-time determination
625    for i,isa in enumerate(isas):
626        code('#define $0 $1', define(isa), i + 1)
627    code()
628
629    # create an enum for any run-time determination of the ISA, we
630    # reuse the same name as the namespaces
631    code('enum class Arch {')
632    for i,isa in enumerate(isas):
633        if i + 1 == len(isas):
634            code('  $0 = $1', namespace(isa), define(isa))
635        else:
636            code('  $0 = $1,', namespace(isa), define(isa))
637    code('};')
638
639    code('''
640
641#define THE_ISA ${{define(target_isa)}}
642#define TheISA ${{namespace(target_isa)}}
643#define THE_ISA_STR "${{target_isa}}"
644
645#endif // __CONFIG_THE_ISA_HH__''')
646
647    code.write(str(target[0]))
648
649env.Command('config/the_isa.hh', map(Value, all_isa_list),
650            MakeAction(makeTheISA, Transform("CFG ISA", 0)))
651
652def makeTheGPUISA(source, target, env):
653    isas = [ src.get_contents() for src in source ]
654    target_gpu_isa = env['TARGET_GPU_ISA']
655    def define(isa):
656        return isa.upper() + '_ISA'
657
658    def namespace(isa):
659        return isa[0].upper() + isa[1:].lower() + 'ISA'
660
661
662    code = code_formatter()
663    code('''\
664#ifndef __CONFIG_THE_GPU_ISA_HH__
665#define __CONFIG_THE_GPU_ISA_HH__
666
667''')
668
669    # create defines for the preprocessing and compile-time determination
670    for i,isa in enumerate(isas):
671        code('#define $0 $1', define(isa), i + 1)
672    code()
673
674    # create an enum for any run-time determination of the ISA, we
675    # reuse the same name as the namespaces
676    code('enum class GPUArch {')
677    for i,isa in enumerate(isas):
678        if i + 1 == len(isas):
679            code('  $0 = $1', namespace(isa), define(isa))
680        else:
681            code('  $0 = $1,', namespace(isa), define(isa))
682    code('};')
683
684    code('''
685
686#define THE_GPU_ISA ${{define(target_gpu_isa)}}
687#define TheGpuISA ${{namespace(target_gpu_isa)}}
688#define THE_GPU_ISA_STR "${{target_gpu_isa}}"
689
690#endif // __CONFIG_THE_GPU_ISA_HH__''')
691
692    code.write(str(target[0]))
693
694env.Command('config/the_gpu_isa.hh', map(Value, all_gpu_isa_list),
695            MakeAction(makeTheGPUISA, Transform("CFG ISA", 0)))
696
697########################################################################
698#
699# Prevent any SimObjects from being added after this point, they
700# should all have been added in the SConscripts above
701#
702SimObject.fixed = True
703
704class DictImporter(object):
705    '''This importer takes a dictionary of arbitrary module names that
706    map to arbitrary filenames.'''
707    def __init__(self, modules):
708        self.modules = modules
709        self.installed = set()
710
711    def __del__(self):
712        self.unload()
713
714    def unload(self):
715        import sys
716        for module in self.installed:
717            del sys.modules[module]
718        self.installed = set()
719
720    def find_module(self, fullname, path):
721        if fullname == 'm5.defines':
722            return self
723
724        if fullname == 'm5.objects':
725            return self
726
727        if fullname.startswith('_m5'):
728            return None
729
730        source = self.modules.get(fullname, None)
731        if source is not None and fullname.startswith('m5.objects'):
732            return self
733
734        return None
735
736    def load_module(self, fullname):
737        mod = imp.new_module(fullname)
738        sys.modules[fullname] = mod
739        self.installed.add(fullname)
740
741        mod.__loader__ = self
742        if fullname == 'm5.objects':
743            mod.__path__ = fullname.split('.')
744            return mod
745
746        if fullname == 'm5.defines':
747            mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
748            return mod
749
750        source = self.modules[fullname]
751        if source.modname == '__init__':
752            mod.__path__ = source.modpath
753        mod.__file__ = source.abspath
754
755        exec file(source.abspath, 'r') in mod.__dict__
756
757        return mod
758
759import m5.SimObject
760import m5.params
761from m5.util import code_formatter
762
763m5.SimObject.clear()
764m5.params.clear()
765
766# install the python importer so we can grab stuff from the source
767# tree itself.  We can't have SimObjects added after this point or
768# else we won't know about them for the rest of the stuff.
769importer = DictImporter(PySource.modules)
770sys.meta_path[0:0] = [ importer ]
771
772# import all sim objects so we can populate the all_objects list
773# make sure that we're working with a list, then let's sort it
774for modname in SimObject.modnames:
775    exec('from m5.objects import %s' % modname)
776
777# we need to unload all of the currently imported modules so that they
778# will be re-imported the next time the sconscript is run
779importer.unload()
780sys.meta_path.remove(importer)
781
782sim_objects = m5.SimObject.allClasses
783all_enums = m5.params.allEnums
784
785for name,obj in sorted(sim_objects.iteritems()):
786    for param in obj._params.local.values():
787        # load the ptype attribute now because it depends on the
788        # current version of SimObject.allClasses, but when scons
789        # actually uses the value, all versions of
790        # SimObject.allClasses will have been loaded
791        param.ptype
792
793########################################################################
794#
795# calculate extra dependencies
796#
797module_depends = ["m5", "m5.SimObject", "m5.params"]
798depends = [ PySource.modules[dep].snode for dep in module_depends ]
799depends.sort(key = lambda x: x.name)
800
801########################################################################
802#
803# Commands for the basic automatically generated python files
804#
805
806# Generate Python file containing a dict specifying the current
807# buildEnv flags.
808def makeDefinesPyFile(target, source, env):
809    build_env = source[0].get_contents()
810
811    code = code_formatter()
812    code("""
813import _m5.core
814import m5.util
815
816buildEnv = m5.util.SmartDict($build_env)
817
818compileDate = _m5.core.compileDate
819_globals = globals()
820for key,val in _m5.core.__dict__.iteritems():
821    if key.startswith('flag_'):
822        flag = key[5:]
823        _globals[flag] = val
824del _globals
825""")
826    code.write(target[0].abspath)
827
828defines_info = Value(build_env)
829# Generate a file with all of the compile options in it
830env.Command('python/m5/defines.py', defines_info,
831            MakeAction(makeDefinesPyFile, Transform("DEFINES", 0)))
832PySource('m5', 'python/m5/defines.py')
833
834# Generate python file containing info about the M5 source code
835def makeInfoPyFile(target, source, env):
836    code = code_formatter()
837    for src in source:
838        data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
839        code('$src = ${{repr(data)}}')
840    code.write(str(target[0]))
841
842# Generate a file that wraps the basic top level files
843env.Command('python/m5/info.py',
844            [ '#/COPYING', '#/LICENSE', '#/README', ],
845            MakeAction(makeInfoPyFile, Transform("INFO")))
846PySource('m5', 'python/m5/info.py')
847
848########################################################################
849#
850# Create all of the SimObject param headers and enum headers
851#
852
853def createSimObjectParamStruct(target, source, env):
854    assert len(target) == 1 and len(source) == 1
855
856    name = source[0].get_text_contents()
857    obj = sim_objects[name]
858
859    code = code_formatter()
860    obj.cxx_param_decl(code)
861    code.write(target[0].abspath)
862
863def createSimObjectCxxConfig(is_header):
864    def body(target, source, env):
865        assert len(target) == 1 and len(source) == 1
866
867        name = str(source[0].get_contents())
868        obj = sim_objects[name]
869
870        code = code_formatter()
871        obj.cxx_config_param_file(code, is_header)
872        code.write(target[0].abspath)
873    return body
874
875def createEnumStrings(target, source, env):
876    assert len(target) == 1 and len(source) == 2
877
878    name = source[0].get_text_contents()
879    use_python = source[1].read()
880    obj = all_enums[name]
881
882    code = code_formatter()
883    obj.cxx_def(code)
884    if use_python:
885        obj.pybind_def(code)
886    code.write(target[0].abspath)
887
888def createEnumDecls(target, source, env):
889    assert len(target) == 1 and len(source) == 1
890
891    name = source[0].get_text_contents()
892    obj = all_enums[name]
893
894    code = code_formatter()
895    obj.cxx_decl(code)
896    code.write(target[0].abspath)
897
898def createSimObjectPyBindWrapper(target, source, env):
899    name = source[0].get_text_contents()
900    obj = sim_objects[name]
901
902    code = code_formatter()
903    obj.pybind_decl(code)
904    code.write(target[0].abspath)
905
906# Generate all of the SimObject param C++ struct header files
907params_hh_files = []
908for name,simobj in sorted(sim_objects.iteritems()):
909    py_source = PySource.modules[simobj.__module__]
910    extra_deps = [ py_source.tnode ]
911
912    hh_file = File('params/%s.hh' % name)
913    params_hh_files.append(hh_file)
914    env.Command(hh_file, Value(name),
915                MakeAction(createSimObjectParamStruct, Transform("SO PARAM")))
916    env.Depends(hh_file, depends + extra_deps)
917
918# C++ parameter description files
919if GetOption('with_cxx_config'):
920    for name,simobj in sorted(sim_objects.iteritems()):
921        py_source = PySource.modules[simobj.__module__]
922        extra_deps = [ py_source.tnode ]
923
924        cxx_config_hh_file = File('cxx_config/%s.hh' % name)
925        cxx_config_cc_file = File('cxx_config/%s.cc' % name)
926        env.Command(cxx_config_hh_file, Value(name),
927                    MakeAction(createSimObjectCxxConfig(True),
928                    Transform("CXXCPRHH")))
929        env.Command(cxx_config_cc_file, Value(name),
930                    MakeAction(createSimObjectCxxConfig(False),
931                    Transform("CXXCPRCC")))
932        env.Depends(cxx_config_hh_file, depends + extra_deps +
933                    [File('params/%s.hh' % name), File('sim/cxx_config.hh')])
934        env.Depends(cxx_config_cc_file, depends + extra_deps +
935                    [cxx_config_hh_file])
936        Source(cxx_config_cc_file)
937
938    cxx_config_init_cc_file = File('cxx_config/init.cc')
939
940    def createCxxConfigInitCC(target, source, env):
941        assert len(target) == 1 and len(source) == 1
942
943        code = code_formatter()
944
945        for name,simobj in sorted(sim_objects.iteritems()):
946            if not hasattr(simobj, 'abstract') or not simobj.abstract:
947                code('#include "cxx_config/${name}.hh"')
948        code()
949        code('void cxxConfigInit()')
950        code('{')
951        code.indent()
952        for name,simobj in sorted(sim_objects.iteritems()):
953            not_abstract = not hasattr(simobj, 'abstract') or \
954                not simobj.abstract
955            if not_abstract and 'type' in simobj.__dict__:
956                code('cxx_config_directory["${name}"] = '
957                     '${name}CxxConfigParams::makeDirectoryEntry();')
958        code.dedent()
959        code('}')
960        code.write(target[0].abspath)
961
962    py_source = PySource.modules[simobj.__module__]
963    extra_deps = [ py_source.tnode ]
964    env.Command(cxx_config_init_cc_file, Value(name),
965        MakeAction(createCxxConfigInitCC, Transform("CXXCINIT")))
966    cxx_param_hh_files = ["cxx_config/%s.hh" % simobj
967        for name,simobj in sorted(sim_objects.iteritems())
968        if not hasattr(simobj, 'abstract') or not simobj.abstract]
969    Depends(cxx_config_init_cc_file, cxx_param_hh_files +
970            [File('sim/cxx_config.hh')])
971    Source(cxx_config_init_cc_file)
972
973# Generate all enum header files
974for name,enum in sorted(all_enums.iteritems()):
975    py_source = PySource.modules[enum.__module__]
976    extra_deps = [ py_source.tnode ]
977
978    cc_file = File('enums/%s.cc' % name)
979    env.Command(cc_file, [Value(name), Value(env['USE_PYTHON'])],
980                MakeAction(createEnumStrings, Transform("ENUM STR")))
981    env.Depends(cc_file, depends + extra_deps)
982    Source(cc_file)
983
984    hh_file = File('enums/%s.hh' % name)
985    env.Command(hh_file, Value(name),
986                MakeAction(createEnumDecls, Transform("ENUMDECL")))
987    env.Depends(hh_file, depends + extra_deps)
988
989# Generate SimObject Python bindings wrapper files
990if env['USE_PYTHON']:
991    for name,simobj in sorted(sim_objects.iteritems()):
992        py_source = PySource.modules[simobj.__module__]
993        extra_deps = [ py_source.tnode ]
994        cc_file = File('python/_m5/param_%s.cc' % name)
995        env.Command(cc_file, Value(name),
996                    MakeAction(createSimObjectPyBindWrapper,
997                               Transform("SO PyBind")))
998        env.Depends(cc_file, depends + extra_deps)
999        Source(cc_file)
1000
1001# Build all protocol buffers if we have got protoc and protobuf available
1002if env['HAVE_PROTOBUF']:
1003    for proto in ProtoBuf.all:
1004        # Use both the source and header as the target, and the .proto
1005        # file as the source. When executing the protoc compiler, also
1006        # specify the proto_path to avoid having the generated files
1007        # include the path.
1008        env.Command([proto.cc_file, proto.hh_file], proto.tnode,
1009                    MakeAction('$PROTOC --cpp_out ${TARGET.dir} '
1010                               '--proto_path ${SOURCE.dir} $SOURCE',
1011                               Transform("PROTOC")))
1012
1013        # Add the C++ source file
1014        Source(proto.cc_file, tags=proto.tags)
1015elif ProtoBuf.all:
1016    print('Got protobuf to build, but lacks support!')
1017    Exit(1)
1018
1019#
1020# Handle debug flags
1021#
1022def makeDebugFlagCC(target, source, env):
1023    assert(len(target) == 1 and len(source) == 1)
1024
1025    code = code_formatter()
1026
1027    # delay definition of CompoundFlags until after all the definition
1028    # of all constituent SimpleFlags
1029    comp_code = code_formatter()
1030
1031    # file header
1032    code('''
1033/*
1034 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
1035 */
1036
1037#include "base/debug.hh"
1038
1039namespace Debug {
1040
1041''')
1042
1043    for name, flag in sorted(source[0].read().iteritems()):
1044        n, compound, desc = flag
1045        assert n == name
1046
1047        if not compound:
1048            code('SimpleFlag $name("$name", "$desc");')
1049        else:
1050            comp_code('CompoundFlag $name("$name", "$desc",')
1051            comp_code.indent()
1052            last = len(compound) - 1
1053            for i,flag in enumerate(compound):
1054                if i != last:
1055                    comp_code('&$flag,')
1056                else:
1057                    comp_code('&$flag);')
1058            comp_code.dedent()
1059
1060    code.append(comp_code)
1061    code()
1062    code('} // namespace Debug')
1063
1064    code.write(str(target[0]))
1065
1066def makeDebugFlagHH(target, source, env):
1067    assert(len(target) == 1 and len(source) == 1)
1068
1069    val = eval(source[0].get_contents())
1070    name, compound, desc = val
1071
1072    code = code_formatter()
1073
1074    # file header boilerplate
1075    code('''\
1076/*
1077 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
1078 */
1079
1080#ifndef __DEBUG_${name}_HH__
1081#define __DEBUG_${name}_HH__
1082
1083namespace Debug {
1084''')
1085
1086    if compound:
1087        code('class CompoundFlag;')
1088    code('class SimpleFlag;')
1089
1090    if compound:
1091        code('extern CompoundFlag $name;')
1092        for flag in compound:
1093            code('extern SimpleFlag $flag;')
1094    else:
1095        code('extern SimpleFlag $name;')
1096
1097    code('''
1098}
1099
1100#endif // __DEBUG_${name}_HH__
1101''')
1102
1103    code.write(str(target[0]))
1104
1105for name,flag in sorted(debug_flags.iteritems()):
1106    n, compound, desc = flag
1107    assert n == name
1108
1109    hh_file = 'debug/%s.hh' % name
1110    env.Command(hh_file, Value(flag),
1111                MakeAction(makeDebugFlagHH, Transform("TRACING", 0)))
1112
1113env.Command('debug/flags.cc', Value(debug_flags),
1114            MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
1115Source('debug/flags.cc')
1116
1117# version tags
1118tags = \
1119env.Command('sim/tags.cc', None,
1120            MakeAction('util/cpt_upgrader.py --get-cc-file > $TARGET',
1121                       Transform("VER TAGS")))
1122env.AlwaysBuild(tags)
1123
1124# Embed python files.  All .py files that have been indicated by a
1125# PySource() call in a SConscript need to be embedded into the M5
1126# library.  To do that, we compile the file to byte code, marshal the
1127# byte code, compress it, and then generate a c++ file that
1128# inserts the result into an array.
1129def embedPyFile(target, source, env):
1130    def c_str(string):
1131        if string is None:
1132            return "0"
1133        return '"%s"' % string
1134
1135    '''Action function to compile a .py into a code object, marshal
1136    it, compress it, and stick it into an asm file so the code appears
1137    as just bytes with a label in the data section'''
1138
1139    src = file(str(source[0]), 'r').read()
1140
1141    pysource = PySource.tnodes[source[0]]
1142    compiled = compile(src, pysource.abspath, 'exec')
1143    marshalled = marshal.dumps(compiled)
1144    compressed = zlib.compress(marshalled)
1145    data = compressed
1146    sym = pysource.symname
1147
1148    code = code_formatter()
1149    code('''\
1150#include "sim/init.hh"
1151
1152namespace {
1153
1154''')
1155    blobToCpp(data, 'data_' + sym, code)
1156    code('''\
1157
1158
1159EmbeddedPython embedded_${sym}(
1160    ${{c_str(pysource.arcname)}},
1161    ${{c_str(pysource.abspath)}},
1162    ${{c_str(pysource.modpath)}},
1163    data_${sym},
1164    ${{len(data)}},
1165    ${{len(marshalled)}});
1166
1167} // anonymous namespace
1168''')
1169    code.write(str(target[0]))
1170
1171for source in PySource.all:
1172    env.Command(source.cpp, source.tnode,
1173                MakeAction(embedPyFile, Transform("EMBED PY")))
1174    Source(source.cpp, tags=source.tags, add_tags='python')
1175
1176########################################################################
1177#
1178# Define binaries.  Each different build type (debug, opt, etc.) gets
1179# a slightly different build environment.
1180#
1181
1182# List of constructed environments to pass back to SConstruct
1183date_source = Source('base/date.cc', tags=[])
1184
1185gem5_binary = Gem5('gem5')
1186
1187# Function to create a new build environment as clone of current
1188# environment 'env' with modified object suffix and optional stripped
1189# binary.  Additional keyword arguments are appended to corresponding
1190# build environment vars.
1191def makeEnv(env, label, objsfx, strip=False, disable_partial=False, **kwargs):
1192    # SCons doesn't know to append a library suffix when there is a '.' in the
1193    # name.  Use '_' instead.
1194    libname = 'gem5_' + label
1195    secondary_exename = 'm5.' + label
1196
1197    new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
1198    new_env.Label = label
1199    new_env.Append(**kwargs)
1200
1201    lib_sources = Source.all.with_tag('gem5 lib')
1202
1203    # Without Python, leave out all Python content from the library
1204    # builds.  The option doesn't affect gem5 built as a program
1205    if GetOption('without_python'):
1206        lib_sources = lib_sources.without_tag('python')
1207
1208    static_objs = []
1209    shared_objs = []
1210
1211    for s in lib_sources.with_tag(Source.ungrouped_tag):
1212        static_objs.append(s.static(new_env))
1213        shared_objs.append(s.shared(new_env))
1214
1215    for group in Source.source_groups:
1216        srcs = lib_sources.with_tag(Source.link_group_tag(group))
1217        if not srcs:
1218            continue
1219
1220        group_static = [ s.static(new_env) for s in srcs ]
1221        group_shared = [ s.shared(new_env) for s in srcs ]
1222
1223        # If partial linking is disabled, add these sources to the build
1224        # directly, and short circuit this loop.
1225        if disable_partial:
1226            static_objs.extend(group_static)
1227            shared_objs.extend(group_shared)
1228            continue
1229
1230        # Set up the static partially linked objects.
1231        file_name = new_env.subst("${OBJPREFIX}lib${OBJSUFFIX}.partial")
1232        target = File(joinpath(group, file_name))
1233        partial = env.PartialStatic(target=target, source=group_static)
1234        static_objs.extend(partial)
1235
1236        # Set up the shared partially linked objects.
1237        file_name = new_env.subst("${SHOBJPREFIX}lib${SHOBJSUFFIX}.partial")
1238        target = File(joinpath(group, file_name))
1239        partial = env.PartialShared(target=target, source=group_shared)
1240        shared_objs.extend(partial)
1241
1242    static_date = date_source.static(new_env)
1243    new_env.Depends(static_date, static_objs)
1244    static_objs.extend(static_date)
1245
1246    shared_date = date_source.shared(new_env)
1247    new_env.Depends(shared_date, shared_objs)
1248    shared_objs.extend(shared_date)
1249
1250    main_objs = [ s.static(new_env) for s in Source.all.with_tag('main') ]
1251
1252    # First make a library of everything but main() so other programs can
1253    # link against m5.
1254    static_lib = new_env.StaticLibrary(libname, static_objs)
1255    shared_lib = new_env.SharedLibrary(libname, shared_objs)
1256
1257    # Keep track of the object files generated so far so Executables can
1258    # include them.
1259    new_env['STATIC_OBJS'] = static_objs
1260    new_env['SHARED_OBJS'] = shared_objs
1261    new_env['MAIN_OBJS'] = main_objs
1262
1263    new_env['STATIC_LIB'] = static_lib
1264    new_env['SHARED_LIB'] = shared_lib
1265
1266    # Record some settings for building Executables.
1267    new_env['EXE_SUFFIX'] = label
1268    new_env['STRIP_EXES'] = strip
1269
1270    for cls in ExecutableMeta.all:
1271        cls.declare_all(new_env)
1272
1273    new_env.M5Binary = File(gem5_binary.path(new_env))
1274
1275    new_env.Command(secondary_exename, new_env.M5Binary,
1276            MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK")))
1277
1278    # Set up regression tests.
1279    SConscript(os.path.join(env.root.abspath, 'tests', 'SConscript'),
1280               variant_dir=Dir('tests').Dir(new_env.Label),
1281               exports={ 'env' : new_env }, duplicate=False)
1282
1283# Start out with the compiler flags common to all compilers,
1284# i.e. they all use -g for opt and -g -pg for prof
1285ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'],
1286           'perf' : ['-g']}
1287
1288# Start out with the linker flags common to all linkers, i.e. -pg for
1289# prof, and -lprofiler for perf. The -lprofile flag is surrounded by
1290# no-as-needed and as-needed as the binutils linker is too clever and
1291# simply doesn't link to the library otherwise.
1292ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'],
1293           'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']}
1294
1295# For Link Time Optimization, the optimisation flags used to compile
1296# individual files are decoupled from those used at link time
1297# (i.e. you can compile with -O3 and perform LTO with -O0), so we need
1298# to also update the linker flags based on the target.
1299if env['GCC']:
1300    if sys.platform == 'sunos5':
1301        ccflags['debug'] += ['-gstabs+']
1302    else:
1303        ccflags['debug'] += ['-ggdb3']
1304    ldflags['debug'] += ['-O0']
1305    # opt, fast, prof and perf all share the same cc flags, also add
1306    # the optimization to the ldflags as LTO defers the optimization
1307    # to link time
1308    for target in ['opt', 'fast', 'prof', 'perf']:
1309        ccflags[target] += ['-O3']
1310        ldflags[target] += ['-O3']
1311
1312    ccflags['fast'] += env['LTO_CCFLAGS']
1313    ldflags['fast'] += env['LTO_LDFLAGS']
1314elif env['CLANG']:
1315    ccflags['debug'] += ['-g', '-O0']
1316    # opt, fast, prof and perf all share the same cc flags
1317    for target in ['opt', 'fast', 'prof', 'perf']:
1318        ccflags[target] += ['-O3']
1319else:
1320    print('Unknown compiler, please fix compiler options')
1321    Exit(1)
1322
1323
1324# To speed things up, we only instantiate the build environments we
1325# need.  We try to identify the needed environment for each target; if
1326# we can't, we fall back on instantiating all the environments just to
1327# be safe.
1328target_types = ['debug', 'opt', 'fast', 'prof', 'perf']
1329obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof',
1330              'gpo' : 'perf'}
1331
1332def identifyTarget(t):
1333    ext = t.split('.')[-1]
1334    if ext in target_types:
1335        return ext
1336    if obj2target.has_key(ext):
1337        return obj2target[ext]
1338    match = re.search(r'/tests/([^/]+)/', t)
1339    if match and match.group(1) in target_types:
1340        return match.group(1)
1341    return 'all'
1342
1343needed_envs = [identifyTarget(target) for target in BUILD_TARGETS]
1344if 'all' in needed_envs:
1345    needed_envs += target_types
1346
1347disable_partial = False
1348if env['PLATFORM'] == 'darwin':
1349    # Up until Apple LLVM version 10.0.0 (clang-1000.11.45.5), partial
1350    # linked objects do not expose symbols that are marked with the
1351    # hidden visibility and consequently building gem5 on Mac OS
1352    # fails. As a workaround, we disable partial linking, however, we
1353    # may want to revisit in the future.
1354    disable_partial = True
1355
1356# Debug binary
1357if 'debug' in needed_envs:
1358    makeEnv(env, 'debug', '.do',
1359            CCFLAGS = Split(ccflags['debug']),
1360            CPPDEFINES = ['DEBUG', 'TRACING_ON=1'],
1361            LINKFLAGS = Split(ldflags['debug']),
1362            disable_partial=disable_partial)
1363
1364# Optimized binary
1365if 'opt' in needed_envs:
1366    makeEnv(env, 'opt', '.o',
1367            CCFLAGS = Split(ccflags['opt']),
1368            CPPDEFINES = ['TRACING_ON=1'],
1369            LINKFLAGS = Split(ldflags['opt']),
1370            disable_partial=disable_partial)
1371
1372# "Fast" binary
1373if 'fast' in needed_envs:
1374    disable_partial = disable_partial and \
1375            env.get('BROKEN_INCREMENTAL_LTO', False) and \
1376            GetOption('force_lto')
1377    makeEnv(env, 'fast', '.fo', strip = True,
1378            CCFLAGS = Split(ccflags['fast']),
1379            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1380            LINKFLAGS = Split(ldflags['fast']),
1381            disable_partial=disable_partial)
1382
1383# Profiled binary using gprof
1384if 'prof' in needed_envs:
1385    makeEnv(env, 'prof', '.po',
1386            CCFLAGS = Split(ccflags['prof']),
1387            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1388            LINKFLAGS = Split(ldflags['prof']),
1389            disable_partial=disable_partial)
1390
1391# Profiled binary using google-pprof
1392if 'perf' in needed_envs:
1393    makeEnv(env, 'perf', '.gpo',
1394            CCFLAGS = Split(ccflags['perf']),
1395            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1396            LINKFLAGS = Split(ldflags['perf']),
1397            disable_partial=disable_partial)
1398