ruby: removed Write_Only AccessPermission
[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 env.Command('sim/tags.cc', None,
966 MakeAction('util/cpt_upgrader.py --get-cc-file > $TARGET',
967 Transform("VER TAGS")))
968
969 # Embed python files. All .py files that have been indicated by a
970 # PySource() call in a SConscript need to be embedded into the M5
971 # library. To do that, we compile the file to byte code, marshal the
972 # byte code, compress it, and then generate a c++ file that
973 # inserts the result into an array.
974 def embedPyFile(target, source, env):
975 def c_str(string):
976 if string is None:
977 return "0"
978 return '"%s"' % string
979
980 '''Action function to compile a .py into a code object, marshal
981 it, compress it, and stick it into an asm file so the code appears
982 as just bytes with a label in the data section'''
983
984 src = file(str(source[0]), 'r').read()
985
986 pysource = PySource.tnodes[source[0]]
987 compiled = compile(src, pysource.abspath, 'exec')
988 marshalled = marshal.dumps(compiled)
989 compressed = zlib.compress(marshalled)
990 data = compressed
991 sym = pysource.symname
992
993 code = code_formatter()
994 code('''\
995 #include "sim/init.hh"
996
997 namespace {
998
999 const uint8_t data_${sym}[] = {
1000 ''')
1001 code.indent()
1002 step = 16
1003 for i in xrange(0, len(data), step):
1004 x = array.array('B', data[i:i+step])
1005 code(''.join('%d,' % d for d in x))
1006 code.dedent()
1007
1008 code('''};
1009
1010 EmbeddedPython embedded_${sym}(
1011 ${{c_str(pysource.arcname)}},
1012 ${{c_str(pysource.abspath)}},
1013 ${{c_str(pysource.modpath)}},
1014 data_${sym},
1015 ${{len(data)}},
1016 ${{len(marshalled)}});
1017
1018 } // anonymous namespace
1019 ''')
1020 code.write(str(target[0]))
1021
1022 for source in PySource.all:
1023 env.Command(source.cpp, source.tnode,
1024 MakeAction(embedPyFile, Transform("EMBED PY")))
1025 env.Depends(SWIG, source.cpp)
1026 Source(source.cpp, skip_no_python=True)
1027
1028 ########################################################################
1029 #
1030 # Define binaries. Each different build type (debug, opt, etc.) gets
1031 # a slightly different build environment.
1032 #
1033
1034 # List of constructed environments to pass back to SConstruct
1035 date_source = Source('base/date.cc', skip_lib=True)
1036
1037 # Capture this directory for the closure makeEnv, otherwise when it is
1038 # called, it won't know what directory it should use.
1039 variant_dir = Dir('.').path
1040 def variant(*path):
1041 return os.path.join(variant_dir, *path)
1042 def variantd(*path):
1043 return variant(*path)+'/'
1044
1045 # Function to create a new build environment as clone of current
1046 # environment 'env' with modified object suffix and optional stripped
1047 # binary. Additional keyword arguments are appended to corresponding
1048 # build environment vars.
1049 def makeEnv(env, label, objsfx, strip = False, **kwargs):
1050 # SCons doesn't know to append a library suffix when there is a '.' in the
1051 # name. Use '_' instead.
1052 libname = variant('gem5_' + label)
1053 exename = variant('gem5.' + label)
1054 secondary_exename = variant('m5.' + label)
1055
1056 new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
1057 new_env.Label = label
1058 new_env.Append(**kwargs)
1059
1060 swig_env = new_env.Clone()
1061
1062 # Both gcc and clang have issues with unused labels and values in
1063 # the SWIG generated code
1064 swig_env.Append(CCFLAGS=['-Wno-unused-label', '-Wno-unused-value'])
1065
1066 if env['GCC']:
1067 # Depending on the SWIG version, we also need to supress
1068 # warnings about uninitialized variables and missing field
1069 # initializers.
1070 swig_env.Append(CCFLAGS=['-Wno-uninitialized',
1071 '-Wno-missing-field-initializers',
1072 '-Wno-unused-but-set-variable',
1073 '-Wno-maybe-uninitialized',
1074 '-Wno-type-limits'])
1075
1076 # Only gcc >= 4.9 supports UBSan, so check both the version
1077 # and the command-line option before adding the compiler and
1078 # linker flags.
1079 if GetOption('with_ubsan') and \
1080 compareVersions(env['GCC_VERSION'], '4.9') >= 0:
1081 new_env.Append(CCFLAGS='-fsanitize=undefined')
1082 new_env.Append(LINKFLAGS='-fsanitize=undefined')
1083
1084 if env['CLANG']:
1085 swig_env.Append(CCFLAGS=['-Wno-sometimes-uninitialized',
1086 '-Wno-deprecated-register',
1087 '-Wno-tautological-compare'])
1088
1089 # All supported clang versions have support for UBSan, so if
1090 # asked to use it, append the compiler and linker flags.
1091 if GetOption('with_ubsan'):
1092 new_env.Append(CCFLAGS='-fsanitize=undefined')
1093 new_env.Append(LINKFLAGS='-fsanitize=undefined')
1094
1095 werror_env = new_env.Clone()
1096 # Treat warnings as errors but white list some warnings that we
1097 # want to allow (e.g., deprecation warnings).
1098 werror_env.Append(CCFLAGS=['-Werror',
1099 '-Wno-error=deprecated-declarations',
1100 '-Wno-error=deprecated',
1101 ])
1102
1103 def make_obj(source, static, extra_deps = None):
1104 '''This function adds the specified source to the correct
1105 build environment, and returns the corresponding SCons Object
1106 nodes'''
1107
1108 if source.swig:
1109 env = swig_env
1110 elif source.Werror:
1111 env = werror_env
1112 else:
1113 env = new_env
1114
1115 if static:
1116 obj = env.StaticObject(source.tnode)
1117 else:
1118 obj = env.SharedObject(source.tnode)
1119
1120 if extra_deps:
1121 env.Depends(obj, extra_deps)
1122
1123 return obj
1124
1125 lib_guards = {'main': False, 'skip_lib': False}
1126
1127 # Without Python, leave out all SWIG and Python content from the
1128 # library builds. The option doesn't affect gem5 built as a program
1129 if GetOption('without_python'):
1130 lib_guards['skip_no_python'] = False
1131
1132 static_objs = [ make_obj(s, True) for s in Source.get(**lib_guards) ]
1133 shared_objs = [ make_obj(s, False) for s in Source.get(**lib_guards) ]
1134
1135 static_date = make_obj(date_source, static=True, extra_deps=static_objs)
1136 static_objs.append(static_date)
1137
1138 shared_date = make_obj(date_source, static=False, extra_deps=shared_objs)
1139 shared_objs.append(shared_date)
1140
1141 # First make a library of everything but main() so other programs can
1142 # link against m5.
1143 static_lib = new_env.StaticLibrary(libname, static_objs)
1144 shared_lib = new_env.SharedLibrary(libname, shared_objs)
1145
1146 # Now link a stub with main() and the static library.
1147 main_objs = [ make_obj(s, True) for s in Source.get(main=True) ]
1148
1149 for test in UnitTest.all:
1150 flags = { test.target : True }
1151 test_sources = Source.get(**flags)
1152 test_objs = [ make_obj(s, static=True) for s in test_sources ]
1153 if test.main:
1154 test_objs += main_objs
1155 path = variant('unittest/%s.%s' % (test.target, label))
1156 new_env.Program(path, test_objs + static_objs)
1157
1158 progname = exename
1159 if strip:
1160 progname += '.unstripped'
1161
1162 targets = new_env.Program(progname, main_objs + static_objs)
1163
1164 if strip:
1165 if sys.platform == 'sunos5':
1166 cmd = 'cp $SOURCE $TARGET; strip $TARGET'
1167 else:
1168 cmd = 'strip $SOURCE -o $TARGET'
1169 targets = new_env.Command(exename, progname,
1170 MakeAction(cmd, Transform("STRIP")))
1171
1172 new_env.Command(secondary_exename, exename,
1173 MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK")))
1174
1175 new_env.M5Binary = targets[0]
1176 return new_env
1177
1178 # Start out with the compiler flags common to all compilers,
1179 # i.e. they all use -g for opt and -g -pg for prof
1180 ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'],
1181 'perf' : ['-g']}
1182
1183 # Start out with the linker flags common to all linkers, i.e. -pg for
1184 # prof, and -lprofiler for perf. The -lprofile flag is surrounded by
1185 # no-as-needed and as-needed as the binutils linker is too clever and
1186 # simply doesn't link to the library otherwise.
1187 ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'],
1188 'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']}
1189
1190 # For Link Time Optimization, the optimisation flags used to compile
1191 # individual files are decoupled from those used at link time
1192 # (i.e. you can compile with -O3 and perform LTO with -O0), so we need
1193 # to also update the linker flags based on the target.
1194 if env['GCC']:
1195 if sys.platform == 'sunos5':
1196 ccflags['debug'] += ['-gstabs+']
1197 else:
1198 ccflags['debug'] += ['-ggdb3']
1199 ldflags['debug'] += ['-O0']
1200 # opt, fast, prof and perf all share the same cc flags, also add
1201 # the optimization to the ldflags as LTO defers the optimization
1202 # to link time
1203 for target in ['opt', 'fast', 'prof', 'perf']:
1204 ccflags[target] += ['-O3']
1205 ldflags[target] += ['-O3']
1206
1207 ccflags['fast'] += env['LTO_CCFLAGS']
1208 ldflags['fast'] += env['LTO_LDFLAGS']
1209 elif env['CLANG']:
1210 ccflags['debug'] += ['-g', '-O0']
1211 # opt, fast, prof and perf all share the same cc flags
1212 for target in ['opt', 'fast', 'prof', 'perf']:
1213 ccflags[target] += ['-O3']
1214 else:
1215 print 'Unknown compiler, please fix compiler options'
1216 Exit(1)
1217
1218
1219 # To speed things up, we only instantiate the build environments we
1220 # need. We try to identify the needed environment for each target; if
1221 # we can't, we fall back on instantiating all the environments just to
1222 # be safe.
1223 target_types = ['debug', 'opt', 'fast', 'prof', 'perf']
1224 obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof',
1225 'gpo' : 'perf'}
1226
1227 def identifyTarget(t):
1228 ext = t.split('.')[-1]
1229 if ext in target_types:
1230 return ext
1231 if obj2target.has_key(ext):
1232 return obj2target[ext]
1233 match = re.search(r'/tests/([^/]+)/', t)
1234 if match and match.group(1) in target_types:
1235 return match.group(1)
1236 return 'all'
1237
1238 needed_envs = [identifyTarget(target) for target in BUILD_TARGETS]
1239 if 'all' in needed_envs:
1240 needed_envs += target_types
1241
1242 gem5_root = Dir('.').up().up().abspath
1243 def makeEnvirons(target, source, env):
1244 # cause any later Source() calls to be fatal, as a diagnostic.
1245 Source.done()
1246
1247 envList = []
1248
1249 # Debug binary
1250 if 'debug' in needed_envs:
1251 envList.append(
1252 makeEnv(env, 'debug', '.do',
1253 CCFLAGS = Split(ccflags['debug']),
1254 CPPDEFINES = ['DEBUG', 'TRACING_ON=1'],
1255 LINKFLAGS = Split(ldflags['debug'])))
1256
1257 # Optimized binary
1258 if 'opt' in needed_envs:
1259 envList.append(
1260 makeEnv(env, 'opt', '.o',
1261 CCFLAGS = Split(ccflags['opt']),
1262 CPPDEFINES = ['TRACING_ON=1'],
1263 LINKFLAGS = Split(ldflags['opt'])))
1264
1265 # "Fast" binary
1266 if 'fast' in needed_envs:
1267 envList.append(
1268 makeEnv(env, 'fast', '.fo', strip = True,
1269 CCFLAGS = Split(ccflags['fast']),
1270 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1271 LINKFLAGS = Split(ldflags['fast'])))
1272
1273 # Profiled binary using gprof
1274 if 'prof' in needed_envs:
1275 envList.append(
1276 makeEnv(env, 'prof', '.po',
1277 CCFLAGS = Split(ccflags['prof']),
1278 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1279 LINKFLAGS = Split(ldflags['prof'])))
1280
1281 # Profiled binary using google-pprof
1282 if 'perf' in needed_envs:
1283 envList.append(
1284 makeEnv(env, 'perf', '.gpo',
1285 CCFLAGS = Split(ccflags['perf']),
1286 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1287 LINKFLAGS = Split(ldflags['perf'])))
1288
1289 # Set up the regression tests for each build.
1290 for e in envList:
1291 SConscript(os.path.join(gem5_root, 'tests', 'SConscript'),
1292 variant_dir = variantd('tests', e.Label),
1293 exports = { 'env' : e }, duplicate = False)
1294
1295 # The MakeEnvirons Builder defers the full dependency collection until
1296 # after processing the ISA definition (due to dynamically generated
1297 # source files). Add this dependency to all targets so they will wait
1298 # until the environments are completely set up. Otherwise, a second
1299 # process (e.g. -j2 or higher) will try to compile the requested target,
1300 # not know how, and fail.
1301 env.Append(BUILDERS = {'MakeEnvirons' :
1302 Builder(action=MakeAction(makeEnvirons,
1303 Transform("ENVIRONS", 1)))})
1304
1305 isa_target = env['PHONY_BASE'] + '-deps'
1306 environs = env['PHONY_BASE'] + '-environs'
1307 env.Depends('#all-deps', isa_target)
1308 env.Depends('#all-environs', environs)
1309 env.ScanISA(isa_target, File('arch/%s/generated/inc.d' % env['TARGET_ISA']))
1310 envSetup = env.MakeEnvirons(environs, isa_target)
1311
1312 # make sure no -deps targets occur before all ISAs are complete
1313 env.Depends(isa_target, '#all-isas')
1314 # likewise for -environs targets and all the -deps targets
1315 env.Depends(environs, '#all-deps')