SConscript revision 13577:70ab75eec40b
16019Shines@cs.fsu.edu# -*- mode:python -*-
26019Shines@cs.fsu.edu
37100Sgblack@eecs.umich.edu# Copyright (c) 2018 ARM Limited
47100Sgblack@eecs.umich.edu#
57100Sgblack@eecs.umich.edu# The license below extends only to copyright in the software and shall
67100Sgblack@eecs.umich.edu# not be construed as granting a license to any other intellectual
77100Sgblack@eecs.umich.edu# property including but not limited to intellectual property relating
87100Sgblack@eecs.umich.edu# to a hardware implementation of the functionality of the software
97100Sgblack@eecs.umich.edu# licensed hereunder.  You may use the software subject to the license
107100Sgblack@eecs.umich.edu# terms below provided that you ensure that this notice is replicated
117100Sgblack@eecs.umich.edu# unmodified and in its entirety in all distributions of the software,
127100Sgblack@eecs.umich.edu# modified or unmodified, in source code or in binary form.
137100Sgblack@eecs.umich.edu#
147100Sgblack@eecs.umich.edu# Copyright (c) 2004-2005 The Regents of The University of Michigan
156019Shines@cs.fsu.edu# All rights reserved.
166019Shines@cs.fsu.edu#
176019Shines@cs.fsu.edu# Redistribution and use in source and binary forms, with or without
186019Shines@cs.fsu.edu# modification, are permitted provided that the following conditions are
196019Shines@cs.fsu.edu# met: redistributions of source code must retain the above copyright
206019Shines@cs.fsu.edu# notice, this list of conditions and the following disclaimer;
216019Shines@cs.fsu.edu# redistributions in binary form must reproduce the above copyright
226019Shines@cs.fsu.edu# notice, this list of conditions and the following disclaimer in the
236019Shines@cs.fsu.edu# documentation and/or other materials provided with the distribution;
246019Shines@cs.fsu.edu# neither the name of the copyright holders nor the names of its
256019Shines@cs.fsu.edu# contributors may be used to endorse or promote products derived from
266019Shines@cs.fsu.edu# this software without specific prior written permission.
276019Shines@cs.fsu.edu#
286019Shines@cs.fsu.edu# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
296019Shines@cs.fsu.edu# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
306019Shines@cs.fsu.edu# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
316019Shines@cs.fsu.edu# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
326019Shines@cs.fsu.edu# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
336019Shines@cs.fsu.edu# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
346019Shines@cs.fsu.edu# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
356019Shines@cs.fsu.edu# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
366019Shines@cs.fsu.edu# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
376019Shines@cs.fsu.edu# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
386019Shines@cs.fsu.edu# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
396019Shines@cs.fsu.edu#
406019Shines@cs.fsu.edu# Authors: Nathan Binkert
416019Shines@cs.fsu.edu
426757SAli.Saidi@ARM.comfrom __future__ import print_function
436019Shines@cs.fsu.edu
446019Shines@cs.fsu.eduimport array
456019Shines@cs.fsu.eduimport bisect
466019Shines@cs.fsu.eduimport functools
476019Shines@cs.fsu.eduimport imp
486019Shines@cs.fsu.eduimport marshal
496019Shines@cs.fsu.eduimport os
506019Shines@cs.fsu.eduimport re
517170Sgblack@eecs.umich.eduimport subprocess
526253Sgblack@eecs.umich.eduimport sys
537202Sgblack@eecs.umich.eduimport zlib
546253Sgblack@eecs.umich.edu
556253Sgblack@eecs.umich.edufrom os.path import basename, dirname, exists, isdir, isfile, join as joinpath
567396Sgblack@eecs.umich.edu
578745Sgblack@eecs.umich.eduimport SCons
587405SAli.Saidi@ARM.com
597259Sgblack@eecs.umich.edufrom gem5_scons import Transform
607423Sgblack@eecs.umich.edu
616397Sgblack@eecs.umich.edu# This file defines how to build a particular configuration of gem5
626019Shines@cs.fsu.edu# based on variable settings in the 'env' build environment.
636757SAli.Saidi@ARM.com
647752SWilliam.Wang@arm.comImport('*')
656019Shines@cs.fsu.edu
668745Sgblack@eecs.umich.edu# Children need to see the environment
676397Sgblack@eecs.umich.eduExport('env')
686019Shines@cs.fsu.edu
696397Sgblack@eecs.umich.edubuild_env = [(opt, env[opt]) for opt in export_vars]
708335Snate@binkert.org
718335Snate@binkert.orgfrom m5.util import code_formatter, compareVersions
728335Snate@binkert.org
738335Snate@binkert.org########################################################################
746019Shines@cs.fsu.edu# Code for adding source files of various types
756757SAli.Saidi@ARM.com#
766757SAli.Saidi@ARM.com# When specifying a source file of some type, a set of tags can be
777694SAli.Saidi@ARM.com# specified for that file.
787585SAli.Saidi@arm.com
797404SAli.Saidi@ARM.comclass SourceFilter(object):
806757SAli.Saidi@ARM.com    def __init__(self, predicate):
816757SAli.Saidi@ARM.com        self.predicate = predicate
826019Shines@cs.fsu.edu
836019Shines@cs.fsu.edu    def __or__(self, other):
846019Shines@cs.fsu.edu        return SourceFilter(lambda tags: self.predicate(tags) or
856019Shines@cs.fsu.edu                                         other.predicate(tags))
866019Shines@cs.fsu.edu
876019Shines@cs.fsu.edu    def __and__(self, other):
886019Shines@cs.fsu.edu        return SourceFilter(lambda tags: self.predicate(tags) and
896019Shines@cs.fsu.edu                                         other.predicate(tags))
906019Shines@cs.fsu.edu
916019Shines@cs.fsu.edudef with_tags_that(predicate):
926019Shines@cs.fsu.edu    '''Return a list of sources with tags that satisfy a predicate.'''
936019Shines@cs.fsu.edu    return SourceFilter(predicate)
94
95def with_any_tags(*tags):
96    '''Return a list of sources with any of the supplied tags.'''
97    return SourceFilter(lambda stags: len(set(tags) & stags) > 0)
98
99def with_all_tags(*tags):
100    '''Return a list of sources with all of the supplied tags.'''
101    return SourceFilter(lambda stags: set(tags) <= stags)
102
103def with_tag(tag):
104    '''Return a list of sources with the supplied tag.'''
105    return SourceFilter(lambda stags: tag in stags)
106
107def without_tags(*tags):
108    '''Return a list of sources without any of the supplied tags.'''
109    return SourceFilter(lambda stags: len(set(tags) & stags) == 0)
110
111def without_tag(tag):
112    '''Return a list of sources with the supplied tag.'''
113    return SourceFilter(lambda stags: tag not in stags)
114
115source_filter_factories = {
116    'with_tags_that': with_tags_that,
117    'with_any_tags': with_any_tags,
118    'with_all_tags': with_all_tags,
119    'with_tag': with_tag,
120    'without_tags': without_tags,
121    'without_tag': without_tag,
122}
123
124Export(source_filter_factories)
125
126class SourceList(list):
127    def apply_filter(self, f):
128        def match(source):
129            return f.predicate(source.tags)
130        return SourceList(filter(match, self))
131
132    def __getattr__(self, name):
133        func = source_filter_factories.get(name, None)
134        if not func:
135            raise AttributeError
136
137        @functools.wraps(func)
138        def wrapper(*args, **kwargs):
139            return self.apply_filter(func(*args, **kwargs))
140        return wrapper
141
142class SourceMeta(type):
143    '''Meta class for source files that keeps track of all files of a
144    particular type.'''
145    def __init__(cls, name, bases, dict):
146        super(SourceMeta, cls).__init__(name, bases, dict)
147        cls.all = SourceList()
148
149class SourceFile(object):
150    '''Base object that encapsulates the notion of a source file.
151    This includes, the source node, target node, various manipulations
152    of those.  A source file also specifies a set of tags which
153    describing arbitrary properties of the source file.'''
154    __metaclass__ = SourceMeta
155
156    static_objs = {}
157    shared_objs = {}
158
159    def __init__(self, source, tags=None, add_tags=None):
160        if tags is None:
161            tags='gem5 lib'
162        if isinstance(tags, basestring):
163            tags = set([tags])
164        if not isinstance(tags, set):
165            tags = set(tags)
166        self.tags = tags
167
168        if add_tags:
169            if isinstance(add_tags, basestring):
170                add_tags = set([add_tags])
171            if not isinstance(add_tags, set):
172                add_tags = set(add_tags)
173            self.tags |= add_tags
174
175        tnode = source
176        if not isinstance(source, SCons.Node.FS.File):
177            tnode = File(source)
178
179        self.tnode = tnode
180        self.snode = tnode.srcnode()
181
182        for base in type(self).__mro__:
183            if issubclass(base, SourceFile):
184                base.all.append(self)
185
186    def static(self, env):
187        key = (self.tnode, env['OBJSUFFIX'])
188        if not key in self.static_objs:
189            self.static_objs[key] = env.StaticObject(self.tnode)
190        return self.static_objs[key]
191
192    def shared(self, env):
193        key = (self.tnode, env['OBJSUFFIX'])
194        if not key in self.shared_objs:
195            self.shared_objs[key] = env.SharedObject(self.tnode)
196        return self.shared_objs[key]
197
198    @property
199    def filename(self):
200        return str(self.tnode)
201
202    @property
203    def dirname(self):
204        return dirname(self.filename)
205
206    @property
207    def basename(self):
208        return basename(self.filename)
209
210    @property
211    def extname(self):
212        index = self.basename.rfind('.')
213        if index <= 0:
214            # dot files aren't extensions
215            return self.basename, None
216
217        return self.basename[:index], self.basename[index+1:]
218
219    def __lt__(self, other): return self.filename < other.filename
220    def __le__(self, other): return self.filename <= other.filename
221    def __gt__(self, other): return self.filename > other.filename
222    def __ge__(self, other): return self.filename >= other.filename
223    def __eq__(self, other): return self.filename == other.filename
224    def __ne__(self, other): return self.filename != other.filename
225
226def blobToCpp(data, symbol, cpp_code, hpp_code=None, namespace=None):
227    '''
228    Convert bytes data into C++ .cpp and .hh uint8_t byte array
229    code containing that binary data.
230
231    :param data: binary data to be converted to C++
232    :param symbol: name of the symbol
233    :param cpp_code: append the generated cpp_code to this object
234    :param hpp_code: append the generated hpp_code to this object
235                     If None, ignore it. Otherwise, also include it
236                     in the .cpp file.
237    :param namespace: namespace to put the symbol into. If None,
238                      don't put the symbols into any namespace.
239    '''
240    symbol_len_declaration = 'const std::size_t {}_len'.format(symbol)
241    symbol_declaration = 'const std::uint8_t {}[]'.format(symbol)
242    if hpp_code is not None:
243        cpp_code('''\
244#include "blobs/{}.hh"
245'''.format(symbol))
246        hpp_code('''\
247#include <cstddef>
248#include <cstdint>
249''')
250        if namespace is not None:
251            hpp_code('namespace {} {{'.format(namespace))
252        hpp_code('extern ' + symbol_len_declaration + ';')
253        hpp_code('extern ' + symbol_declaration + ';')
254        if namespace is not None:
255            hpp_code('}')
256    if namespace is not None:
257        cpp_code('namespace {} {{'.format(namespace))
258    cpp_code(symbol_len_declaration + ' = {};'.format(len(data)))
259    cpp_code(symbol_declaration + ' = {')
260    cpp_code.indent()
261    step = 16
262    for i in xrange(0, len(data), step):
263        x = array.array('B', data[i:i+step])
264        cpp_code(''.join('%d,' % d for d in x))
265    cpp_code.dedent()
266    cpp_code('};')
267    if namespace is not None:
268        cpp_code('}')
269
270def Blob(blob_path, symbol):
271    '''
272    Embed an arbitrary blob into the gem5 executable,
273    and make it accessible to C++ as a byte array.
274    '''
275    blob_path = os.path.abspath(blob_path)
276    blob_out_dir = os.path.join(env['BUILDDIR'], 'blobs')
277    path_noext = joinpath(blob_out_dir, symbol)
278    cpp_path = path_noext + '.cc'
279    hpp_path = path_noext + '.hh'
280    def embedBlob(target, source, env):
281        data = file(str(source[0]), 'r').read()
282        cpp_code = code_formatter()
283        hpp_code = code_formatter()
284        blobToCpp(data, symbol, cpp_code, hpp_code, namespace='Blobs')
285        cpp_path = str(target[0])
286        hpp_path = str(target[1])
287        cpp_dir = os.path.split(cpp_path)[0]
288        if not os.path.exists(cpp_dir):
289            os.makedirs(cpp_dir)
290        cpp_code.write(cpp_path)
291        hpp_code.write(hpp_path)
292    env.Command([cpp_path, hpp_path], blob_path,
293                MakeAction(embedBlob, Transform("EMBED BLOB")))
294    Source(cpp_path)
295
296def GdbXml(xml_id, symbol):
297    Blob(joinpath(gdb_xml_dir, xml_id), symbol)
298
299class Source(SourceFile):
300    ungrouped_tag = 'No link group'
301    source_groups = set()
302
303    _current_group_tag = ungrouped_tag
304
305    @staticmethod
306    def link_group_tag(group):
307        return 'link group: %s' % group
308
309    @classmethod
310    def set_group(cls, group):
311        new_tag = Source.link_group_tag(group)
312        Source._current_group_tag = new_tag
313        Source.source_groups.add(group)
314
315    def _add_link_group_tag(self):
316        self.tags.add(Source._current_group_tag)
317
318    '''Add a c/c++ source file to the build'''
319    def __init__(self, source, tags=None, add_tags=None):
320        '''specify the source file, and any tags'''
321        super(Source, self).__init__(source, tags, add_tags)
322        self._add_link_group_tag()
323
324class PySource(SourceFile):
325    '''Add a python source file to the named package'''
326    invalid_sym_char = re.compile('[^A-z0-9_]')
327    modules = {}
328    tnodes = {}
329    symnames = {}
330
331    def __init__(self, package, source, tags=None, add_tags=None):
332        '''specify the python package, the source file, and any tags'''
333        super(PySource, self).__init__(source, tags, add_tags)
334
335        modname,ext = self.extname
336        assert ext == 'py'
337
338        if package:
339            path = package.split('.')
340        else:
341            path = []
342
343        modpath = path[:]
344        if modname != '__init__':
345            modpath += [ modname ]
346        modpath = '.'.join(modpath)
347
348        arcpath = path + [ self.basename ]
349        abspath = self.snode.abspath
350        if not exists(abspath):
351            abspath = self.tnode.abspath
352
353        self.package = package
354        self.modname = modname
355        self.modpath = modpath
356        self.arcname = joinpath(*arcpath)
357        self.abspath = abspath
358        self.compiled = File(self.filename + 'c')
359        self.cpp = File(self.filename + '.cc')
360        self.symname = PySource.invalid_sym_char.sub('_', modpath)
361
362        PySource.modules[modpath] = self
363        PySource.tnodes[self.tnode] = self
364        PySource.symnames[self.symname] = self
365
366class SimObject(PySource):
367    '''Add a SimObject python file as a python source object and add
368    it to a list of sim object modules'''
369
370    fixed = False
371    modnames = []
372
373    def __init__(self, source, tags=None, add_tags=None):
374        '''Specify the source file and any tags (automatically in
375        the m5.objects package)'''
376        super(SimObject, self).__init__('m5.objects', source, tags, add_tags)
377        if self.fixed:
378            raise AttributeError, "Too late to call SimObject now."
379
380        bisect.insort_right(SimObject.modnames, self.modname)
381
382class ProtoBuf(SourceFile):
383    '''Add a Protocol Buffer to build'''
384
385    def __init__(self, source, tags=None, add_tags=None):
386        '''Specify the source file, and any tags'''
387        super(ProtoBuf, self).__init__(source, tags, add_tags)
388
389        # Get the file name and the extension
390        modname,ext = self.extname
391        assert ext == 'proto'
392
393        # Currently, we stick to generating the C++ headers, so we
394        # only need to track the source and header.
395        self.cc_file = File(modname + '.pb.cc')
396        self.hh_file = File(modname + '.pb.h')
397
398
399exectuable_classes = []
400class ExecutableMeta(type):
401    '''Meta class for Executables.'''
402    all = []
403
404    def __init__(cls, name, bases, d):
405        if not d.pop('abstract', False):
406            ExecutableMeta.all.append(cls)
407        super(ExecutableMeta, cls).__init__(name, bases, d)
408
409        cls.all = []
410
411class Executable(object):
412    '''Base class for creating an executable from sources.'''
413    __metaclass__ = ExecutableMeta
414
415    abstract = True
416
417    def __init__(self, target, *srcs_and_filts):
418        '''Specify the target name and any sources. Sources that are
419        not SourceFiles are evalued with Source().'''
420        super(Executable, self).__init__()
421        self.all.append(self)
422        self.target = target
423
424        isFilter = lambda arg: isinstance(arg, SourceFilter)
425        self.filters = filter(isFilter, srcs_and_filts)
426        sources = filter(lambda a: not isFilter(a), srcs_and_filts)
427
428        srcs = SourceList()
429        for src in sources:
430            if not isinstance(src, SourceFile):
431                src = Source(src, tags=[])
432            srcs.append(src)
433
434        self.sources = srcs
435        self.dir = Dir('.')
436
437    def path(self, env):
438        return self.dir.File(self.target + '.' + env['EXE_SUFFIX'])
439
440    def srcs_to_objs(self, env, sources):
441        return list([ s.static(env) for s in sources ])
442
443    @classmethod
444    def declare_all(cls, env):
445        return list([ instance.declare(env) for instance in cls.all ])
446
447    def declare(self, env, objs=None):
448        if objs is None:
449            objs = self.srcs_to_objs(env, self.sources)
450
451        if env['STRIP_EXES']:
452            stripped = self.path(env)
453            unstripped = env.File(str(stripped) + '.unstripped')
454            if sys.platform == 'sunos5':
455                cmd = 'cp $SOURCE $TARGET; strip $TARGET'
456            else:
457                cmd = 'strip $SOURCE -o $TARGET'
458            env.Program(unstripped, objs)
459            return env.Command(stripped, unstripped,
460                               MakeAction(cmd, Transform("STRIP")))
461        else:
462            return env.Program(self.path(env), objs)
463
464class UnitTest(Executable):
465    '''Create a UnitTest'''
466    def __init__(self, target, *srcs_and_filts, **kwargs):
467        super(UnitTest, self).__init__(target, *srcs_and_filts)
468
469        self.main = kwargs.get('main', False)
470
471    def declare(self, env):
472        sources = list(self.sources)
473        for f in self.filters:
474            sources = Source.all.apply_filter(f)
475        objs = self.srcs_to_objs(env, sources) + env['STATIC_OBJS']
476        if self.main:
477            objs += env['MAIN_OBJS']
478        return super(UnitTest, self).declare(env, objs)
479
480class GTest(Executable):
481    '''Create a unit test based on the google test framework.'''
482    all = []
483    def __init__(self, *srcs_and_filts, **kwargs):
484        super(GTest, self).__init__(*srcs_and_filts)
485
486        self.skip_lib = kwargs.pop('skip_lib', False)
487
488    @classmethod
489    def declare_all(cls, env):
490        env = env.Clone()
491        env.Append(LIBS=env['GTEST_LIBS'])
492        env.Append(CPPFLAGS=env['GTEST_CPPFLAGS'])
493        env['GTEST_LIB_SOURCES'] = Source.all.with_tag('gtest lib')
494        env['GTEST_OUT_DIR'] = \
495            Dir(env['BUILDDIR']).Dir('unittests.' + env['EXE_SUFFIX'])
496        return super(GTest, cls).declare_all(env)
497
498    def declare(self, env):
499        sources = list(self.sources)
500        if not self.skip_lib:
501            sources += env['GTEST_LIB_SOURCES']
502        for f in self.filters:
503            sources += Source.all.apply_filter(f)
504        objs = self.srcs_to_objs(env, sources)
505
506        binary = super(GTest, self).declare(env, objs)
507
508        out_dir = env['GTEST_OUT_DIR']
509        xml_file = out_dir.Dir(str(self.dir)).File(self.target + '.xml')
510        AlwaysBuild(env.Command(xml_file, binary,
511            "${SOURCES[0]} --gtest_output=xml:${TARGETS[0]}"))
512
513        return binary
514
515class Gem5(Executable):
516    '''Create a gem5 executable.'''
517
518    def __init__(self, target):
519        super(Gem5, self).__init__(target)
520
521    def declare(self, env):
522        objs = env['MAIN_OBJS'] + env['STATIC_OBJS']
523        return super(Gem5, self).declare(env, objs)
524
525
526# Children should have access
527Export('Blob')
528Export('GdbXml')
529Export('Source')
530Export('PySource')
531Export('SimObject')
532Export('ProtoBuf')
533Export('Executable')
534Export('UnitTest')
535Export('GTest')
536
537########################################################################
538#
539# Debug Flags
540#
541debug_flags = {}
542def DebugFlag(name, desc=None):
543    if name in debug_flags:
544        raise AttributeError, "Flag %s already specified" % name
545    debug_flags[name] = (name, (), desc)
546
547def CompoundFlag(name, flags, desc=None):
548    if name in debug_flags:
549        raise AttributeError, "Flag %s already specified" % name
550
551    compound = tuple(flags)
552    debug_flags[name] = (name, compound, desc)
553
554Export('DebugFlag')
555Export('CompoundFlag')
556
557########################################################################
558#
559# Set some compiler variables
560#
561
562# Include file paths are rooted in this directory.  SCons will
563# automatically expand '.' to refer to both the source directory and
564# the corresponding build directory to pick up generated include
565# files.
566env.Append(CPPPATH=Dir('.'))
567
568for extra_dir in extras_dir_list:
569    env.Append(CPPPATH=Dir(extra_dir))
570
571# Workaround for bug in SCons version > 0.97d20071212
572# Scons bug id: 2006 gem5 Bug id: 308
573for root, dirs, files in os.walk(base_dir, topdown=True):
574    Dir(root[len(base_dir) + 1:])
575
576########################################################################
577#
578# Walk the tree and execute all SConscripts in subdirectories
579#
580
581here = Dir('.').srcnode().abspath
582for root, dirs, files in os.walk(base_dir, topdown=True):
583    if root == here:
584        # we don't want to recurse back into this SConscript
585        continue
586
587    if 'SConscript' in files:
588        build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
589        Source.set_group(build_dir)
590        SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
591
592for extra_dir in extras_dir_list:
593    prefix_len = len(dirname(extra_dir)) + 1
594
595    # Also add the corresponding build directory to pick up generated
596    # include files.
597    env.Append(CPPPATH=Dir(joinpath(env['BUILDDIR'], extra_dir[prefix_len:])))
598
599    for root, dirs, files in os.walk(extra_dir, topdown=True):
600        # if build lives in the extras directory, don't walk down it
601        if 'build' in dirs:
602            dirs.remove('build')
603
604        if 'SConscript' in files:
605            build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
606            SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
607
608for opt in export_vars:
609    env.ConfigFile(opt)
610
611def makeTheISA(source, target, env):
612    isas = [ src.get_contents() for src in source ]
613    target_isa = env['TARGET_ISA']
614    def define(isa):
615        return isa.upper() + '_ISA'
616
617    def namespace(isa):
618        return isa[0].upper() + isa[1:].lower() + 'ISA'
619
620
621    code = code_formatter()
622    code('''\
623#ifndef __CONFIG_THE_ISA_HH__
624#define __CONFIG_THE_ISA_HH__
625
626''')
627
628    # create defines for the preprocessing and compile-time determination
629    for i,isa in enumerate(isas):
630        code('#define $0 $1', define(isa), i + 1)
631    code()
632
633    # create an enum for any run-time determination of the ISA, we
634    # reuse the same name as the namespaces
635    code('enum class Arch {')
636    for i,isa in enumerate(isas):
637        if i + 1 == len(isas):
638            code('  $0 = $1', namespace(isa), define(isa))
639        else:
640            code('  $0 = $1,', namespace(isa), define(isa))
641    code('};')
642
643    code('''
644
645#define THE_ISA ${{define(target_isa)}}
646#define TheISA ${{namespace(target_isa)}}
647#define THE_ISA_STR "${{target_isa}}"
648
649#endif // __CONFIG_THE_ISA_HH__''')
650
651    code.write(str(target[0]))
652
653env.Command('config/the_isa.hh', map(Value, all_isa_list),
654            MakeAction(makeTheISA, Transform("CFG ISA", 0)))
655
656def makeTheGPUISA(source, target, env):
657    isas = [ src.get_contents() for src in source ]
658    target_gpu_isa = env['TARGET_GPU_ISA']
659    def define(isa):
660        return isa.upper() + '_ISA'
661
662    def namespace(isa):
663        return isa[0].upper() + isa[1:].lower() + 'ISA'
664
665
666    code = code_formatter()
667    code('''\
668#ifndef __CONFIG_THE_GPU_ISA_HH__
669#define __CONFIG_THE_GPU_ISA_HH__
670
671''')
672
673    # create defines for the preprocessing and compile-time determination
674    for i,isa in enumerate(isas):
675        code('#define $0 $1', define(isa), i + 1)
676    code()
677
678    # create an enum for any run-time determination of the ISA, we
679    # reuse the same name as the namespaces
680    code('enum class GPUArch {')
681    for i,isa in enumerate(isas):
682        if i + 1 == len(isas):
683            code('  $0 = $1', namespace(isa), define(isa))
684        else:
685            code('  $0 = $1,', namespace(isa), define(isa))
686    code('};')
687
688    code('''
689
690#define THE_GPU_ISA ${{define(target_gpu_isa)}}
691#define TheGpuISA ${{namespace(target_gpu_isa)}}
692#define THE_GPU_ISA_STR "${{target_gpu_isa}}"
693
694#endif // __CONFIG_THE_GPU_ISA_HH__''')
695
696    code.write(str(target[0]))
697
698env.Command('config/the_gpu_isa.hh', map(Value, all_gpu_isa_list),
699            MakeAction(makeTheGPUISA, Transform("CFG ISA", 0)))
700
701########################################################################
702#
703# Prevent any SimObjects from being added after this point, they
704# should all have been added in the SConscripts above
705#
706SimObject.fixed = True
707
708class DictImporter(object):
709    '''This importer takes a dictionary of arbitrary module names that
710    map to arbitrary filenames.'''
711    def __init__(self, modules):
712        self.modules = modules
713        self.installed = set()
714
715    def __del__(self):
716        self.unload()
717
718    def unload(self):
719        import sys
720        for module in self.installed:
721            del sys.modules[module]
722        self.installed = set()
723
724    def find_module(self, fullname, path):
725        if fullname == 'm5.defines':
726            return self
727
728        if fullname == 'm5.objects':
729            return self
730
731        if fullname.startswith('_m5'):
732            return None
733
734        source = self.modules.get(fullname, None)
735        if source is not None and fullname.startswith('m5.objects'):
736            return self
737
738        return None
739
740    def load_module(self, fullname):
741        mod = imp.new_module(fullname)
742        sys.modules[fullname] = mod
743        self.installed.add(fullname)
744
745        mod.__loader__ = self
746        if fullname == 'm5.objects':
747            mod.__path__ = fullname.split('.')
748            return mod
749
750        if fullname == 'm5.defines':
751            mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
752            return mod
753
754        source = self.modules[fullname]
755        if source.modname == '__init__':
756            mod.__path__ = source.modpath
757        mod.__file__ = source.abspath
758
759        exec file(source.abspath, 'r') in mod.__dict__
760
761        return mod
762
763import m5.SimObject
764import m5.params
765from m5.util import code_formatter
766
767m5.SimObject.clear()
768m5.params.clear()
769
770# install the python importer so we can grab stuff from the source
771# tree itself.  We can't have SimObjects added after this point or
772# else we won't know about them for the rest of the stuff.
773importer = DictImporter(PySource.modules)
774sys.meta_path[0:0] = [ importer ]
775
776# import all sim objects so we can populate the all_objects list
777# make sure that we're working with a list, then let's sort it
778for modname in SimObject.modnames:
779    exec('from m5.objects import %s' % modname)
780
781# we need to unload all of the currently imported modules so that they
782# will be re-imported the next time the sconscript is run
783importer.unload()
784sys.meta_path.remove(importer)
785
786sim_objects = m5.SimObject.allClasses
787all_enums = m5.params.allEnums
788
789for name,obj in sorted(sim_objects.iteritems()):
790    for param in obj._params.local.values():
791        # load the ptype attribute now because it depends on the
792        # current version of SimObject.allClasses, but when scons
793        # actually uses the value, all versions of
794        # SimObject.allClasses will have been loaded
795        param.ptype
796
797########################################################################
798#
799# calculate extra dependencies
800#
801module_depends = ["m5", "m5.SimObject", "m5.params"]
802depends = [ PySource.modules[dep].snode for dep in module_depends ]
803depends.sort(key = lambda x: x.name)
804
805########################################################################
806#
807# Commands for the basic automatically generated python files
808#
809
810# Generate Python file containing a dict specifying the current
811# buildEnv flags.
812def makeDefinesPyFile(target, source, env):
813    build_env = source[0].get_contents()
814
815    code = code_formatter()
816    code("""
817import _m5.core
818import m5.util
819
820buildEnv = m5.util.SmartDict($build_env)
821
822compileDate = _m5.core.compileDate
823_globals = globals()
824for key,val in _m5.core.__dict__.iteritems():
825    if key.startswith('flag_'):
826        flag = key[5:]
827        _globals[flag] = val
828del _globals
829""")
830    code.write(target[0].abspath)
831
832defines_info = Value(build_env)
833# Generate a file with all of the compile options in it
834env.Command('python/m5/defines.py', defines_info,
835            MakeAction(makeDefinesPyFile, Transform("DEFINES", 0)))
836PySource('m5', 'python/m5/defines.py')
837
838# Generate python file containing info about the M5 source code
839def makeInfoPyFile(target, source, env):
840    code = code_formatter()
841    for src in source:
842        data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
843        code('$src = ${{repr(data)}}')
844    code.write(str(target[0]))
845
846# Generate a file that wraps the basic top level files
847env.Command('python/m5/info.py',
848            [ '#/COPYING', '#/LICENSE', '#/README', ],
849            MakeAction(makeInfoPyFile, Transform("INFO")))
850PySource('m5', 'python/m5/info.py')
851
852########################################################################
853#
854# Create all of the SimObject param headers and enum headers
855#
856
857def createSimObjectParamStruct(target, source, env):
858    assert len(target) == 1 and len(source) == 1
859
860    name = source[0].get_text_contents()
861    obj = sim_objects[name]
862
863    code = code_formatter()
864    obj.cxx_param_decl(code)
865    code.write(target[0].abspath)
866
867def createSimObjectCxxConfig(is_header):
868    def body(target, source, env):
869        assert len(target) == 1 and len(source) == 1
870
871        name = str(source[0].get_contents())
872        obj = sim_objects[name]
873
874        code = code_formatter()
875        obj.cxx_config_param_file(code, is_header)
876        code.write(target[0].abspath)
877    return body
878
879def createEnumStrings(target, source, env):
880    assert len(target) == 1 and len(source) == 2
881
882    name = source[0].get_text_contents()
883    use_python = source[1].read()
884    obj = all_enums[name]
885
886    code = code_formatter()
887    obj.cxx_def(code)
888    if use_python:
889        obj.pybind_def(code)
890    code.write(target[0].abspath)
891
892def createEnumDecls(target, source, env):
893    assert len(target) == 1 and len(source) == 1
894
895    name = source[0].get_text_contents()
896    obj = all_enums[name]
897
898    code = code_formatter()
899    obj.cxx_decl(code)
900    code.write(target[0].abspath)
901
902def createSimObjectPyBindWrapper(target, source, env):
903    name = source[0].get_text_contents()
904    obj = sim_objects[name]
905
906    code = code_formatter()
907    obj.pybind_decl(code)
908    code.write(target[0].abspath)
909
910# Generate all of the SimObject param C++ struct header files
911params_hh_files = []
912for name,simobj in sorted(sim_objects.iteritems()):
913    py_source = PySource.modules[simobj.__module__]
914    extra_deps = [ py_source.tnode ]
915
916    hh_file = File('params/%s.hh' % name)
917    params_hh_files.append(hh_file)
918    env.Command(hh_file, Value(name),
919                MakeAction(createSimObjectParamStruct, Transform("SO PARAM")))
920    env.Depends(hh_file, depends + extra_deps)
921
922# C++ parameter description files
923if GetOption('with_cxx_config'):
924    for name,simobj in sorted(sim_objects.iteritems()):
925        py_source = PySource.modules[simobj.__module__]
926        extra_deps = [ py_source.tnode ]
927
928        cxx_config_hh_file = File('cxx_config/%s.hh' % name)
929        cxx_config_cc_file = File('cxx_config/%s.cc' % name)
930        env.Command(cxx_config_hh_file, Value(name),
931                    MakeAction(createSimObjectCxxConfig(True),
932                    Transform("CXXCPRHH")))
933        env.Command(cxx_config_cc_file, Value(name),
934                    MakeAction(createSimObjectCxxConfig(False),
935                    Transform("CXXCPRCC")))
936        env.Depends(cxx_config_hh_file, depends + extra_deps +
937                    [File('params/%s.hh' % name), File('sim/cxx_config.hh')])
938        env.Depends(cxx_config_cc_file, depends + extra_deps +
939                    [cxx_config_hh_file])
940        Source(cxx_config_cc_file)
941
942    cxx_config_init_cc_file = File('cxx_config/init.cc')
943
944    def createCxxConfigInitCC(target, source, env):
945        assert len(target) == 1 and len(source) == 1
946
947        code = code_formatter()
948
949        for name,simobj in sorted(sim_objects.iteritems()):
950            if not hasattr(simobj, 'abstract') or not simobj.abstract:
951                code('#include "cxx_config/${name}.hh"')
952        code()
953        code('void cxxConfigInit()')
954        code('{')
955        code.indent()
956        for name,simobj in sorted(sim_objects.iteritems()):
957            not_abstract = not hasattr(simobj, 'abstract') or \
958                not simobj.abstract
959            if not_abstract and 'type' in simobj.__dict__:
960                code('cxx_config_directory["${name}"] = '
961                     '${name}CxxConfigParams::makeDirectoryEntry();')
962        code.dedent()
963        code('}')
964        code.write(target[0].abspath)
965
966    py_source = PySource.modules[simobj.__module__]
967    extra_deps = [ py_source.tnode ]
968    env.Command(cxx_config_init_cc_file, Value(name),
969        MakeAction(createCxxConfigInitCC, Transform("CXXCINIT")))
970    cxx_param_hh_files = ["cxx_config/%s.hh" % simobj
971        for name,simobj in sorted(sim_objects.iteritems())
972        if not hasattr(simobj, 'abstract') or not simobj.abstract]
973    Depends(cxx_config_init_cc_file, cxx_param_hh_files +
974            [File('sim/cxx_config.hh')])
975    Source(cxx_config_init_cc_file)
976
977# Generate all enum header files
978for name,enum in sorted(all_enums.iteritems()):
979    py_source = PySource.modules[enum.__module__]
980    extra_deps = [ py_source.tnode ]
981
982    cc_file = File('enums/%s.cc' % name)
983    env.Command(cc_file, [Value(name), Value(env['USE_PYTHON'])],
984                MakeAction(createEnumStrings, Transform("ENUM STR")))
985    env.Depends(cc_file, depends + extra_deps)
986    Source(cc_file)
987
988    hh_file = File('enums/%s.hh' % name)
989    env.Command(hh_file, Value(name),
990                MakeAction(createEnumDecls, Transform("ENUMDECL")))
991    env.Depends(hh_file, depends + extra_deps)
992
993# Generate SimObject Python bindings wrapper files
994if env['USE_PYTHON']:
995    for name,simobj in sorted(sim_objects.iteritems()):
996        py_source = PySource.modules[simobj.__module__]
997        extra_deps = [ py_source.tnode ]
998        cc_file = File('python/_m5/param_%s.cc' % name)
999        env.Command(cc_file, Value(name),
1000                    MakeAction(createSimObjectPyBindWrapper,
1001                               Transform("SO PyBind")))
1002        env.Depends(cc_file, depends + extra_deps)
1003        Source(cc_file)
1004
1005# Build all protocol buffers if we have got protoc and protobuf available
1006if env['HAVE_PROTOBUF']:
1007    for proto in ProtoBuf.all:
1008        # Use both the source and header as the target, and the .proto
1009        # file as the source. When executing the protoc compiler, also
1010        # specify the proto_path to avoid having the generated files
1011        # include the path.
1012        env.Command([proto.cc_file, proto.hh_file], proto.tnode,
1013                    MakeAction('$PROTOC --cpp_out ${TARGET.dir} '
1014                               '--proto_path ${SOURCE.dir} $SOURCE',
1015                               Transform("PROTOC")))
1016
1017        # Add the C++ source file
1018        Source(proto.cc_file, tags=proto.tags)
1019elif ProtoBuf.all:
1020    print('Got protobuf to build, but lacks support!')
1021    Exit(1)
1022
1023#
1024# Handle debug flags
1025#
1026def makeDebugFlagCC(target, source, env):
1027    assert(len(target) == 1 and len(source) == 1)
1028
1029    code = code_formatter()
1030
1031    # delay definition of CompoundFlags until after all the definition
1032    # of all constituent SimpleFlags
1033    comp_code = code_formatter()
1034
1035    # file header
1036    code('''
1037/*
1038 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
1039 */
1040
1041#include "base/debug.hh"
1042
1043namespace Debug {
1044
1045''')
1046
1047    for name, flag in sorted(source[0].read().iteritems()):
1048        n, compound, desc = flag
1049        assert n == name
1050
1051        if not compound:
1052            code('SimpleFlag $name("$name", "$desc");')
1053        else:
1054            comp_code('CompoundFlag $name("$name", "$desc",')
1055            comp_code.indent()
1056            last = len(compound) - 1
1057            for i,flag in enumerate(compound):
1058                if i != last:
1059                    comp_code('&$flag,')
1060                else:
1061                    comp_code('&$flag);')
1062            comp_code.dedent()
1063
1064    code.append(comp_code)
1065    code()
1066    code('} // namespace Debug')
1067
1068    code.write(str(target[0]))
1069
1070def makeDebugFlagHH(target, source, env):
1071    assert(len(target) == 1 and len(source) == 1)
1072
1073    val = eval(source[0].get_contents())
1074    name, compound, desc = val
1075
1076    code = code_formatter()
1077
1078    # file header boilerplate
1079    code('''\
1080/*
1081 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
1082 */
1083
1084#ifndef __DEBUG_${name}_HH__
1085#define __DEBUG_${name}_HH__
1086
1087namespace Debug {
1088''')
1089
1090    if compound:
1091        code('class CompoundFlag;')
1092    code('class SimpleFlag;')
1093
1094    if compound:
1095        code('extern CompoundFlag $name;')
1096        for flag in compound:
1097            code('extern SimpleFlag $flag;')
1098    else:
1099        code('extern SimpleFlag $name;')
1100
1101    code('''
1102}
1103
1104#endif // __DEBUG_${name}_HH__
1105''')
1106
1107    code.write(str(target[0]))
1108
1109for name,flag in sorted(debug_flags.iteritems()):
1110    n, compound, desc = flag
1111    assert n == name
1112
1113    hh_file = 'debug/%s.hh' % name
1114    env.Command(hh_file, Value(flag),
1115                MakeAction(makeDebugFlagHH, Transform("TRACING", 0)))
1116
1117env.Command('debug/flags.cc', Value(debug_flags),
1118            MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
1119Source('debug/flags.cc')
1120
1121# version tags
1122tags = \
1123env.Command('sim/tags.cc', None,
1124            MakeAction('util/cpt_upgrader.py --get-cc-file > $TARGET',
1125                       Transform("VER TAGS")))
1126env.AlwaysBuild(tags)
1127
1128# Embed python files.  All .py files that have been indicated by a
1129# PySource() call in a SConscript need to be embedded into the M5
1130# library.  To do that, we compile the file to byte code, marshal the
1131# byte code, compress it, and then generate a c++ file that
1132# inserts the result into an array.
1133def embedPyFile(target, source, env):
1134    def c_str(string):
1135        if string is None:
1136            return "0"
1137        return '"%s"' % string
1138
1139    '''Action function to compile a .py into a code object, marshal
1140    it, compress it, and stick it into an asm file so the code appears
1141    as just bytes with a label in the data section'''
1142
1143    src = file(str(source[0]), 'r').read()
1144
1145    pysource = PySource.tnodes[source[0]]
1146    compiled = compile(src, pysource.abspath, 'exec')
1147    marshalled = marshal.dumps(compiled)
1148    compressed = zlib.compress(marshalled)
1149    data = compressed
1150    sym = pysource.symname
1151
1152    code = code_formatter()
1153    code('''\
1154#include "sim/init.hh"
1155
1156namespace {
1157
1158''')
1159    blobToCpp(data, 'data_' + sym, code)
1160    code('''\
1161
1162
1163EmbeddedPython embedded_${sym}(
1164    ${{c_str(pysource.arcname)}},
1165    ${{c_str(pysource.abspath)}},
1166    ${{c_str(pysource.modpath)}},
1167    data_${sym},
1168    ${{len(data)}},
1169    ${{len(marshalled)}});
1170
1171} // anonymous namespace
1172''')
1173    code.write(str(target[0]))
1174
1175for source in PySource.all:
1176    env.Command(source.cpp, source.tnode,
1177                MakeAction(embedPyFile, Transform("EMBED PY")))
1178    Source(source.cpp, tags=source.tags, add_tags='python')
1179
1180########################################################################
1181#
1182# Define binaries.  Each different build type (debug, opt, etc.) gets
1183# a slightly different build environment.
1184#
1185
1186# List of constructed environments to pass back to SConstruct
1187date_source = Source('base/date.cc', tags=[])
1188
1189gem5_binary = Gem5('gem5')
1190
1191# Function to create a new build environment as clone of current
1192# environment 'env' with modified object suffix and optional stripped
1193# binary.  Additional keyword arguments are appended to corresponding
1194# build environment vars.
1195def makeEnv(env, label, objsfx, strip=False, disable_partial=False, **kwargs):
1196    # SCons doesn't know to append a library suffix when there is a '.' in the
1197    # name.  Use '_' instead.
1198    libname = 'gem5_' + label
1199    secondary_exename = 'm5.' + label
1200
1201    new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
1202    new_env.Label = label
1203    new_env.Append(**kwargs)
1204
1205    lib_sources = Source.all.with_tag('gem5 lib')
1206
1207    # Without Python, leave out all Python content from the library
1208    # builds.  The option doesn't affect gem5 built as a program
1209    if GetOption('without_python'):
1210        lib_sources = lib_sources.without_tag('python')
1211
1212    static_objs = []
1213    shared_objs = []
1214
1215    for s in lib_sources.with_tag(Source.ungrouped_tag):
1216        static_objs.append(s.static(new_env))
1217        shared_objs.append(s.shared(new_env))
1218
1219    for group in Source.source_groups:
1220        srcs = lib_sources.with_tag(Source.link_group_tag(group))
1221        if not srcs:
1222            continue
1223
1224        group_static = [ s.static(new_env) for s in srcs ]
1225        group_shared = [ s.shared(new_env) for s in srcs ]
1226
1227        # If partial linking is disabled, add these sources to the build
1228        # directly, and short circuit this loop.
1229        if disable_partial:
1230            static_objs.extend(group_static)
1231            shared_objs.extend(group_shared)
1232            continue
1233
1234        # Set up the static partially linked objects.
1235        file_name = new_env.subst("${OBJPREFIX}lib${OBJSUFFIX}.partial")
1236        target = File(joinpath(group, file_name))
1237        partial = env.PartialStatic(target=target, source=group_static)
1238        static_objs.extend(partial)
1239
1240        # Set up the shared partially linked objects.
1241        file_name = new_env.subst("${SHOBJPREFIX}lib${SHOBJSUFFIX}.partial")
1242        target = File(joinpath(group, file_name))
1243        partial = env.PartialShared(target=target, source=group_shared)
1244        shared_objs.extend(partial)
1245
1246    static_date = date_source.static(new_env)
1247    new_env.Depends(static_date, static_objs)
1248    static_objs.extend(static_date)
1249
1250    shared_date = date_source.shared(new_env)
1251    new_env.Depends(shared_date, shared_objs)
1252    shared_objs.extend(shared_date)
1253
1254    main_objs = [ s.static(new_env) for s in Source.all.with_tag('main') ]
1255
1256    # First make a library of everything but main() so other programs can
1257    # link against m5.
1258    static_lib = new_env.StaticLibrary(libname, static_objs)
1259    shared_lib = new_env.SharedLibrary(libname, shared_objs)
1260
1261    # Keep track of the object files generated so far so Executables can
1262    # include them.
1263    new_env['STATIC_OBJS'] = static_objs
1264    new_env['SHARED_OBJS'] = shared_objs
1265    new_env['MAIN_OBJS'] = main_objs
1266
1267    new_env['STATIC_LIB'] = static_lib
1268    new_env['SHARED_LIB'] = shared_lib
1269
1270    # Record some settings for building Executables.
1271    new_env['EXE_SUFFIX'] = label
1272    new_env['STRIP_EXES'] = strip
1273
1274    for cls in ExecutableMeta.all:
1275        cls.declare_all(new_env)
1276
1277    new_env.M5Binary = File(gem5_binary.path(new_env))
1278
1279    new_env.Command(secondary_exename, new_env.M5Binary,
1280            MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK")))
1281
1282    # Set up regression tests.
1283    SConscript(os.path.join(env.root.abspath, 'tests', 'SConscript'),
1284               variant_dir=Dir('tests').Dir(new_env.Label),
1285               exports={ 'env' : new_env }, duplicate=False)
1286
1287# Start out with the compiler flags common to all compilers,
1288# i.e. they all use -g for opt and -g -pg for prof
1289ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'],
1290           'perf' : ['-g']}
1291
1292# Start out with the linker flags common to all linkers, i.e. -pg for
1293# prof, and -lprofiler for perf. The -lprofile flag is surrounded by
1294# no-as-needed and as-needed as the binutils linker is too clever and
1295# simply doesn't link to the library otherwise.
1296ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'],
1297           'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']}
1298
1299# For Link Time Optimization, the optimisation flags used to compile
1300# individual files are decoupled from those used at link time
1301# (i.e. you can compile with -O3 and perform LTO with -O0), so we need
1302# to also update the linker flags based on the target.
1303if env['GCC']:
1304    if sys.platform == 'sunos5':
1305        ccflags['debug'] += ['-gstabs+']
1306    else:
1307        ccflags['debug'] += ['-ggdb3']
1308    ldflags['debug'] += ['-O0']
1309    # opt, fast, prof and perf all share the same cc flags, also add
1310    # the optimization to the ldflags as LTO defers the optimization
1311    # to link time
1312    for target in ['opt', 'fast', 'prof', 'perf']:
1313        ccflags[target] += ['-O3']
1314        ldflags[target] += ['-O3']
1315
1316    ccflags['fast'] += env['LTO_CCFLAGS']
1317    ldflags['fast'] += env['LTO_LDFLAGS']
1318elif env['CLANG']:
1319    ccflags['debug'] += ['-g', '-O0']
1320    # opt, fast, prof and perf all share the same cc flags
1321    for target in ['opt', 'fast', 'prof', 'perf']:
1322        ccflags[target] += ['-O3']
1323else:
1324    print('Unknown compiler, please fix compiler options')
1325    Exit(1)
1326
1327
1328# To speed things up, we only instantiate the build environments we
1329# need.  We try to identify the needed environment for each target; if
1330# we can't, we fall back on instantiating all the environments just to
1331# be safe.
1332target_types = ['debug', 'opt', 'fast', 'prof', 'perf']
1333obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof',
1334              'gpo' : 'perf'}
1335
1336def identifyTarget(t):
1337    ext = t.split('.')[-1]
1338    if ext in target_types:
1339        return ext
1340    if obj2target.has_key(ext):
1341        return obj2target[ext]
1342    match = re.search(r'/tests/([^/]+)/', t)
1343    if match and match.group(1) in target_types:
1344        return match.group(1)
1345    return 'all'
1346
1347needed_envs = [identifyTarget(target) for target in BUILD_TARGETS]
1348if 'all' in needed_envs:
1349    needed_envs += target_types
1350
1351disable_partial = False
1352if env['PLATFORM'] == 'darwin':
1353    # Up until Apple LLVM version 10.0.0 (clang-1000.11.45.5), partial
1354    # linked objects do not expose symbols that are marked with the
1355    # hidden visibility and consequently building gem5 on Mac OS
1356    # fails. As a workaround, we disable partial linking, however, we
1357    # may want to revisit in the future.
1358    disable_partial = True
1359
1360# Debug binary
1361if 'debug' in needed_envs:
1362    makeEnv(env, 'debug', '.do',
1363            CCFLAGS = Split(ccflags['debug']),
1364            CPPDEFINES = ['DEBUG', 'TRACING_ON=1'],
1365            LINKFLAGS = Split(ldflags['debug']),
1366            disable_partial=disable_partial)
1367
1368# Optimized binary
1369if 'opt' in needed_envs:
1370    makeEnv(env, 'opt', '.o',
1371            CCFLAGS = Split(ccflags['opt']),
1372            CPPDEFINES = ['TRACING_ON=1'],
1373            LINKFLAGS = Split(ldflags['opt']),
1374            disable_partial=disable_partial)
1375
1376# "Fast" binary
1377if 'fast' in needed_envs:
1378    disable_partial = disable_partial and \
1379            env.get('BROKEN_INCREMENTAL_LTO', False) and \
1380            GetOption('force_lto')
1381    makeEnv(env, 'fast', '.fo', strip = True,
1382            CCFLAGS = Split(ccflags['fast']),
1383            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1384            LINKFLAGS = Split(ldflags['fast']),
1385            disable_partial=disable_partial)
1386
1387# Profiled binary using gprof
1388if 'prof' in needed_envs:
1389    makeEnv(env, 'prof', '.po',
1390            CCFLAGS = Split(ccflags['prof']),
1391            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1392            LINKFLAGS = Split(ldflags['prof']),
1393            disable_partial=disable_partial)
1394
1395# Profiled binary using google-pprof
1396if 'perf' in needed_envs:
1397    makeEnv(env, 'perf', '.gpo',
1398            CCFLAGS = Split(ccflags['perf']),
1399            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1400            LINKFLAGS = Split(ldflags['perf']),
1401            disable_partial=disable_partial)
1402