SConscript revision 13577:70ab75eec40b
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    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