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