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