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