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