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