SConscript revision 13709
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        env = env.Clone()
453        env['BIN_RPATH_PREFIX'] = os.path.relpath(
454                env['BUILDDIR'], self.path(env).dir.abspath)
455
456        if env['STRIP_EXES']:
457            stripped = self.path(env)
458            unstripped = env.File(str(stripped) + '.unstripped')
459            if sys.platform == 'sunos5':
460                cmd = 'cp $SOURCE $TARGET; strip $TARGET'
461            else:
462                cmd = 'strip $SOURCE -o $TARGET'
463            env.Program(unstripped, objs)
464            return env.Command(stripped, unstripped,
465                               MakeAction(cmd, Transform("STRIP")))
466        else:
467            return env.Program(self.path(env), objs)
468
469class UnitTest(Executable):
470    '''Create a UnitTest'''
471    def __init__(self, target, *srcs_and_filts, **kwargs):
472        super(UnitTest, self).__init__(target, *srcs_and_filts)
473
474        self.main = kwargs.get('main', False)
475
476    def declare(self, env):
477        sources = list(self.sources)
478        for f in self.filters:
479            sources += Source.all.apply_filter(f)
480        objs = self.srcs_to_objs(env, sources) + env['STATIC_OBJS']
481        if self.main:
482            objs += env['MAIN_OBJS']
483        return super(UnitTest, self).declare(env, objs)
484
485class GTest(Executable):
486    '''Create a unit test based on the google test framework.'''
487    all = []
488    def __init__(self, *srcs_and_filts, **kwargs):
489        super(GTest, self).__init__(*srcs_and_filts)
490
491        self.skip_lib = kwargs.pop('skip_lib', False)
492
493    @classmethod
494    def declare_all(cls, env):
495        env = env.Clone()
496        env.Append(LIBS=env['GTEST_LIBS'])
497        env.Append(CPPFLAGS=env['GTEST_CPPFLAGS'])
498        env['GTEST_LIB_SOURCES'] = Source.all.with_tag('gtest lib')
499        env['GTEST_OUT_DIR'] = \
500            Dir(env['BUILDDIR']).Dir('unittests.' + env['EXE_SUFFIX'])
501        return super(GTest, cls).declare_all(env)
502
503    def declare(self, env):
504        sources = list(self.sources)
505        if not self.skip_lib:
506            sources += env['GTEST_LIB_SOURCES']
507        for f in self.filters:
508            sources += Source.all.apply_filter(f)
509        objs = self.srcs_to_objs(env, sources)
510
511        binary = super(GTest, self).declare(env, objs)
512
513        out_dir = env['GTEST_OUT_DIR']
514        xml_file = out_dir.Dir(str(self.dir)).File(self.target + '.xml')
515        AlwaysBuild(env.Command(xml_file, binary,
516            "${SOURCES[0]} --gtest_output=xml:${TARGETS[0]}"))
517
518        return binary
519
520class Gem5(Executable):
521    '''Create a gem5 executable.'''
522
523    def __init__(self, target):
524        super(Gem5, self).__init__(target)
525
526    def declare(self, env):
527        objs = env['MAIN_OBJS'] + env['STATIC_OBJS']
528        return super(Gem5, self).declare(env, objs)
529
530
531# Children should have access
532Export('Blob')
533Export('GdbXml')
534Export('Source')
535Export('PySource')
536Export('SimObject')
537Export('ProtoBuf')
538Export('Executable')
539Export('UnitTest')
540Export('GTest')
541
542########################################################################
543#
544# Debug Flags
545#
546debug_flags = {}
547def DebugFlag(name, desc=None):
548    if name in debug_flags:
549        raise AttributeError, "Flag %s already specified" % name
550    debug_flags[name] = (name, (), desc)
551
552def CompoundFlag(name, flags, desc=None):
553    if name in debug_flags:
554        raise AttributeError, "Flag %s already specified" % name
555
556    compound = tuple(flags)
557    debug_flags[name] = (name, compound, desc)
558
559Export('DebugFlag')
560Export('CompoundFlag')
561
562########################################################################
563#
564# Set some compiler variables
565#
566
567# Include file paths are rooted in this directory.  SCons will
568# automatically expand '.' to refer to both the source directory and
569# the corresponding build directory to pick up generated include
570# files.
571env.Append(CPPPATH=Dir('.'))
572
573for extra_dir in extras_dir_list:
574    env.Append(CPPPATH=Dir(extra_dir))
575
576# Workaround for bug in SCons version > 0.97d20071212
577# Scons bug id: 2006 gem5 Bug id: 308
578for root, dirs, files in os.walk(base_dir, topdown=True):
579    Dir(root[len(base_dir) + 1:])
580
581########################################################################
582#
583# Walk the tree and execute all SConscripts in subdirectories
584#
585
586here = Dir('.').srcnode().abspath
587for root, dirs, files in os.walk(base_dir, topdown=True):
588    if root == here:
589        # we don't want to recurse back into this SConscript
590        continue
591
592    if 'SConscript' in files:
593        build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
594        Source.set_group(build_dir)
595        SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
596
597for extra_dir in extras_dir_list:
598    prefix_len = len(dirname(extra_dir)) + 1
599
600    # Also add the corresponding build directory to pick up generated
601    # include files.
602    env.Append(CPPPATH=Dir(joinpath(env['BUILDDIR'], extra_dir[prefix_len:])))
603
604    for root, dirs, files in os.walk(extra_dir, topdown=True):
605        # if build lives in the extras directory, don't walk down it
606        if 'build' in dirs:
607            dirs.remove('build')
608
609        if 'SConscript' in files:
610            build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
611            SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
612
613for opt in export_vars:
614    env.ConfigFile(opt)
615
616def makeTheISA(source, target, env):
617    isas = [ src.get_contents() for src in source ]
618    target_isa = env['TARGET_ISA']
619    def define(isa):
620        return isa.upper() + '_ISA'
621
622    def namespace(isa):
623        return isa[0].upper() + isa[1:].lower() + 'ISA'
624
625
626    code = code_formatter()
627    code('''\
628#ifndef __CONFIG_THE_ISA_HH__
629#define __CONFIG_THE_ISA_HH__
630
631''')
632
633    # create defines for the preprocessing and compile-time determination
634    for i,isa in enumerate(isas):
635        code('#define $0 $1', define(isa), i + 1)
636    code()
637
638    # create an enum for any run-time determination of the ISA, we
639    # reuse the same name as the namespaces
640    code('enum class Arch {')
641    for i,isa in enumerate(isas):
642        if i + 1 == len(isas):
643            code('  $0 = $1', namespace(isa), define(isa))
644        else:
645            code('  $0 = $1,', namespace(isa), define(isa))
646    code('};')
647
648    code('''
649
650#define THE_ISA ${{define(target_isa)}}
651#define TheISA ${{namespace(target_isa)}}
652#define THE_ISA_STR "${{target_isa}}"
653
654#endif // __CONFIG_THE_ISA_HH__''')
655
656    code.write(str(target[0]))
657
658env.Command('config/the_isa.hh', map(Value, all_isa_list),
659            MakeAction(makeTheISA, Transform("CFG ISA", 0)))
660
661def makeTheGPUISA(source, target, env):
662    isas = [ src.get_contents() for src in source ]
663    target_gpu_isa = env['TARGET_GPU_ISA']
664    def define(isa):
665        return isa.upper() + '_ISA'
666
667    def namespace(isa):
668        return isa[0].upper() + isa[1:].lower() + 'ISA'
669
670
671    code = code_formatter()
672    code('''\
673#ifndef __CONFIG_THE_GPU_ISA_HH__
674#define __CONFIG_THE_GPU_ISA_HH__
675
676''')
677
678    # create defines for the preprocessing and compile-time determination
679    for i,isa in enumerate(isas):
680        code('#define $0 $1', define(isa), i + 1)
681    code()
682
683    # create an enum for any run-time determination of the ISA, we
684    # reuse the same name as the namespaces
685    code('enum class GPUArch {')
686    for i,isa in enumerate(isas):
687        if i + 1 == len(isas):
688            code('  $0 = $1', namespace(isa), define(isa))
689        else:
690            code('  $0 = $1,', namespace(isa), define(isa))
691    code('};')
692
693    code('''
694
695#define THE_GPU_ISA ${{define(target_gpu_isa)}}
696#define TheGpuISA ${{namespace(target_gpu_isa)}}
697#define THE_GPU_ISA_STR "${{target_gpu_isa}}"
698
699#endif // __CONFIG_THE_GPU_ISA_HH__''')
700
701    code.write(str(target[0]))
702
703env.Command('config/the_gpu_isa.hh', map(Value, all_gpu_isa_list),
704            MakeAction(makeTheGPUISA, Transform("CFG ISA", 0)))
705
706########################################################################
707#
708# Prevent any SimObjects from being added after this point, they
709# should all have been added in the SConscripts above
710#
711SimObject.fixed = True
712
713class DictImporter(object):
714    '''This importer takes a dictionary of arbitrary module names that
715    map to arbitrary filenames.'''
716    def __init__(self, modules):
717        self.modules = modules
718        self.installed = set()
719
720    def __del__(self):
721        self.unload()
722
723    def unload(self):
724        import sys
725        for module in self.installed:
726            del sys.modules[module]
727        self.installed = set()
728
729    def find_module(self, fullname, path):
730        if fullname == 'm5.defines':
731            return self
732
733        if fullname == 'm5.objects':
734            return self
735
736        if fullname.startswith('_m5'):
737            return None
738
739        source = self.modules.get(fullname, None)
740        if source is not None and fullname.startswith('m5.objects'):
741            return self
742
743        return None
744
745    def load_module(self, fullname):
746        mod = imp.new_module(fullname)
747        sys.modules[fullname] = mod
748        self.installed.add(fullname)
749
750        mod.__loader__ = self
751        if fullname == 'm5.objects':
752            mod.__path__ = fullname.split('.')
753            return mod
754
755        if fullname == 'm5.defines':
756            mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
757            return mod
758
759        source = self.modules[fullname]
760        if source.modname == '__init__':
761            mod.__path__ = source.modpath
762        mod.__file__ = source.abspath
763
764        exec file(source.abspath, 'r') in mod.__dict__
765
766        return mod
767
768import m5.SimObject
769import m5.params
770from m5.util import code_formatter
771
772m5.SimObject.clear()
773m5.params.clear()
774
775# install the python importer so we can grab stuff from the source
776# tree itself.  We can't have SimObjects added after this point or
777# else we won't know about them for the rest of the stuff.
778importer = DictImporter(PySource.modules)
779sys.meta_path[0:0] = [ importer ]
780
781# import all sim objects so we can populate the all_objects list
782# make sure that we're working with a list, then let's sort it
783for modname in SimObject.modnames:
784    exec('from m5.objects import %s' % modname)
785
786# we need to unload all of the currently imported modules so that they
787# will be re-imported the next time the sconscript is run
788importer.unload()
789sys.meta_path.remove(importer)
790
791sim_objects = m5.SimObject.allClasses
792all_enums = m5.params.allEnums
793
794for name,obj in sorted(sim_objects.iteritems()):
795    for param in obj._params.local.values():
796        # load the ptype attribute now because it depends on the
797        # current version of SimObject.allClasses, but when scons
798        # actually uses the value, all versions of
799        # SimObject.allClasses will have been loaded
800        param.ptype
801
802########################################################################
803#
804# calculate extra dependencies
805#
806module_depends = ["m5", "m5.SimObject", "m5.params"]
807depends = [ PySource.modules[dep].snode for dep in module_depends ]
808depends.sort(key = lambda x: x.name)
809
810########################################################################
811#
812# Commands for the basic automatically generated python files
813#
814
815# Generate Python file containing a dict specifying the current
816# buildEnv flags.
817def makeDefinesPyFile(target, source, env):
818    build_env = source[0].get_contents()
819
820    code = code_formatter()
821    code("""
822import _m5.core
823import m5.util
824
825buildEnv = m5.util.SmartDict($build_env)
826
827compileDate = _m5.core.compileDate
828_globals = globals()
829for key,val in _m5.core.__dict__.items():
830    if key.startswith('flag_'):
831        flag = key[5:]
832        _globals[flag] = val
833del _globals
834""")
835    code.write(target[0].abspath)
836
837defines_info = Value(build_env)
838# Generate a file with all of the compile options in it
839env.Command('python/m5/defines.py', defines_info,
840            MakeAction(makeDefinesPyFile, Transform("DEFINES", 0)))
841PySource('m5', 'python/m5/defines.py')
842
843# Generate python file containing info about the M5 source code
844def makeInfoPyFile(target, source, env):
845    code = code_formatter()
846    for src in source:
847        data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
848        code('$src = ${{repr(data)}}')
849    code.write(str(target[0]))
850
851# Generate a file that wraps the basic top level files
852env.Command('python/m5/info.py',
853            [ '#/COPYING', '#/LICENSE', '#/README', ],
854            MakeAction(makeInfoPyFile, Transform("INFO")))
855PySource('m5', 'python/m5/info.py')
856
857########################################################################
858#
859# Create all of the SimObject param headers and enum headers
860#
861
862def createSimObjectParamStruct(target, source, env):
863    assert len(target) == 1 and len(source) == 1
864
865    name = source[0].get_text_contents()
866    obj = sim_objects[name]
867
868    code = code_formatter()
869    obj.cxx_param_decl(code)
870    code.write(target[0].abspath)
871
872def createSimObjectCxxConfig(is_header):
873    def body(target, source, env):
874        assert len(target) == 1 and len(source) == 1
875
876        name = str(source[0].get_contents())
877        obj = sim_objects[name]
878
879        code = code_formatter()
880        obj.cxx_config_param_file(code, is_header)
881        code.write(target[0].abspath)
882    return body
883
884def createEnumStrings(target, source, env):
885    assert len(target) == 1 and len(source) == 2
886
887    name = source[0].get_text_contents()
888    use_python = source[1].read()
889    obj = all_enums[name]
890
891    code = code_formatter()
892    obj.cxx_def(code)
893    if use_python:
894        obj.pybind_def(code)
895    code.write(target[0].abspath)
896
897def createEnumDecls(target, source, env):
898    assert len(target) == 1 and len(source) == 1
899
900    name = source[0].get_text_contents()
901    obj = all_enums[name]
902
903    code = code_formatter()
904    obj.cxx_decl(code)
905    code.write(target[0].abspath)
906
907def createSimObjectPyBindWrapper(target, source, env):
908    name = source[0].get_text_contents()
909    obj = sim_objects[name]
910
911    code = code_formatter()
912    obj.pybind_decl(code)
913    code.write(target[0].abspath)
914
915# Generate all of the SimObject param C++ struct header files
916params_hh_files = []
917for name,simobj in sorted(sim_objects.iteritems()):
918    py_source = PySource.modules[simobj.__module__]
919    extra_deps = [ py_source.tnode ]
920
921    hh_file = File('params/%s.hh' % name)
922    params_hh_files.append(hh_file)
923    env.Command(hh_file, Value(name),
924                MakeAction(createSimObjectParamStruct, Transform("SO PARAM")))
925    env.Depends(hh_file, depends + extra_deps)
926
927# C++ parameter description files
928if GetOption('with_cxx_config'):
929    for name,simobj in sorted(sim_objects.iteritems()):
930        py_source = PySource.modules[simobj.__module__]
931        extra_deps = [ py_source.tnode ]
932
933        cxx_config_hh_file = File('cxx_config/%s.hh' % name)
934        cxx_config_cc_file = File('cxx_config/%s.cc' % name)
935        env.Command(cxx_config_hh_file, Value(name),
936                    MakeAction(createSimObjectCxxConfig(True),
937                    Transform("CXXCPRHH")))
938        env.Command(cxx_config_cc_file, Value(name),
939                    MakeAction(createSimObjectCxxConfig(False),
940                    Transform("CXXCPRCC")))
941        env.Depends(cxx_config_hh_file, depends + extra_deps +
942                    [File('params/%s.hh' % name), File('sim/cxx_config.hh')])
943        env.Depends(cxx_config_cc_file, depends + extra_deps +
944                    [cxx_config_hh_file])
945        Source(cxx_config_cc_file)
946
947    cxx_config_init_cc_file = File('cxx_config/init.cc')
948
949    def createCxxConfigInitCC(target, source, env):
950        assert len(target) == 1 and len(source) == 1
951
952        code = code_formatter()
953
954        for name,simobj in sorted(sim_objects.iteritems()):
955            if not hasattr(simobj, 'abstract') or not simobj.abstract:
956                code('#include "cxx_config/${name}.hh"')
957        code()
958        code('void cxxConfigInit()')
959        code('{')
960        code.indent()
961        for name,simobj in sorted(sim_objects.iteritems()):
962            not_abstract = not hasattr(simobj, 'abstract') or \
963                not simobj.abstract
964            if not_abstract and 'type' in simobj.__dict__:
965                code('cxx_config_directory["${name}"] = '
966                     '${name}CxxConfigParams::makeDirectoryEntry();')
967        code.dedent()
968        code('}')
969        code.write(target[0].abspath)
970
971    py_source = PySource.modules[simobj.__module__]
972    extra_deps = [ py_source.tnode ]
973    env.Command(cxx_config_init_cc_file, Value(name),
974        MakeAction(createCxxConfigInitCC, Transform("CXXCINIT")))
975    cxx_param_hh_files = ["cxx_config/%s.hh" % simobj
976        for name,simobj in sorted(sim_objects.iteritems())
977        if not hasattr(simobj, 'abstract') or not simobj.abstract]
978    Depends(cxx_config_init_cc_file, cxx_param_hh_files +
979            [File('sim/cxx_config.hh')])
980    Source(cxx_config_init_cc_file)
981
982# Generate all enum header files
983for name,enum in sorted(all_enums.iteritems()):
984    py_source = PySource.modules[enum.__module__]
985    extra_deps = [ py_source.tnode ]
986
987    cc_file = File('enums/%s.cc' % name)
988    env.Command(cc_file, [Value(name), Value(env['USE_PYTHON'])],
989                MakeAction(createEnumStrings, Transform("ENUM STR")))
990    env.Depends(cc_file, depends + extra_deps)
991    Source(cc_file)
992
993    hh_file = File('enums/%s.hh' % name)
994    env.Command(hh_file, Value(name),
995                MakeAction(createEnumDecls, Transform("ENUMDECL")))
996    env.Depends(hh_file, depends + extra_deps)
997
998# Generate SimObject Python bindings wrapper files
999if env['USE_PYTHON']:
1000    for name,simobj in sorted(sim_objects.iteritems()):
1001        py_source = PySource.modules[simobj.__module__]
1002        extra_deps = [ py_source.tnode ]
1003        cc_file = File('python/_m5/param_%s.cc' % name)
1004        env.Command(cc_file, Value(name),
1005                    MakeAction(createSimObjectPyBindWrapper,
1006                               Transform("SO PyBind")))
1007        env.Depends(cc_file, depends + extra_deps)
1008        Source(cc_file)
1009
1010# Build all protocol buffers if we have got protoc and protobuf available
1011if env['HAVE_PROTOBUF']:
1012    for proto in ProtoBuf.all:
1013        # Use both the source and header as the target, and the .proto
1014        # file as the source. When executing the protoc compiler, also
1015        # specify the proto_path to avoid having the generated files
1016        # include the path.
1017        env.Command([proto.cc_file, proto.hh_file], proto.tnode,
1018                    MakeAction('$PROTOC --cpp_out ${TARGET.dir} '
1019                               '--proto_path ${SOURCE.dir} $SOURCE',
1020                               Transform("PROTOC")))
1021
1022        # Add the C++ source file
1023        Source(proto.cc_file, tags=proto.tags)
1024elif ProtoBuf.all:
1025    print('Got protobuf to build, but lacks support!')
1026    Exit(1)
1027
1028#
1029# Handle debug flags
1030#
1031def makeDebugFlagCC(target, source, env):
1032    assert(len(target) == 1 and len(source) == 1)
1033
1034    code = code_formatter()
1035
1036    # delay definition of CompoundFlags until after all the definition
1037    # of all constituent SimpleFlags
1038    comp_code = code_formatter()
1039
1040    # file header
1041    code('''
1042/*
1043 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
1044 */
1045
1046#include "base/debug.hh"
1047
1048namespace Debug {
1049
1050''')
1051
1052    for name, flag in sorted(source[0].read().iteritems()):
1053        n, compound, desc = flag
1054        assert n == name
1055
1056        if not compound:
1057            code('SimpleFlag $name("$name", "$desc");')
1058        else:
1059            comp_code('CompoundFlag $name("$name", "$desc",')
1060            comp_code.indent()
1061            last = len(compound) - 1
1062            for i,flag in enumerate(compound):
1063                if i != last:
1064                    comp_code('&$flag,')
1065                else:
1066                    comp_code('&$flag);')
1067            comp_code.dedent()
1068
1069    code.append(comp_code)
1070    code()
1071    code('} // namespace Debug')
1072
1073    code.write(str(target[0]))
1074
1075def makeDebugFlagHH(target, source, env):
1076    assert(len(target) == 1 and len(source) == 1)
1077
1078    val = eval(source[0].get_contents())
1079    name, compound, desc = val
1080
1081    code = code_formatter()
1082
1083    # file header boilerplate
1084    code('''\
1085/*
1086 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
1087 */
1088
1089#ifndef __DEBUG_${name}_HH__
1090#define __DEBUG_${name}_HH__
1091
1092namespace Debug {
1093''')
1094
1095    if compound:
1096        code('class CompoundFlag;')
1097    code('class SimpleFlag;')
1098
1099    if compound:
1100        code('extern CompoundFlag $name;')
1101        for flag in compound:
1102            code('extern SimpleFlag $flag;')
1103    else:
1104        code('extern SimpleFlag $name;')
1105
1106    code('''
1107}
1108
1109#endif // __DEBUG_${name}_HH__
1110''')
1111
1112    code.write(str(target[0]))
1113
1114for name,flag in sorted(debug_flags.iteritems()):
1115    n, compound, desc = flag
1116    assert n == name
1117
1118    hh_file = 'debug/%s.hh' % name
1119    env.Command(hh_file, Value(flag),
1120                MakeAction(makeDebugFlagHH, Transform("TRACING", 0)))
1121
1122env.Command('debug/flags.cc', Value(debug_flags),
1123            MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
1124Source('debug/flags.cc')
1125
1126# version tags
1127tags = \
1128env.Command('sim/tags.cc', None,
1129            MakeAction('util/cpt_upgrader.py --get-cc-file > $TARGET',
1130                       Transform("VER TAGS")))
1131env.AlwaysBuild(tags)
1132
1133# Embed python files.  All .py files that have been indicated by a
1134# PySource() call in a SConscript need to be embedded into the M5
1135# library.  To do that, we compile the file to byte code, marshal the
1136# byte code, compress it, and then generate a c++ file that
1137# inserts the result into an array.
1138def embedPyFile(target, source, env):
1139    def c_str(string):
1140        if string is None:
1141            return "0"
1142        return '"%s"' % string
1143
1144    '''Action function to compile a .py into a code object, marshal
1145    it, compress it, and stick it into an asm file so the code appears
1146    as just bytes with a label in the data section'''
1147
1148    src = file(str(source[0]), 'r').read()
1149
1150    pysource = PySource.tnodes[source[0]]
1151    compiled = compile(src, pysource.abspath, 'exec')
1152    marshalled = marshal.dumps(compiled)
1153    compressed = zlib.compress(marshalled)
1154    data = compressed
1155    sym = pysource.symname
1156
1157    code = code_formatter()
1158    code('''\
1159#include "sim/init.hh"
1160
1161namespace {
1162
1163''')
1164    blobToCpp(data, 'data_' + sym, code)
1165    code('''\
1166
1167
1168EmbeddedPython embedded_${sym}(
1169    ${{c_str(pysource.arcname)}},
1170    ${{c_str(pysource.abspath)}},
1171    ${{c_str(pysource.modpath)}},
1172    data_${sym},
1173    ${{len(data)}},
1174    ${{len(marshalled)}});
1175
1176} // anonymous namespace
1177''')
1178    code.write(str(target[0]))
1179
1180for source in PySource.all:
1181    env.Command(source.cpp, source.tnode,
1182                MakeAction(embedPyFile, Transform("EMBED PY")))
1183    Source(source.cpp, tags=source.tags, add_tags='python')
1184
1185########################################################################
1186#
1187# Define binaries.  Each different build type (debug, opt, etc.) gets
1188# a slightly different build environment.
1189#
1190
1191# List of constructed environments to pass back to SConstruct
1192date_source = Source('base/date.cc', tags=[])
1193
1194gem5_binary = Gem5('gem5')
1195
1196# Function to create a new build environment as clone of current
1197# environment 'env' with modified object suffix and optional stripped
1198# binary.  Additional keyword arguments are appended to corresponding
1199# build environment vars.
1200def makeEnv(env, label, objsfx, strip=False, disable_partial=False, **kwargs):
1201    # SCons doesn't know to append a library suffix when there is a '.' in the
1202    # name.  Use '_' instead.
1203    libname = 'gem5_' + label
1204    secondary_exename = 'm5.' + label
1205
1206    new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
1207    new_env.Label = label
1208    new_env.Append(**kwargs)
1209
1210    lib_sources = Source.all.with_tag('gem5 lib')
1211
1212    # Without Python, leave out all Python content from the library
1213    # builds.  The option doesn't affect gem5 built as a program
1214    if GetOption('without_python'):
1215        lib_sources = lib_sources.without_tag('python')
1216
1217    static_objs = []
1218    shared_objs = []
1219
1220    for s in lib_sources.with_tag(Source.ungrouped_tag):
1221        static_objs.append(s.static(new_env))
1222        shared_objs.append(s.shared(new_env))
1223
1224    for group in Source.source_groups:
1225        srcs = lib_sources.with_tag(Source.link_group_tag(group))
1226        if not srcs:
1227            continue
1228
1229        group_static = [ s.static(new_env) for s in srcs ]
1230        group_shared = [ s.shared(new_env) for s in srcs ]
1231
1232        # If partial linking is disabled, add these sources to the build
1233        # directly, and short circuit this loop.
1234        if disable_partial:
1235            static_objs.extend(group_static)
1236            shared_objs.extend(group_shared)
1237            continue
1238
1239        # Set up the static partially linked objects.
1240        file_name = new_env.subst("${OBJPREFIX}lib${OBJSUFFIX}.partial")
1241        target = File(joinpath(group, file_name))
1242        partial = env.PartialStatic(target=target, source=group_static)
1243        static_objs.extend(partial)
1244
1245        # Set up the shared partially linked objects.
1246        file_name = new_env.subst("${SHOBJPREFIX}lib${SHOBJSUFFIX}.partial")
1247        target = File(joinpath(group, file_name))
1248        partial = env.PartialShared(target=target, source=group_shared)
1249        shared_objs.extend(partial)
1250
1251    static_date = date_source.static(new_env)
1252    new_env.Depends(static_date, static_objs)
1253    static_objs.extend(static_date)
1254
1255    shared_date = date_source.shared(new_env)
1256    new_env.Depends(shared_date, shared_objs)
1257    shared_objs.extend(shared_date)
1258
1259    main_objs = [ s.static(new_env) for s in Source.all.with_tag('main') ]
1260
1261    # First make a library of everything but main() so other programs can
1262    # link against m5.
1263    static_lib = new_env.StaticLibrary(libname, static_objs)
1264    shared_lib = new_env.SharedLibrary(libname, shared_objs)
1265
1266    # Keep track of the object files generated so far so Executables can
1267    # include them.
1268    new_env['STATIC_OBJS'] = static_objs
1269    new_env['SHARED_OBJS'] = shared_objs
1270    new_env['MAIN_OBJS'] = main_objs
1271
1272    new_env['STATIC_LIB'] = static_lib
1273    new_env['SHARED_LIB'] = shared_lib
1274
1275    # Record some settings for building Executables.
1276    new_env['EXE_SUFFIX'] = label
1277    new_env['STRIP_EXES'] = strip
1278
1279    for cls in ExecutableMeta.all:
1280        cls.declare_all(new_env)
1281
1282    new_env.M5Binary = File(gem5_binary.path(new_env))
1283
1284    new_env.Command(secondary_exename, new_env.M5Binary,
1285            MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK")))
1286
1287    # Set up regression tests.
1288    SConscript(os.path.join(env.root.abspath, 'tests', 'SConscript'),
1289               variant_dir=Dir('tests').Dir(new_env.Label),
1290               exports={ 'env' : new_env }, duplicate=False)
1291
1292# Start out with the compiler flags common to all compilers,
1293# i.e. they all use -g for opt and -g -pg for prof
1294ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'],
1295           'perf' : ['-g']}
1296
1297# Start out with the linker flags common to all linkers, i.e. -pg for
1298# prof, and -lprofiler for perf. The -lprofile flag is surrounded by
1299# no-as-needed and as-needed as the binutils linker is too clever and
1300# simply doesn't link to the library otherwise.
1301ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'],
1302           'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']}
1303
1304# For Link Time Optimization, the optimisation flags used to compile
1305# individual files are decoupled from those used at link time
1306# (i.e. you can compile with -O3 and perform LTO with -O0), so we need
1307# to also update the linker flags based on the target.
1308if env['GCC']:
1309    if sys.platform == 'sunos5':
1310        ccflags['debug'] += ['-gstabs+']
1311    else:
1312        ccflags['debug'] += ['-ggdb3']
1313    ldflags['debug'] += ['-O0']
1314    # opt, fast, prof and perf all share the same cc flags, also add
1315    # the optimization to the ldflags as LTO defers the optimization
1316    # to link time
1317    for target in ['opt', 'fast', 'prof', 'perf']:
1318        ccflags[target] += ['-O3']
1319        ldflags[target] += ['-O3']
1320
1321    ccflags['fast'] += env['LTO_CCFLAGS']
1322    ldflags['fast'] += env['LTO_LDFLAGS']
1323elif env['CLANG']:
1324    ccflags['debug'] += ['-g', '-O0']
1325    # opt, fast, prof and perf all share the same cc flags
1326    for target in ['opt', 'fast', 'prof', 'perf']:
1327        ccflags[target] += ['-O3']
1328else:
1329    print('Unknown compiler, please fix compiler options')
1330    Exit(1)
1331
1332
1333# To speed things up, we only instantiate the build environments we
1334# need.  We try to identify the needed environment for each target; if
1335# we can't, we fall back on instantiating all the environments just to
1336# be safe.
1337target_types = ['debug', 'opt', 'fast', 'prof', 'perf']
1338obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof',
1339              'gpo' : 'perf'}
1340
1341def identifyTarget(t):
1342    ext = t.split('.')[-1]
1343    if ext in target_types:
1344        return ext
1345    if ext in obj2target:
1346        return obj2target[ext]
1347    match = re.search(r'/tests/([^/]+)/', t)
1348    if match and match.group(1) in target_types:
1349        return match.group(1)
1350    return 'all'
1351
1352needed_envs = [identifyTarget(target) for target in BUILD_TARGETS]
1353if 'all' in needed_envs:
1354    needed_envs += target_types
1355
1356disable_partial = False
1357if env['PLATFORM'] == 'darwin':
1358    # Up until Apple LLVM version 10.0.0 (clang-1000.11.45.5), partial
1359    # linked objects do not expose symbols that are marked with the
1360    # hidden visibility and consequently building gem5 on Mac OS
1361    # fails. As a workaround, we disable partial linking, however, we
1362    # may want to revisit in the future.
1363    disable_partial = True
1364
1365# Debug binary
1366if 'debug' in needed_envs:
1367    makeEnv(env, 'debug', '.do',
1368            CCFLAGS = Split(ccflags['debug']),
1369            CPPDEFINES = ['DEBUG', 'TRACING_ON=1'],
1370            LINKFLAGS = Split(ldflags['debug']),
1371            disable_partial=disable_partial)
1372
1373# Optimized binary
1374if 'opt' in needed_envs:
1375    makeEnv(env, 'opt', '.o',
1376            CCFLAGS = Split(ccflags['opt']),
1377            CPPDEFINES = ['TRACING_ON=1'],
1378            LINKFLAGS = Split(ldflags['opt']),
1379            disable_partial=disable_partial)
1380
1381# "Fast" binary
1382if 'fast' in needed_envs:
1383    disable_partial = disable_partial and \
1384            env.get('BROKEN_INCREMENTAL_LTO', False) and \
1385            GetOption('force_lto')
1386    makeEnv(env, 'fast', '.fo', strip = True,
1387            CCFLAGS = Split(ccflags['fast']),
1388            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1389            LINKFLAGS = Split(ldflags['fast']),
1390            disable_partial=disable_partial)
1391
1392# Profiled binary using gprof
1393if 'prof' in needed_envs:
1394    makeEnv(env, 'prof', '.po',
1395            CCFLAGS = Split(ccflags['prof']),
1396            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1397            LINKFLAGS = Split(ldflags['prof']),
1398            disable_partial=disable_partial)
1399
1400# Profiled binary using google-pprof
1401if 'perf' in needed_envs:
1402    makeEnv(env, 'perf', '.gpo',
1403            CCFLAGS = Split(ccflags['perf']),
1404            CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1405            LINKFLAGS = Split(ldflags['perf']),
1406            disable_partial=disable_partial)
1407