scons: allow embedding arbitrary blobs into the gem5 executable
[gem5.git] / src / SConscript
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
42 from __future__ import print_function
43
44 import array
45 import bisect
46 import functools
47 import imp
48 import marshal
49 import os
50 import re
51 import subprocess
52 import sys
53 import zlib
54
55 from os.path import basename, dirname, exists, isdir, isfile, join as joinpath
56
57 import SCons
58
59 from 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
64 Import('*')
65
66 # Children need to see the environment
67 Export('env')
68
69 build_env = [(opt, env[opt]) for opt in export_vars]
70
71 from 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
79 class 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
91 def with_tags_that(predicate):
92 '''Return a list of sources with tags that satisfy a predicate.'''
93 return SourceFilter(predicate)
94
95 def 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
99 def 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
103 def with_tag(tag):
104 '''Return a list of sources with the supplied tag.'''
105 return SourceFilter(lambda stags: tag in stags)
106
107 def 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
111 def without_tag(tag):
112 '''Return a list of sources with the supplied tag.'''
113 return SourceFilter(lambda stags: tag not in stags)
114
115 source_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
124 Export(source_filter_factories)
125
126 class 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
142 class 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
149 class 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
226 def 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
270 def 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
296 class 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
321 class 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
363 class 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
379 class 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
396 exectuable_classes = []
397 class 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
408 class 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
461 class 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
477 class 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
512 class 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
524 Export('Blob')
525 Export('Source')
526 Export('PySource')
527 Export('SimObject')
528 Export('ProtoBuf')
529 Export('Executable')
530 Export('UnitTest')
531 Export('GTest')
532
533 ########################################################################
534 #
535 # Debug Flags
536 #
537 debug_flags = {}
538 def 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
543 def 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
550 Export('DebugFlag')
551 Export('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.
562 env.Append(CPPPATH=Dir('.'))
563
564 for 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
569 for 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
577 here = Dir('.').srcnode().abspath
578 for 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
588 for 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
604 for opt in export_vars:
605 env.ConfigFile(opt)
606
607 def 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
649 env.Command('config/the_isa.hh', map(Value, all_isa_list),
650 MakeAction(makeTheISA, Transform("CFG ISA", 0)))
651
652 def 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
694 env.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 #
702 SimObject.fixed = True
703
704 class 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
759 import m5.SimObject
760 import m5.params
761 from m5.util import code_formatter
762
763 m5.SimObject.clear()
764 m5.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.
769 importer = DictImporter(PySource.modules)
770 sys.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
774 for 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
779 importer.unload()
780 sys.meta_path.remove(importer)
781
782 sim_objects = m5.SimObject.allClasses
783 all_enums = m5.params.allEnums
784
785 for 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 #
797 module_depends = ["m5", "m5.SimObject", "m5.params"]
798 depends = [ PySource.modules[dep].snode for dep in module_depends ]
799 depends.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.
808 def makeDefinesPyFile(target, source, env):
809 build_env = source[0].get_contents()
810
811 code = code_formatter()
812 code("""
813 import _m5.core
814 import m5.util
815
816 buildEnv = m5.util.SmartDict($build_env)
817
818 compileDate = _m5.core.compileDate
819 _globals = globals()
820 for key,val in _m5.core.__dict__.iteritems():
821 if key.startswith('flag_'):
822 flag = key[5:]
823 _globals[flag] = val
824 del _globals
825 """)
826 code.write(target[0].abspath)
827
828 defines_info = Value(build_env)
829 # Generate a file with all of the compile options in it
830 env.Command('python/m5/defines.py', defines_info,
831 MakeAction(makeDefinesPyFile, Transform("DEFINES", 0)))
832 PySource('m5', 'python/m5/defines.py')
833
834 # Generate python file containing info about the M5 source code
835 def 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
843 env.Command('python/m5/info.py',
844 [ '#/COPYING', '#/LICENSE', '#/README', ],
845 MakeAction(makeInfoPyFile, Transform("INFO")))
846 PySource('m5', 'python/m5/info.py')
847
848 ########################################################################
849 #
850 # Create all of the SimObject param headers and enum headers
851 #
852
853 def 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
863 def 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
875 def 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
888 def 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
898 def 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
907 params_hh_files = []
908 for 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
919 if 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
974 for 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
990 if 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
1002 if 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)
1015 elif ProtoBuf.all:
1016 print('Got protobuf to build, but lacks support!')
1017 Exit(1)
1018
1019 #
1020 # Handle debug flags
1021 #
1022 def 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
1039 namespace 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
1066 def 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
1083 namespace 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
1105 for 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
1113 env.Command('debug/flags.cc', Value(debug_flags),
1114 MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
1115 Source('debug/flags.cc')
1116
1117 # version tags
1118 tags = \
1119 env.Command('sim/tags.cc', None,
1120 MakeAction('util/cpt_upgrader.py --get-cc-file > $TARGET',
1121 Transform("VER TAGS")))
1122 env.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.
1129 def 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
1152 namespace {
1153
1154 ''')
1155 blobToCpp(data, 'data_' + sym, code)
1156 code('''\
1157
1158
1159 EmbeddedPython 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
1171 for 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
1183 date_source = Source('base/date.cc', tags=[])
1184
1185 gem5_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.
1191 def 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
1285 ccflags = {'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.
1292 ldflags = {'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.
1299 if 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']
1314 elif 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']
1319 else:
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.
1328 target_types = ['debug', 'opt', 'fast', 'prof', 'perf']
1329 obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof',
1330 'gpo' : 'perf'}
1331
1332 def 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
1343 needed_envs = [identifyTarget(target) for target in BUILD_TARGETS]
1344 if 'all' in needed_envs:
1345 needed_envs += target_types
1346
1347 disable_partial = False
1348 if 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
1357 if '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
1365 if '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
1373 if '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
1384 if '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
1392 if '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)