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