scons: Enable building with the gcc/clang Address Sanitizer
[gem5.git] / src / SConscript
1 # -*- mode:python -*-
2
3 # Copyright (c) 2004-2005 The Regents of The University of Michigan
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions are
8 # met: redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer;
10 # redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution;
13 # neither the name of the copyright holders nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #
29 # Authors: Nathan Binkert
30
31 import array
32 import bisect
33 import imp
34 import marshal
35 import os
36 import re
37 import sys
38 import zlib
39
40 from os.path import basename, dirname, exists, isdir, isfile, join as joinpath
41
42 import SCons
43
44 # This file defines how to build a particular configuration of gem5
45 # based on variable settings in the 'env' build environment.
46
47 Import('*')
48
49 # Children need to see the environment
50 Export('env')
51
52 build_env = [(opt, env[opt]) for opt in export_vars]
53
54 from m5.util import code_formatter, compareVersions
55
56 ########################################################################
57 # Code for adding source files of various types
58 #
59 # When specifying a source file of some type, a set of guards can be
60 # specified for that file. When get() is used to find the files, if
61 # get specifies a set of filters, only files that match those filters
62 # will be accepted (unspecified filters on files are assumed to be
63 # false). Current filters are:
64 # main -- specifies the gem5 main() function
65 # skip_lib -- do not put this file into the gem5 library
66 # skip_no_python -- do not put this file into a no_python library
67 # as it embeds compiled Python
68 # <unittest> -- unit tests use filters based on the unit test name
69 #
70 # A parent can now be specified for a source file and default filter
71 # values will be retrieved recursively from parents (children override
72 # parents).
73 #
74 class SourceMeta(type):
75 '''Meta class for source files that keeps track of all files of a
76 particular type and has a get function for finding all functions
77 of a certain type that match a set of guards'''
78 def __init__(cls, name, bases, dict):
79 super(SourceMeta, cls).__init__(name, bases, dict)
80 cls.all = []
81
82 def get(cls, **guards):
83 '''Find all files that match the specified guards. If a source
84 file does not specify a flag, the default is False'''
85 for src in cls.all:
86 for flag,value in guards.iteritems():
87 # if the flag is found and has a different value, skip
88 # this file
89 if src.all_guards.get(flag, False) != value:
90 break
91 else:
92 yield src
93
94 class SourceFile(object):
95 '''Base object that encapsulates the notion of a source file.
96 This includes, the source node, target node, various manipulations
97 of those. A source file also specifies a set of guards which
98 describing which builds the source file applies to. A parent can
99 also be specified to get default guards from'''
100 __metaclass__ = SourceMeta
101 def __init__(self, source, parent=None, **guards):
102 self.guards = guards
103 self.parent = parent
104
105 tnode = source
106 if not isinstance(source, SCons.Node.FS.File):
107 tnode = File(source)
108
109 self.tnode = tnode
110 self.snode = tnode.srcnode()
111
112 for base in type(self).__mro__:
113 if issubclass(base, SourceFile):
114 base.all.append(self)
115
116 @property
117 def filename(self):
118 return str(self.tnode)
119
120 @property
121 def dirname(self):
122 return dirname(self.filename)
123
124 @property
125 def basename(self):
126 return basename(self.filename)
127
128 @property
129 def extname(self):
130 index = self.basename.rfind('.')
131 if index <= 0:
132 # dot files aren't extensions
133 return self.basename, None
134
135 return self.basename[:index], self.basename[index+1:]
136
137 @property
138 def all_guards(self):
139 '''find all guards for this object getting default values
140 recursively from its parents'''
141 guards = {}
142 if self.parent:
143 guards.update(self.parent.guards)
144 guards.update(self.guards)
145 return guards
146
147 def __lt__(self, other): return self.filename < other.filename
148 def __le__(self, other): return self.filename <= other.filename
149 def __gt__(self, other): return self.filename > other.filename
150 def __ge__(self, other): return self.filename >= other.filename
151 def __eq__(self, other): return self.filename == other.filename
152 def __ne__(self, other): return self.filename != other.filename
153
154 @staticmethod
155 def done():
156 def disabled(cls, name, *ignored):
157 raise RuntimeError("Additional SourceFile '%s'" % name,\
158 "declared, but targets deps are already fixed.")
159 SourceFile.__init__ = disabled
160
161
162 class Source(SourceFile):
163 '''Add a c/c++ source file to the build'''
164 def __init__(self, source, Werror=True, swig=False, **guards):
165 '''specify the source file, and any guards'''
166 super(Source, self).__init__(source, **guards)
167
168 self.Werror = Werror
169 self.swig = swig
170
171 class PySource(SourceFile):
172 '''Add a python source file to the named package'''
173 invalid_sym_char = re.compile('[^A-z0-9_]')
174 modules = {}
175 tnodes = {}
176 symnames = {}
177
178 def __init__(self, package, source, **guards):
179 '''specify the python package, the source file, and any guards'''
180 super(PySource, self).__init__(source, **guards)
181
182 modname,ext = self.extname
183 assert ext == 'py'
184
185 if package:
186 path = package.split('.')
187 else:
188 path = []
189
190 modpath = path[:]
191 if modname != '__init__':
192 modpath += [ modname ]
193 modpath = '.'.join(modpath)
194
195 arcpath = path + [ self.basename ]
196 abspath = self.snode.abspath
197 if not exists(abspath):
198 abspath = self.tnode.abspath
199
200 self.package = package
201 self.modname = modname
202 self.modpath = modpath
203 self.arcname = joinpath(*arcpath)
204 self.abspath = abspath
205 self.compiled = File(self.filename + 'c')
206 self.cpp = File(self.filename + '.cc')
207 self.symname = PySource.invalid_sym_char.sub('_', modpath)
208
209 PySource.modules[modpath] = self
210 PySource.tnodes[self.tnode] = self
211 PySource.symnames[self.symname] = self
212
213 class SimObject(PySource):
214 '''Add a SimObject python file as a python source object and add
215 it to a list of sim object modules'''
216
217 fixed = False
218 modnames = []
219
220 def __init__(self, source, **guards):
221 '''Specify the source file and any guards (automatically in
222 the m5.objects package)'''
223 super(SimObject, self).__init__('m5.objects', source, **guards)
224 if self.fixed:
225 raise AttributeError, "Too late to call SimObject now."
226
227 bisect.insort_right(SimObject.modnames, self.modname)
228
229 class SwigSource(SourceFile):
230 '''Add a swig file to build'''
231
232 def __init__(self, package, source, **guards):
233 '''Specify the python package, the source file, and any guards'''
234 super(SwigSource, self).__init__(source, skip_no_python=True, **guards)
235
236 modname,ext = self.extname
237 assert ext == 'i'
238
239 self.module = modname
240 cc_file = joinpath(self.dirname, modname + '_wrap.cc')
241 py_file = joinpath(self.dirname, modname + '.py')
242
243 self.cc_source = Source(cc_file, swig=True, parent=self, **guards)
244 self.py_source = PySource(package, py_file, parent=self, **guards)
245
246 class ProtoBuf(SourceFile):
247 '''Add a Protocol Buffer to build'''
248
249 def __init__(self, source, **guards):
250 '''Specify the source file, and any guards'''
251 super(ProtoBuf, self).__init__(source, **guards)
252
253 # Get the file name and the extension
254 modname,ext = self.extname
255 assert ext == 'proto'
256
257 # Currently, we stick to generating the C++ headers, so we
258 # only need to track the source and header.
259 self.cc_file = File(modname + '.pb.cc')
260 self.hh_file = File(modname + '.pb.h')
261
262 class UnitTest(object):
263 '''Create a UnitTest'''
264
265 all = []
266 def __init__(self, target, *sources, **kwargs):
267 '''Specify the target name and any sources. Sources that are
268 not SourceFiles are evalued with Source(). All files are
269 guarded with a guard of the same name as the UnitTest
270 target.'''
271
272 srcs = []
273 for src in sources:
274 if not isinstance(src, SourceFile):
275 src = Source(src, skip_lib=True)
276 src.guards[target] = True
277 srcs.append(src)
278
279 self.sources = srcs
280 self.target = target
281 self.main = kwargs.get('main', False)
282 UnitTest.all.append(self)
283
284 # Children should have access
285 Export('Source')
286 Export('PySource')
287 Export('SimObject')
288 Export('SwigSource')
289 Export('ProtoBuf')
290 Export('UnitTest')
291
292 ########################################################################
293 #
294 # Debug Flags
295 #
296 debug_flags = {}
297 def DebugFlag(name, desc=None):
298 if name in debug_flags:
299 raise AttributeError, "Flag %s already specified" % name
300 debug_flags[name] = (name, (), desc)
301
302 def CompoundFlag(name, flags, desc=None):
303 if name in debug_flags:
304 raise AttributeError, "Flag %s already specified" % name
305
306 compound = tuple(flags)
307 debug_flags[name] = (name, compound, desc)
308
309 Export('DebugFlag')
310 Export('CompoundFlag')
311
312 ########################################################################
313 #
314 # Set some compiler variables
315 #
316
317 # Include file paths are rooted in this directory. SCons will
318 # automatically expand '.' to refer to both the source directory and
319 # the corresponding build directory to pick up generated include
320 # files.
321 env.Append(CPPPATH=Dir('.'))
322
323 for extra_dir in extras_dir_list:
324 env.Append(CPPPATH=Dir(extra_dir))
325
326 # Workaround for bug in SCons version > 0.97d20071212
327 # Scons bug id: 2006 gem5 Bug id: 308
328 for root, dirs, files in os.walk(base_dir, topdown=True):
329 Dir(root[len(base_dir) + 1:])
330
331 ########################################################################
332 #
333 # Walk the tree and execute all SConscripts in subdirectories
334 #
335
336 here = Dir('.').srcnode().abspath
337 for root, dirs, files in os.walk(base_dir, topdown=True):
338 if root == here:
339 # we don't want to recurse back into this SConscript
340 continue
341
342 if 'SConscript' in files:
343 build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
344 SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
345
346 for extra_dir in extras_dir_list:
347 prefix_len = len(dirname(extra_dir)) + 1
348
349 # Also add the corresponding build directory to pick up generated
350 # include files.
351 env.Append(CPPPATH=Dir(joinpath(env['BUILDDIR'], extra_dir[prefix_len:])))
352
353 for root, dirs, files in os.walk(extra_dir, topdown=True):
354 # if build lives in the extras directory, don't walk down it
355 if 'build' in dirs:
356 dirs.remove('build')
357
358 if 'SConscript' in files:
359 build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
360 SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
361
362 for opt in export_vars:
363 env.ConfigFile(opt)
364
365 def makeTheISA(source, target, env):
366 isas = [ src.get_contents() for src in source ]
367 target_isa = env['TARGET_ISA']
368 def define(isa):
369 return isa.upper() + '_ISA'
370
371 def namespace(isa):
372 return isa[0].upper() + isa[1:].lower() + 'ISA'
373
374
375 code = code_formatter()
376 code('''\
377 #ifndef __CONFIG_THE_ISA_HH__
378 #define __CONFIG_THE_ISA_HH__
379
380 ''')
381
382 # create defines for the preprocessing and compile-time determination
383 for i,isa in enumerate(isas):
384 code('#define $0 $1', define(isa), i + 1)
385 code()
386
387 # create an enum for any run-time determination of the ISA, we
388 # reuse the same name as the namespaces
389 code('enum class Arch {')
390 for i,isa in enumerate(isas):
391 if i + 1 == len(isas):
392 code(' $0 = $1', namespace(isa), define(isa))
393 else:
394 code(' $0 = $1,', namespace(isa), define(isa))
395 code('};')
396
397 code('''
398
399 #define THE_ISA ${{define(target_isa)}}
400 #define TheISA ${{namespace(target_isa)}}
401 #define THE_ISA_STR "${{target_isa}}"
402
403 #endif // __CONFIG_THE_ISA_HH__''')
404
405 code.write(str(target[0]))
406
407 env.Command('config/the_isa.hh', map(Value, all_isa_list),
408 MakeAction(makeTheISA, Transform("CFG ISA", 0)))
409
410 def makeTheGPUISA(source, target, env):
411 isas = [ src.get_contents() for src in source ]
412 target_gpu_isa = env['TARGET_GPU_ISA']
413 def define(isa):
414 return isa.upper() + '_ISA'
415
416 def namespace(isa):
417 return isa[0].upper() + isa[1:].lower() + 'ISA'
418
419
420 code = code_formatter()
421 code('''\
422 #ifndef __CONFIG_THE_GPU_ISA_HH__
423 #define __CONFIG_THE_GPU_ISA_HH__
424
425 ''')
426
427 # create defines for the preprocessing and compile-time determination
428 for i,isa in enumerate(isas):
429 code('#define $0 $1', define(isa), i + 1)
430 code()
431
432 # create an enum for any run-time determination of the ISA, we
433 # reuse the same name as the namespaces
434 code('enum class GPUArch {')
435 for i,isa in enumerate(isas):
436 if i + 1 == len(isas):
437 code(' $0 = $1', namespace(isa), define(isa))
438 else:
439 code(' $0 = $1,', namespace(isa), define(isa))
440 code('};')
441
442 code('''
443
444 #define THE_GPU_ISA ${{define(target_gpu_isa)}}
445 #define TheGpuISA ${{namespace(target_gpu_isa)}}
446 #define THE_GPU_ISA_STR "${{target_gpu_isa}}"
447
448 #endif // __CONFIG_THE_GPU_ISA_HH__''')
449
450 code.write(str(target[0]))
451
452 env.Command('config/the_gpu_isa.hh', map(Value, all_gpu_isa_list),
453 MakeAction(makeTheGPUISA, Transform("CFG ISA", 0)))
454
455 ########################################################################
456 #
457 # Prevent any SimObjects from being added after this point, they
458 # should all have been added in the SConscripts above
459 #
460 SimObject.fixed = True
461
462 class DictImporter(object):
463 '''This importer takes a dictionary of arbitrary module names that
464 map to arbitrary filenames.'''
465 def __init__(self, modules):
466 self.modules = modules
467 self.installed = set()
468
469 def __del__(self):
470 self.unload()
471
472 def unload(self):
473 import sys
474 for module in self.installed:
475 del sys.modules[module]
476 self.installed = set()
477
478 def find_module(self, fullname, path):
479 if fullname == 'm5.defines':
480 return self
481
482 if fullname == 'm5.objects':
483 return self
484
485 if fullname.startswith('m5.internal'):
486 return None
487
488 source = self.modules.get(fullname, None)
489 if source is not None and fullname.startswith('m5.objects'):
490 return self
491
492 return None
493
494 def load_module(self, fullname):
495 mod = imp.new_module(fullname)
496 sys.modules[fullname] = mod
497 self.installed.add(fullname)
498
499 mod.__loader__ = self
500 if fullname == 'm5.objects':
501 mod.__path__ = fullname.split('.')
502 return mod
503
504 if fullname == 'm5.defines':
505 mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
506 return mod
507
508 source = self.modules[fullname]
509 if source.modname == '__init__':
510 mod.__path__ = source.modpath
511 mod.__file__ = source.abspath
512
513 exec file(source.abspath, 'r') in mod.__dict__
514
515 return mod
516
517 import m5.SimObject
518 import m5.params
519 from m5.util import code_formatter
520
521 m5.SimObject.clear()
522 m5.params.clear()
523
524 # install the python importer so we can grab stuff from the source
525 # tree itself. We can't have SimObjects added after this point or
526 # else we won't know about them for the rest of the stuff.
527 importer = DictImporter(PySource.modules)
528 sys.meta_path[0:0] = [ importer ]
529
530 # import all sim objects so we can populate the all_objects list
531 # make sure that we're working with a list, then let's sort it
532 for modname in SimObject.modnames:
533 exec('from m5.objects import %s' % modname)
534
535 # we need to unload all of the currently imported modules so that they
536 # will be re-imported the next time the sconscript is run
537 importer.unload()
538 sys.meta_path.remove(importer)
539
540 sim_objects = m5.SimObject.allClasses
541 all_enums = m5.params.allEnums
542
543 if m5.SimObject.noCxxHeader:
544 print >> sys.stderr, \
545 "warning: At least one SimObject lacks a header specification. " \
546 "This can cause unexpected results in the generated SWIG " \
547 "wrappers."
548
549 # Find param types that need to be explicitly wrapped with swig.
550 # These will be recognized because the ParamDesc will have a
551 # swig_decl() method. Most param types are based on types that don't
552 # need this, either because they're based on native types (like Int)
553 # or because they're SimObjects (which get swigged independently).
554 # For now the only things handled here are VectorParam types.
555 params_to_swig = {}
556 for name,obj in sorted(sim_objects.iteritems()):
557 for param in obj._params.local.values():
558 # load the ptype attribute now because it depends on the
559 # current version of SimObject.allClasses, but when scons
560 # actually uses the value, all versions of
561 # SimObject.allClasses will have been loaded
562 param.ptype
563
564 if not hasattr(param, 'swig_decl'):
565 continue
566 pname = param.ptype_str
567 if pname not in params_to_swig:
568 params_to_swig[pname] = param
569
570 ########################################################################
571 #
572 # calculate extra dependencies
573 #
574 module_depends = ["m5", "m5.SimObject", "m5.params"]
575 depends = [ PySource.modules[dep].snode for dep in module_depends ]
576 depends.sort(key = lambda x: x.name)
577
578 ########################################################################
579 #
580 # Commands for the basic automatically generated python files
581 #
582
583 # Generate Python file containing a dict specifying the current
584 # buildEnv flags.
585 def makeDefinesPyFile(target, source, env):
586 build_env = source[0].get_contents()
587
588 code = code_formatter()
589 code("""
590 import m5.internal
591 import m5.util
592
593 buildEnv = m5.util.SmartDict($build_env)
594
595 compileDate = m5.internal.core.compileDate
596 _globals = globals()
597 for key,val in m5.internal.core.__dict__.iteritems():
598 if key.startswith('flag_'):
599 flag = key[5:]
600 _globals[flag] = val
601 del _globals
602 """)
603 code.write(target[0].abspath)
604
605 defines_info = Value(build_env)
606 # Generate a file with all of the compile options in it
607 env.Command('python/m5/defines.py', defines_info,
608 MakeAction(makeDefinesPyFile, Transform("DEFINES", 0)))
609 PySource('m5', 'python/m5/defines.py')
610
611 # Generate python file containing info about the M5 source code
612 def makeInfoPyFile(target, source, env):
613 code = code_formatter()
614 for src in source:
615 data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
616 code('$src = ${{repr(data)}}')
617 code.write(str(target[0]))
618
619 # Generate a file that wraps the basic top level files
620 env.Command('python/m5/info.py',
621 [ '#/COPYING', '#/LICENSE', '#/README', ],
622 MakeAction(makeInfoPyFile, Transform("INFO")))
623 PySource('m5', 'python/m5/info.py')
624
625 ########################################################################
626 #
627 # Create all of the SimObject param headers and enum headers
628 #
629
630 def createSimObjectParamStruct(target, source, env):
631 assert len(target) == 1 and len(source) == 1
632
633 name = str(source[0].get_contents())
634 obj = sim_objects[name]
635
636 code = code_formatter()
637 obj.cxx_param_decl(code)
638 code.write(target[0].abspath)
639
640 def createSimObjectCxxConfig(is_header):
641 def body(target, source, env):
642 assert len(target) == 1 and len(source) == 1
643
644 name = str(source[0].get_contents())
645 obj = sim_objects[name]
646
647 code = code_formatter()
648 obj.cxx_config_param_file(code, is_header)
649 code.write(target[0].abspath)
650 return body
651
652 def createParamSwigWrapper(target, source, env):
653 assert len(target) == 1 and len(source) == 1
654
655 name = str(source[0].get_contents())
656 param = params_to_swig[name]
657
658 code = code_formatter()
659 param.swig_decl(code)
660 code.write(target[0].abspath)
661
662 def createEnumStrings(target, source, env):
663 assert len(target) == 1 and len(source) == 1
664
665 name = str(source[0].get_contents())
666 obj = all_enums[name]
667
668 code = code_formatter()
669 obj.cxx_def(code)
670 code.write(target[0].abspath)
671
672 def createEnumDecls(target, source, env):
673 assert len(target) == 1 and len(source) == 1
674
675 name = str(source[0].get_contents())
676 obj = all_enums[name]
677
678 code = code_formatter()
679 obj.cxx_decl(code)
680 code.write(target[0].abspath)
681
682 def createEnumSwigWrapper(target, source, env):
683 assert len(target) == 1 and len(source) == 1
684
685 name = str(source[0].get_contents())
686 obj = all_enums[name]
687
688 code = code_formatter()
689 obj.swig_decl(code)
690 code.write(target[0].abspath)
691
692 def createSimObjectSwigWrapper(target, source, env):
693 name = source[0].get_contents()
694 obj = sim_objects[name]
695
696 code = code_formatter()
697 obj.swig_decl(code)
698 code.write(target[0].abspath)
699
700 # dummy target for generated code
701 # we start out with all the Source files so they get copied to build/*/ also.
702 SWIG = env.Dummy('swig', [s.tnode for s in Source.get()])
703
704 # Generate all of the SimObject param C++ struct header files
705 params_hh_files = []
706 for name,simobj in sorted(sim_objects.iteritems()):
707 py_source = PySource.modules[simobj.__module__]
708 extra_deps = [ py_source.tnode ]
709
710 hh_file = File('params/%s.hh' % name)
711 params_hh_files.append(hh_file)
712 env.Command(hh_file, Value(name),
713 MakeAction(createSimObjectParamStruct, Transform("SO PARAM")))
714 env.Depends(hh_file, depends + extra_deps)
715 env.Depends(SWIG, hh_file)
716
717 # C++ parameter description files
718 if GetOption('with_cxx_config'):
719 for name,simobj in sorted(sim_objects.iteritems()):
720 py_source = PySource.modules[simobj.__module__]
721 extra_deps = [ py_source.tnode ]
722
723 cxx_config_hh_file = File('cxx_config/%s.hh' % name)
724 cxx_config_cc_file = File('cxx_config/%s.cc' % name)
725 env.Command(cxx_config_hh_file, Value(name),
726 MakeAction(createSimObjectCxxConfig(True),
727 Transform("CXXCPRHH")))
728 env.Command(cxx_config_cc_file, Value(name),
729 MakeAction(createSimObjectCxxConfig(False),
730 Transform("CXXCPRCC")))
731 env.Depends(cxx_config_hh_file, depends + extra_deps +
732 [File('params/%s.hh' % name), File('sim/cxx_config.hh')])
733 env.Depends(cxx_config_cc_file, depends + extra_deps +
734 [cxx_config_hh_file])
735 Source(cxx_config_cc_file)
736
737 cxx_config_init_cc_file = File('cxx_config/init.cc')
738
739 def createCxxConfigInitCC(target, source, env):
740 assert len(target) == 1 and len(source) == 1
741
742 code = code_formatter()
743
744 for name,simobj in sorted(sim_objects.iteritems()):
745 if not hasattr(simobj, 'abstract') or not simobj.abstract:
746 code('#include "cxx_config/${name}.hh"')
747 code()
748 code('void cxxConfigInit()')
749 code('{')
750 code.indent()
751 for name,simobj in sorted(sim_objects.iteritems()):
752 not_abstract = not hasattr(simobj, 'abstract') or \
753 not simobj.abstract
754 if not_abstract and 'type' in simobj.__dict__:
755 code('cxx_config_directory["${name}"] = '
756 '${name}CxxConfigParams::makeDirectoryEntry();')
757 code.dedent()
758 code('}')
759 code.write(target[0].abspath)
760
761 py_source = PySource.modules[simobj.__module__]
762 extra_deps = [ py_source.tnode ]
763 env.Command(cxx_config_init_cc_file, Value(name),
764 MakeAction(createCxxConfigInitCC, Transform("CXXCINIT")))
765 cxx_param_hh_files = ["cxx_config/%s.hh" % simobj
766 for name,simobj in sorted(sim_objects.iteritems())
767 if not hasattr(simobj, 'abstract') or not simobj.abstract]
768 Depends(cxx_config_init_cc_file, cxx_param_hh_files +
769 [File('sim/cxx_config.hh')])
770 Source(cxx_config_init_cc_file)
771
772 # Generate any needed param SWIG wrapper files
773 params_i_files = []
774 for name,param in sorted(params_to_swig.iteritems()):
775 i_file = File('python/m5/internal/%s.i' % (param.swig_module_name()))
776 params_i_files.append(i_file)
777 env.Command(i_file, Value(name),
778 MakeAction(createParamSwigWrapper, Transform("SW PARAM")))
779 env.Depends(i_file, depends)
780 env.Depends(SWIG, i_file)
781 SwigSource('m5.internal', i_file)
782
783 # Generate all enum header files
784 for name,enum in sorted(all_enums.iteritems()):
785 py_source = PySource.modules[enum.__module__]
786 extra_deps = [ py_source.tnode ]
787
788 cc_file = File('enums/%s.cc' % name)
789 env.Command(cc_file, Value(name),
790 MakeAction(createEnumStrings, Transform("ENUM STR")))
791 env.Depends(cc_file, depends + extra_deps)
792 env.Depends(SWIG, cc_file)
793 Source(cc_file)
794
795 hh_file = File('enums/%s.hh' % name)
796 env.Command(hh_file, Value(name),
797 MakeAction(createEnumDecls, Transform("ENUMDECL")))
798 env.Depends(hh_file, depends + extra_deps)
799 env.Depends(SWIG, hh_file)
800
801 i_file = File('python/m5/internal/enum_%s.i' % name)
802 env.Command(i_file, Value(name),
803 MakeAction(createEnumSwigWrapper, Transform("ENUMSWIG")))
804 env.Depends(i_file, depends + extra_deps)
805 env.Depends(SWIG, i_file)
806 SwigSource('m5.internal', i_file)
807
808 # Generate SimObject SWIG wrapper files
809 for name,simobj in sorted(sim_objects.iteritems()):
810 py_source = PySource.modules[simobj.__module__]
811 extra_deps = [ py_source.tnode ]
812 i_file = File('python/m5/internal/param_%s.i' % name)
813 env.Command(i_file, Value(name),
814 MakeAction(createSimObjectSwigWrapper, Transform("SO SWIG")))
815 env.Depends(i_file, depends + extra_deps)
816 SwigSource('m5.internal', i_file)
817
818 # Generate the main swig init file
819 def makeEmbeddedSwigInit(target, source, env):
820 code = code_formatter()
821 module = source[0].get_contents()
822 code('''\
823 #include "sim/init.hh"
824
825 extern "C" {
826 void init_${module}();
827 }
828
829 EmbeddedSwig embed_swig_${module}(init_${module});
830 ''')
831 code.write(str(target[0]))
832
833 # Build all swig modules
834 for swig in SwigSource.all:
835 env.Command([swig.cc_source.tnode, swig.py_source.tnode], swig.tnode,
836 MakeAction('$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} '
837 '-o ${TARGETS[0]} $SOURCES', Transform("SWIG")))
838 cc_file = str(swig.tnode)
839 init_file = '%s/%s_init.cc' % (dirname(cc_file), basename(cc_file))
840 env.Command(init_file, Value(swig.module),
841 MakeAction(makeEmbeddedSwigInit, Transform("EMBED SW")))
842 env.Depends(SWIG, init_file)
843 Source(init_file, **swig.guards)
844
845 # Build all protocol buffers if we have got protoc and protobuf available
846 if env['HAVE_PROTOBUF']:
847 for proto in ProtoBuf.all:
848 # Use both the source and header as the target, and the .proto
849 # file as the source. When executing the protoc compiler, also
850 # specify the proto_path to avoid having the generated files
851 # include the path.
852 env.Command([proto.cc_file, proto.hh_file], proto.tnode,
853 MakeAction('$PROTOC --cpp_out ${TARGET.dir} '
854 '--proto_path ${SOURCE.dir} $SOURCE',
855 Transform("PROTOC")))
856
857 env.Depends(SWIG, [proto.cc_file, proto.hh_file])
858 # Add the C++ source file
859 Source(proto.cc_file, **proto.guards)
860 elif ProtoBuf.all:
861 print 'Got protobuf to build, but lacks support!'
862 Exit(1)
863
864 #
865 # Handle debug flags
866 #
867 def makeDebugFlagCC(target, source, env):
868 assert(len(target) == 1 and len(source) == 1)
869
870 code = code_formatter()
871
872 # delay definition of CompoundFlags until after all the definition
873 # of all constituent SimpleFlags
874 comp_code = code_formatter()
875
876 # file header
877 code('''
878 /*
879 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
880 */
881
882 #include "base/debug.hh"
883
884 namespace Debug {
885
886 ''')
887
888 for name, flag in sorted(source[0].read().iteritems()):
889 n, compound, desc = flag
890 assert n == name
891
892 if not compound:
893 code('SimpleFlag $name("$name", "$desc");')
894 else:
895 comp_code('CompoundFlag $name("$name", "$desc",')
896 comp_code.indent()
897 last = len(compound) - 1
898 for i,flag in enumerate(compound):
899 if i != last:
900 comp_code('&$flag,')
901 else:
902 comp_code('&$flag);')
903 comp_code.dedent()
904
905 code.append(comp_code)
906 code()
907 code('} // namespace Debug')
908
909 code.write(str(target[0]))
910
911 def makeDebugFlagHH(target, source, env):
912 assert(len(target) == 1 and len(source) == 1)
913
914 val = eval(source[0].get_contents())
915 name, compound, desc = val
916
917 code = code_formatter()
918
919 # file header boilerplate
920 code('''\
921 /*
922 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
923 */
924
925 #ifndef __DEBUG_${name}_HH__
926 #define __DEBUG_${name}_HH__
927
928 namespace Debug {
929 ''')
930
931 if compound:
932 code('class CompoundFlag;')
933 code('class SimpleFlag;')
934
935 if compound:
936 code('extern CompoundFlag $name;')
937 for flag in compound:
938 code('extern SimpleFlag $flag;')
939 else:
940 code('extern SimpleFlag $name;')
941
942 code('''
943 }
944
945 #endif // __DEBUG_${name}_HH__
946 ''')
947
948 code.write(str(target[0]))
949
950 for name,flag in sorted(debug_flags.iteritems()):
951 n, compound, desc = flag
952 assert n == name
953
954 hh_file = 'debug/%s.hh' % name
955 env.Command(hh_file, Value(flag),
956 MakeAction(makeDebugFlagHH, Transform("TRACING", 0)))
957 env.Depends(SWIG, hh_file)
958
959 env.Command('debug/flags.cc', Value(debug_flags),
960 MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
961 env.Depends(SWIG, 'debug/flags.cc')
962 Source('debug/flags.cc')
963
964 # version tags
965 tags = \
966 env.Command('sim/tags.cc', None,
967 MakeAction('util/cpt_upgrader.py --get-cc-file > $TARGET',
968 Transform("VER TAGS")))
969 env.AlwaysBuild(tags)
970
971 # Embed python files. All .py files that have been indicated by a
972 # PySource() call in a SConscript need to be embedded into the M5
973 # library. To do that, we compile the file to byte code, marshal the
974 # byte code, compress it, and then generate a c++ file that
975 # inserts the result into an array.
976 def embedPyFile(target, source, env):
977 def c_str(string):
978 if string is None:
979 return "0"
980 return '"%s"' % string
981
982 '''Action function to compile a .py into a code object, marshal
983 it, compress it, and stick it into an asm file so the code appears
984 as just bytes with a label in the data section'''
985
986 src = file(str(source[0]), 'r').read()
987
988 pysource = PySource.tnodes[source[0]]
989 compiled = compile(src, pysource.abspath, 'exec')
990 marshalled = marshal.dumps(compiled)
991 compressed = zlib.compress(marshalled)
992 data = compressed
993 sym = pysource.symname
994
995 code = code_formatter()
996 code('''\
997 #include "sim/init.hh"
998
999 namespace {
1000
1001 const uint8_t data_${sym}[] = {
1002 ''')
1003 code.indent()
1004 step = 16
1005 for i in xrange(0, len(data), step):
1006 x = array.array('B', data[i:i+step])
1007 code(''.join('%d,' % d for d in x))
1008 code.dedent()
1009
1010 code('''};
1011
1012 EmbeddedPython embedded_${sym}(
1013 ${{c_str(pysource.arcname)}},
1014 ${{c_str(pysource.abspath)}},
1015 ${{c_str(pysource.modpath)}},
1016 data_${sym},
1017 ${{len(data)}},
1018 ${{len(marshalled)}});
1019
1020 } // anonymous namespace
1021 ''')
1022 code.write(str(target[0]))
1023
1024 for source in PySource.all:
1025 env.Command(source.cpp, source.tnode,
1026 MakeAction(embedPyFile, Transform("EMBED PY")))
1027 env.Depends(SWIG, source.cpp)
1028 Source(source.cpp, skip_no_python=True)
1029
1030 ########################################################################
1031 #
1032 # Define binaries. Each different build type (debug, opt, etc.) gets
1033 # a slightly different build environment.
1034 #
1035
1036 # List of constructed environments to pass back to SConstruct
1037 date_source = Source('base/date.cc', skip_lib=True)
1038
1039 # Capture this directory for the closure makeEnv, otherwise when it is
1040 # called, it won't know what directory it should use.
1041 variant_dir = Dir('.').path
1042 def variant(*path):
1043 return os.path.join(variant_dir, *path)
1044 def variantd(*path):
1045 return variant(*path)+'/'
1046
1047 # Function to create a new build environment as clone of current
1048 # environment 'env' with modified object suffix and optional stripped
1049 # binary. Additional keyword arguments are appended to corresponding
1050 # build environment vars.
1051 def makeEnv(env, label, objsfx, strip = False, **kwargs):
1052 # SCons doesn't know to append a library suffix when there is a '.' in the
1053 # name. Use '_' instead.
1054 libname = variant('gem5_' + label)
1055 exename = variant('gem5.' + label)
1056 secondary_exename = variant('m5.' + label)
1057
1058 new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
1059 new_env.Label = label
1060 new_env.Append(**kwargs)
1061
1062 swig_env = new_env.Clone()
1063
1064 # Both gcc and clang have issues with unused labels and values in
1065 # the SWIG generated code
1066 swig_env.Append(CCFLAGS=['-Wno-unused-label', '-Wno-unused-value'])
1067
1068 if env['GCC']:
1069 # Depending on the SWIG version, we also need to supress
1070 # warnings about uninitialized variables and missing field
1071 # initializers.
1072 swig_env.Append(CCFLAGS=['-Wno-uninitialized',
1073 '-Wno-missing-field-initializers',
1074 '-Wno-unused-but-set-variable',
1075 '-Wno-maybe-uninitialized',
1076 '-Wno-type-limits'])
1077
1078 # Only gcc >= 4.9 supports UBSan, so check both the version
1079 # and the command-line option before adding the compiler and
1080 # linker flags.
1081 if GetOption('with_ubsan') and \
1082 compareVersions(env['GCC_VERSION'], '4.9') >= 0:
1083 new_env.Append(CCFLAGS='-fsanitize=undefined')
1084 new_env.Append(LINKFLAGS='-fsanitize=undefined')
1085
1086 # The address sanitizer is available for gcc >= 4.8
1087 if GetOption('with_asan') and \
1088 compareVersions(env['GCC_VERSION'], '4.8') >= 0:
1089 new_env.Append(CCFLAGS='-fsanitize=address')
1090 new_env.Append(LINKFLAGS='-fsanitize=address')
1091
1092 if env['CLANG']:
1093 swig_env.Append(CCFLAGS=['-Wno-sometimes-uninitialized',
1094 '-Wno-deprecated-register',
1095 '-Wno-tautological-compare'])
1096
1097 # We require clang >= 3.1, so there is no need to check any
1098 # versions here.
1099 if GetOption('with_ubsan'):
1100 new_env.Append(CCFLAGS='-fsanitize=undefined')
1101 new_env.Append(LINKFLAGS='-fsanitize=undefined')
1102
1103 if GetOption('with_asan'):
1104 new_env.Append(CCFLAGS='-fsanitize=address')
1105 new_env.Append(LINKFLAGS='-fsanitize=address')
1106
1107 werror_env = new_env.Clone()
1108 # Treat warnings as errors but white list some warnings that we
1109 # want to allow (e.g., deprecation warnings).
1110 werror_env.Append(CCFLAGS=['-Werror',
1111 '-Wno-error=deprecated-declarations',
1112 '-Wno-error=deprecated',
1113 ])
1114
1115 def make_obj(source, static, extra_deps = None):
1116 '''This function adds the specified source to the correct
1117 build environment, and returns the corresponding SCons Object
1118 nodes'''
1119
1120 if source.swig:
1121 env = swig_env
1122 elif source.Werror:
1123 env = werror_env
1124 else:
1125 env = new_env
1126
1127 if static:
1128 obj = env.StaticObject(source.tnode)
1129 else:
1130 obj = env.SharedObject(source.tnode)
1131
1132 if extra_deps:
1133 env.Depends(obj, extra_deps)
1134
1135 return obj
1136
1137 lib_guards = {'main': False, 'skip_lib': False}
1138
1139 # Without Python, leave out all SWIG and Python content from the
1140 # library builds. The option doesn't affect gem5 built as a program
1141 if GetOption('without_python'):
1142 lib_guards['skip_no_python'] = False
1143
1144 static_objs = [ make_obj(s, True) for s in Source.get(**lib_guards) ]
1145 shared_objs = [ make_obj(s, False) for s in Source.get(**lib_guards) ]
1146
1147 static_date = make_obj(date_source, static=True, extra_deps=static_objs)
1148 static_objs.append(static_date)
1149
1150 shared_date = make_obj(date_source, static=False, extra_deps=shared_objs)
1151 shared_objs.append(shared_date)
1152
1153 # First make a library of everything but main() so other programs can
1154 # link against m5.
1155 static_lib = new_env.StaticLibrary(libname, static_objs)
1156 shared_lib = new_env.SharedLibrary(libname, shared_objs)
1157
1158 # Now link a stub with main() and the static library.
1159 main_objs = [ make_obj(s, True) for s in Source.get(main=True) ]
1160
1161 for test in UnitTest.all:
1162 flags = { test.target : True }
1163 test_sources = Source.get(**flags)
1164 test_objs = [ make_obj(s, static=True) for s in test_sources ]
1165 if test.main:
1166 test_objs += main_objs
1167 path = variant('unittest/%s.%s' % (test.target, label))
1168 new_env.Program(path, test_objs + static_objs)
1169
1170 progname = exename
1171 if strip:
1172 progname += '.unstripped'
1173
1174 targets = new_env.Program(progname, main_objs + static_objs)
1175
1176 if strip:
1177 if sys.platform == 'sunos5':
1178 cmd = 'cp $SOURCE $TARGET; strip $TARGET'
1179 else:
1180 cmd = 'strip $SOURCE -o $TARGET'
1181 targets = new_env.Command(exename, progname,
1182 MakeAction(cmd, Transform("STRIP")))
1183
1184 new_env.Command(secondary_exename, exename,
1185 MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK")))
1186
1187 new_env.M5Binary = targets[0]
1188 return new_env
1189
1190 # Start out with the compiler flags common to all compilers,
1191 # i.e. they all use -g for opt and -g -pg for prof
1192 ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'],
1193 'perf' : ['-g']}
1194
1195 # Start out with the linker flags common to all linkers, i.e. -pg for
1196 # prof, and -lprofiler for perf. The -lprofile flag is surrounded by
1197 # no-as-needed and as-needed as the binutils linker is too clever and
1198 # simply doesn't link to the library otherwise.
1199 ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'],
1200 'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']}
1201
1202 # For Link Time Optimization, the optimisation flags used to compile
1203 # individual files are decoupled from those used at link time
1204 # (i.e. you can compile with -O3 and perform LTO with -O0), so we need
1205 # to also update the linker flags based on the target.
1206 if env['GCC']:
1207 if sys.platform == 'sunos5':
1208 ccflags['debug'] += ['-gstabs+']
1209 else:
1210 ccflags['debug'] += ['-ggdb3']
1211 ldflags['debug'] += ['-O0']
1212 # opt, fast, prof and perf all share the same cc flags, also add
1213 # the optimization to the ldflags as LTO defers the optimization
1214 # to link time
1215 for target in ['opt', 'fast', 'prof', 'perf']:
1216 ccflags[target] += ['-O3']
1217 ldflags[target] += ['-O3']
1218
1219 ccflags['fast'] += env['LTO_CCFLAGS']
1220 ldflags['fast'] += env['LTO_LDFLAGS']
1221 elif env['CLANG']:
1222 ccflags['debug'] += ['-g', '-O0']
1223 # opt, fast, prof and perf all share the same cc flags
1224 for target in ['opt', 'fast', 'prof', 'perf']:
1225 ccflags[target] += ['-O3']
1226 else:
1227 print 'Unknown compiler, please fix compiler options'
1228 Exit(1)
1229
1230
1231 # To speed things up, we only instantiate the build environments we
1232 # need. We try to identify the needed environment for each target; if
1233 # we can't, we fall back on instantiating all the environments just to
1234 # be safe.
1235 target_types = ['debug', 'opt', 'fast', 'prof', 'perf']
1236 obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof',
1237 'gpo' : 'perf'}
1238
1239 def identifyTarget(t):
1240 ext = t.split('.')[-1]
1241 if ext in target_types:
1242 return ext
1243 if obj2target.has_key(ext):
1244 return obj2target[ext]
1245 match = re.search(r'/tests/([^/]+)/', t)
1246 if match and match.group(1) in target_types:
1247 return match.group(1)
1248 return 'all'
1249
1250 needed_envs = [identifyTarget(target) for target in BUILD_TARGETS]
1251 if 'all' in needed_envs:
1252 needed_envs += target_types
1253
1254 gem5_root = Dir('.').up().up().abspath
1255 def makeEnvirons(target, source, env):
1256 # cause any later Source() calls to be fatal, as a diagnostic.
1257 Source.done()
1258
1259 envList = []
1260
1261 # Debug binary
1262 if 'debug' in needed_envs:
1263 envList.append(
1264 makeEnv(env, 'debug', '.do',
1265 CCFLAGS = Split(ccflags['debug']),
1266 CPPDEFINES = ['DEBUG', 'TRACING_ON=1'],
1267 LINKFLAGS = Split(ldflags['debug'])))
1268
1269 # Optimized binary
1270 if 'opt' in needed_envs:
1271 envList.append(
1272 makeEnv(env, 'opt', '.o',
1273 CCFLAGS = Split(ccflags['opt']),
1274 CPPDEFINES = ['TRACING_ON=1'],
1275 LINKFLAGS = Split(ldflags['opt'])))
1276
1277 # "Fast" binary
1278 if 'fast' in needed_envs:
1279 envList.append(
1280 makeEnv(env, 'fast', '.fo', strip = True,
1281 CCFLAGS = Split(ccflags['fast']),
1282 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1283 LINKFLAGS = Split(ldflags['fast'])))
1284
1285 # Profiled binary using gprof
1286 if 'prof' in needed_envs:
1287 envList.append(
1288 makeEnv(env, 'prof', '.po',
1289 CCFLAGS = Split(ccflags['prof']),
1290 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1291 LINKFLAGS = Split(ldflags['prof'])))
1292
1293 # Profiled binary using google-pprof
1294 if 'perf' in needed_envs:
1295 envList.append(
1296 makeEnv(env, 'perf', '.gpo',
1297 CCFLAGS = Split(ccflags['perf']),
1298 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1299 LINKFLAGS = Split(ldflags['perf'])))
1300
1301 # Set up the regression tests for each build.
1302 for e in envList:
1303 SConscript(os.path.join(gem5_root, 'tests', 'SConscript'),
1304 variant_dir = variantd('tests', e.Label),
1305 exports = { 'env' : e }, duplicate = False)
1306
1307 # The MakeEnvirons Builder defers the full dependency collection until
1308 # after processing the ISA definition (due to dynamically generated
1309 # source files). Add this dependency to all targets so they will wait
1310 # until the environments are completely set up. Otherwise, a second
1311 # process (e.g. -j2 or higher) will try to compile the requested target,
1312 # not know how, and fail.
1313 env.Append(BUILDERS = {'MakeEnvirons' :
1314 Builder(action=MakeAction(makeEnvirons,
1315 Transform("ENVIRONS", 1)))})
1316
1317 isa_target = env['PHONY_BASE'] + '-deps'
1318 environs = env['PHONY_BASE'] + '-environs'
1319 env.Depends('#all-deps', isa_target)
1320 env.Depends('#all-environs', environs)
1321 env.ScanISA(isa_target, File('arch/%s/generated/inc.d' % env['TARGET_ISA']))
1322 envSetup = env.MakeEnvirons(environs, isa_target)
1323
1324 # make sure no -deps targets occur before all ISAs are complete
1325 env.Depends(isa_target, '#all-isas')
1326 # likewise for -environs targets and all the -deps targets
1327 env.Depends(environs, '#all-deps')