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