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