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
31import array
32import bisect
33import imp
34import marshal
35import os
36import re
37import subprocess
38import sys
39import zlib
40
41from os.path import basename, dirname, exists, isdir, isfile, join as joinpath
42
43import SCons
44
45from gem5_scons import Transform
46
47# This file defines how to build a particular configuration of gem5
48# based on variable settings in the 'env' build environment.
49
50Import('*')
51
52# Children need to see the environment
53Export('env')
54
55build_env = [(opt, env[opt]) for opt in export_vars]
56
57from m5.util import code_formatter, compareVersions
58
59########################################################################
60# Code for adding source files of various types
61#
62# When specifying a source file of some type, a set of tags can be
63# specified for that file.
64
65class SourceList(list):
66 def with_tags_that(self, predicate):
67 '''Return a list of sources with tags that satisfy a predicate.'''
68 def match(source):
69 return predicate(source.tags)
70 return SourceList(filter(match, self))
71
72 def with_any_tags(self, *tags):
73 '''Return a list of sources with any of the supplied tags.'''
74 return self.with_tags_that(lambda stags: len(tags & stags) > 0)
75
76 def with_all_tags(self, *tags):
77 '''Return a list of sources with all of the supplied tags.'''
78 return self.with_tags_that(lambda stags: tags <= stags)
79
80 def with_tag(self, tag):
81 '''Return a list of sources with the supplied tag.'''
82 return self.with_tags_that(lambda stags: tag in stags)
83
84 def without_tags(self, *tags):
85 '''Return a list of sources without any of the supplied tags.'''
86 return self.with_tags_that(lambda stags: len(tags & stags) == 0)
87
88 def without_tag(self, tag):
89 '''Return a list of sources with the supplied tag.'''
90 return self.with_tags_that(lambda stags: tag not in stags)
91
92class SourceMeta(type):
93 '''Meta class for source files that keeps track of all files of a
94 particular type.'''
95 def __init__(cls, name, bases, dict):
96 super(SourceMeta, cls).__init__(name, bases, dict)
97 cls.all = SourceList()
98
99class SourceFile(object):
100 '''Base object that encapsulates the notion of a source file.
101 This includes, the source node, target node, various manipulations
102 of those. A source file also specifies a set of tags which
103 describing arbitrary properties of the source file.'''
104 __metaclass__ = SourceMeta
105 def __init__(self, source, tags=None, add_tags=None):
106 if tags is None:
107 tags='gem5 lib'
108 if isinstance(tags, basestring):
109 tags = set([tags])
110 if isinstance(add_tags, basestring):
111 add_tags = set([add_tags])
112 if add_tags:
113 tags = tags | add_tags
114 self.tags = set(tags)
115
116 tnode = source
117 if not isinstance(source, SCons.Node.FS.File):
118 tnode = File(source)
119
120 self.tnode = tnode
121 self.snode = tnode.srcnode()
122
123 for base in type(self).__mro__:
124 if issubclass(base, SourceFile):
125 base.all.append(self)
126
127 @property
128 def filename(self):
129 return str(self.tnode)
130
131 @property
132 def dirname(self):
133 return dirname(self.filename)
134
135 @property
136 def basename(self):
137 return basename(self.filename)
138
139 @property
140 def extname(self):
141 index = self.basename.rfind('.')
142 if index <= 0:
143 # dot files aren't extensions
144 return self.basename, None
145
146 return self.basename[:index], self.basename[index+1:]
147
148 def __lt__(self, other): return self.filename < other.filename
149 def __le__(self, other): return self.filename <= other.filename
150 def __gt__(self, other): return self.filename > other.filename
151 def __ge__(self, other): return self.filename >= other.filename
152 def __eq__(self, other): return self.filename == other.filename
153 def __ne__(self, other): return self.filename != other.filename
154
155class Source(SourceFile):
156 ungrouped_tag = 'No link group'
157 source_groups = set()
158
159 _current_group_tag = ungrouped_tag
160
161 @staticmethod
162 def link_group_tag(group):
163 return 'link group: %s' % group
164
165 @classmethod
166 def set_group(cls, group):
167 new_tag = Source.link_group_tag(group)
168 Source._current_group_tag = new_tag
169 Source.source_groups.add(group)
170
171 def _add_link_group_tag(self):
172 self.tags.add(Source._current_group_tag)
173
174 '''Add a c/c++ source file to the build'''
175 def __init__(self, source, tags=None, add_tags=None, Werror=True):
175 def __init__(self, source, tags=None, add_tags=None):
176 '''specify the source file, and any tags'''
177 super(Source, self).__init__(source, tags, add_tags)
178 self._add_link_group_tag()
179 self.Werror = Werror
179
180class PySource(SourceFile):
181 '''Add a python source file to the named package'''
182 invalid_sym_char = re.compile('[^A-z0-9_]')
183 modules = {}
184 tnodes = {}
185 symnames = {}
186
187 def __init__(self, package, source, tags=None, add_tags=None):
188 '''specify the python package, the source file, and any tags'''
189 super(PySource, self).__init__(source, tags, add_tags)
190
191 modname,ext = self.extname
192 assert ext == 'py'
193
194 if package:
195 path = package.split('.')
196 else:
197 path = []
198
199 modpath = path[:]
200 if modname != '__init__':
201 modpath += [ modname ]
202 modpath = '.'.join(modpath)
203
204 arcpath = path + [ self.basename ]
205 abspath = self.snode.abspath
206 if not exists(abspath):
207 abspath = self.tnode.abspath
208
209 self.package = package
210 self.modname = modname
211 self.modpath = modpath
212 self.arcname = joinpath(*arcpath)
213 self.abspath = abspath
214 self.compiled = File(self.filename + 'c')
215 self.cpp = File(self.filename + '.cc')
216 self.symname = PySource.invalid_sym_char.sub('_', modpath)
217
218 PySource.modules[modpath] = self
219 PySource.tnodes[self.tnode] = self
220 PySource.symnames[self.symname] = self
221
222class SimObject(PySource):
223 '''Add a SimObject python file as a python source object and add
224 it to a list of sim object modules'''
225
226 fixed = False
227 modnames = []
228
229 def __init__(self, source, tags=None, add_tags=None):
230 '''Specify the source file and any tags (automatically in
231 the m5.objects package)'''
232 super(SimObject, self).__init__('m5.objects', source, tags, add_tags)
233 if self.fixed:
234 raise AttributeError, "Too late to call SimObject now."
235
236 bisect.insort_right(SimObject.modnames, self.modname)
237
238class ProtoBuf(SourceFile):
239 '''Add a Protocol Buffer to build'''
240
241 def __init__(self, source, tags=None, add_tags=None):
242 '''Specify the source file, and any tags'''
243 super(ProtoBuf, self).__init__(source, tags, add_tags)
244
245 # Get the file name and the extension
246 modname,ext = self.extname
247 assert ext == 'proto'
248
249 # Currently, we stick to generating the C++ headers, so we
250 # only need to track the source and header.
251 self.cc_file = File(modname + '.pb.cc')
252 self.hh_file = File(modname + '.pb.h')
253
254class UnitTest(object):
255 '''Create a UnitTest'''
256
257 all = []
258 def __init__(self, target, *sources, **kwargs):
259 '''Specify the target name and any sources. Sources that are
260 not SourceFiles are evalued with Source(). All files are
261 tagged with the name of the UnitTest target.'''
262
263 srcs = SourceList()
264 for src in sources:
265 if not isinstance(src, SourceFile):
266 src = Source(src, tags=str(target))
267 srcs.append(src)
268
269 self.sources = srcs
270 self.target = target
271 self.main = kwargs.get('main', False)
272 UnitTest.all.append(self)
273
274# Children should have access
275Export('Source')
276Export('PySource')
277Export('SimObject')
278Export('ProtoBuf')
279Export('UnitTest')
280
281########################################################################
282#
283# Debug Flags
284#
285debug_flags = {}
286def DebugFlag(name, desc=None):
287 if name in debug_flags:
288 raise AttributeError, "Flag %s already specified" % name
289 debug_flags[name] = (name, (), desc)
290
291def CompoundFlag(name, flags, desc=None):
292 if name in debug_flags:
293 raise AttributeError, "Flag %s already specified" % name
294
295 compound = tuple(flags)
296 debug_flags[name] = (name, compound, desc)
297
298Export('DebugFlag')
299Export('CompoundFlag')
300
301########################################################################
302#
303# Set some compiler variables
304#
305
306# Include file paths are rooted in this directory. SCons will
307# automatically expand '.' to refer to both the source directory and
308# the corresponding build directory to pick up generated include
309# files.
310env.Append(CPPPATH=Dir('.'))
311
312for extra_dir in extras_dir_list:
313 env.Append(CPPPATH=Dir(extra_dir))
314
315# Workaround for bug in SCons version > 0.97d20071212
316# Scons bug id: 2006 gem5 Bug id: 308
317for root, dirs, files in os.walk(base_dir, topdown=True):
318 Dir(root[len(base_dir) + 1:])
319
320########################################################################
321#
322# Walk the tree and execute all SConscripts in subdirectories
323#
324
325here = Dir('.').srcnode().abspath
326for root, dirs, files in os.walk(base_dir, topdown=True):
327 if root == here:
328 # we don't want to recurse back into this SConscript
329 continue
330
331 if 'SConscript' in files:
332 build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
333 Source.set_group(build_dir)
334 SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
335
336for extra_dir in extras_dir_list:
337 prefix_len = len(dirname(extra_dir)) + 1
338
339 # Also add the corresponding build directory to pick up generated
340 # include files.
341 env.Append(CPPPATH=Dir(joinpath(env['BUILDDIR'], extra_dir[prefix_len:])))
342
343 for root, dirs, files in os.walk(extra_dir, topdown=True):
344 # if build lives in the extras directory, don't walk down it
345 if 'build' in dirs:
346 dirs.remove('build')
347
348 if 'SConscript' in files:
349 build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
350 SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
351
352for opt in export_vars:
353 env.ConfigFile(opt)
354
355def makeTheISA(source, target, env):
356 isas = [ src.get_contents() for src in source ]
357 target_isa = env['TARGET_ISA']
358 def define(isa):
359 return isa.upper() + '_ISA'
360
361 def namespace(isa):
362 return isa[0].upper() + isa[1:].lower() + 'ISA'
363
364
365 code = code_formatter()
366 code('''\
367#ifndef __CONFIG_THE_ISA_HH__
368#define __CONFIG_THE_ISA_HH__
369
370''')
371
372 # create defines for the preprocessing and compile-time determination
373 for i,isa in enumerate(isas):
374 code('#define $0 $1', define(isa), i + 1)
375 code()
376
377 # create an enum for any run-time determination of the ISA, we
378 # reuse the same name as the namespaces
379 code('enum class Arch {')
380 for i,isa in enumerate(isas):
381 if i + 1 == len(isas):
382 code(' $0 = $1', namespace(isa), define(isa))
383 else:
384 code(' $0 = $1,', namespace(isa), define(isa))
385 code('};')
386
387 code('''
388
389#define THE_ISA ${{define(target_isa)}}
390#define TheISA ${{namespace(target_isa)}}
391#define THE_ISA_STR "${{target_isa}}"
392
393#endif // __CONFIG_THE_ISA_HH__''')
394
395 code.write(str(target[0]))
396
397env.Command('config/the_isa.hh', map(Value, all_isa_list),
398 MakeAction(makeTheISA, Transform("CFG ISA", 0)))
399
400def makeTheGPUISA(source, target, env):
401 isas = [ src.get_contents() for src in source ]
402 target_gpu_isa = env['TARGET_GPU_ISA']
403 def define(isa):
404 return isa.upper() + '_ISA'
405
406 def namespace(isa):
407 return isa[0].upper() + isa[1:].lower() + 'ISA'
408
409
410 code = code_formatter()
411 code('''\
412#ifndef __CONFIG_THE_GPU_ISA_HH__
413#define __CONFIG_THE_GPU_ISA_HH__
414
415''')
416
417 # create defines for the preprocessing and compile-time determination
418 for i,isa in enumerate(isas):
419 code('#define $0 $1', define(isa), i + 1)
420 code()
421
422 # create an enum for any run-time determination of the ISA, we
423 # reuse the same name as the namespaces
424 code('enum class GPUArch {')
425 for i,isa in enumerate(isas):
426 if i + 1 == len(isas):
427 code(' $0 = $1', namespace(isa), define(isa))
428 else:
429 code(' $0 = $1,', namespace(isa), define(isa))
430 code('};')
431
432 code('''
433
434#define THE_GPU_ISA ${{define(target_gpu_isa)}}
435#define TheGpuISA ${{namespace(target_gpu_isa)}}
436#define THE_GPU_ISA_STR "${{target_gpu_isa}}"
437
438#endif // __CONFIG_THE_GPU_ISA_HH__''')
439
440 code.write(str(target[0]))
441
442env.Command('config/the_gpu_isa.hh', map(Value, all_gpu_isa_list),
443 MakeAction(makeTheGPUISA, Transform("CFG ISA", 0)))
444
445########################################################################
446#
447# Prevent any SimObjects from being added after this point, they
448# should all have been added in the SConscripts above
449#
450SimObject.fixed = True
451
452class DictImporter(object):
453 '''This importer takes a dictionary of arbitrary module names that
454 map to arbitrary filenames.'''
455 def __init__(self, modules):
456 self.modules = modules
457 self.installed = set()
458
459 def __del__(self):
460 self.unload()
461
462 def unload(self):
463 import sys
464 for module in self.installed:
465 del sys.modules[module]
466 self.installed = set()
467
468 def find_module(self, fullname, path):
469 if fullname == 'm5.defines':
470 return self
471
472 if fullname == 'm5.objects':
473 return self
474
475 if fullname.startswith('_m5'):
476 return None
477
478 source = self.modules.get(fullname, None)
479 if source is not None and fullname.startswith('m5.objects'):
480 return self
481
482 return None
483
484 def load_module(self, fullname):
485 mod = imp.new_module(fullname)
486 sys.modules[fullname] = mod
487 self.installed.add(fullname)
488
489 mod.__loader__ = self
490 if fullname == 'm5.objects':
491 mod.__path__ = fullname.split('.')
492 return mod
493
494 if fullname == 'm5.defines':
495 mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
496 return mod
497
498 source = self.modules[fullname]
499 if source.modname == '__init__':
500 mod.__path__ = source.modpath
501 mod.__file__ = source.abspath
502
503 exec file(source.abspath, 'r') in mod.__dict__
504
505 return mod
506
507import m5.SimObject
508import m5.params
509from m5.util import code_formatter
510
511m5.SimObject.clear()
512m5.params.clear()
513
514# install the python importer so we can grab stuff from the source
515# tree itself. We can't have SimObjects added after this point or
516# else we won't know about them for the rest of the stuff.
517importer = DictImporter(PySource.modules)
518sys.meta_path[0:0] = [ importer ]
519
520# import all sim objects so we can populate the all_objects list
521# make sure that we're working with a list, then let's sort it
522for modname in SimObject.modnames:
523 exec('from m5.objects import %s' % modname)
524
525# we need to unload all of the currently imported modules so that they
526# will be re-imported the next time the sconscript is run
527importer.unload()
528sys.meta_path.remove(importer)
529
530sim_objects = m5.SimObject.allClasses
531all_enums = m5.params.allEnums
532
533for name,obj in sorted(sim_objects.iteritems()):
534 for param in obj._params.local.values():
535 # load the ptype attribute now because it depends on the
536 # current version of SimObject.allClasses, but when scons
537 # actually uses the value, all versions of
538 # SimObject.allClasses will have been loaded
539 param.ptype
540
541########################################################################
542#
543# calculate extra dependencies
544#
545module_depends = ["m5", "m5.SimObject", "m5.params"]
546depends = [ PySource.modules[dep].snode for dep in module_depends ]
547depends.sort(key = lambda x: x.name)
548
549########################################################################
550#
551# Commands for the basic automatically generated python files
552#
553
554# Generate Python file containing a dict specifying the current
555# buildEnv flags.
556def makeDefinesPyFile(target, source, env):
557 build_env = source[0].get_contents()
558
559 code = code_formatter()
560 code("""
561import _m5.core
562import m5.util
563
564buildEnv = m5.util.SmartDict($build_env)
565
566compileDate = _m5.core.compileDate
567_globals = globals()
568for key,val in _m5.core.__dict__.iteritems():
569 if key.startswith('flag_'):
570 flag = key[5:]
571 _globals[flag] = val
572del _globals
573""")
574 code.write(target[0].abspath)
575
576defines_info = Value(build_env)
577# Generate a file with all of the compile options in it
578env.Command('python/m5/defines.py', defines_info,
579 MakeAction(makeDefinesPyFile, Transform("DEFINES", 0)))
580PySource('m5', 'python/m5/defines.py')
581
582# Generate python file containing info about the M5 source code
583def makeInfoPyFile(target, source, env):
584 code = code_formatter()
585 for src in source:
586 data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
587 code('$src = ${{repr(data)}}')
588 code.write(str(target[0]))
589
590# Generate a file that wraps the basic top level files
591env.Command('python/m5/info.py',
592 [ '#/COPYING', '#/LICENSE', '#/README', ],
593 MakeAction(makeInfoPyFile, Transform("INFO")))
594PySource('m5', 'python/m5/info.py')
595
596########################################################################
597#
598# Create all of the SimObject param headers and enum headers
599#
600
601def createSimObjectParamStruct(target, source, env):
602 assert len(target) == 1 and len(source) == 1
603
604 name = source[0].get_text_contents()
605 obj = sim_objects[name]
606
607 code = code_formatter()
608 obj.cxx_param_decl(code)
609 code.write(target[0].abspath)
610
611def createSimObjectCxxConfig(is_header):
612 def body(target, source, env):
613 assert len(target) == 1 and len(source) == 1
614
615 name = str(source[0].get_contents())
616 obj = sim_objects[name]
617
618 code = code_formatter()
619 obj.cxx_config_param_file(code, is_header)
620 code.write(target[0].abspath)
621 return body
622
623def createEnumStrings(target, source, env):
624 assert len(target) == 1 and len(source) == 2
625
626 name = source[0].get_text_contents()
627 use_python = source[1].read()
628 obj = all_enums[name]
629
630 code = code_formatter()
631 obj.cxx_def(code)
632 if use_python:
633 obj.pybind_def(code)
634 code.write(target[0].abspath)
635
636def createEnumDecls(target, source, env):
637 assert len(target) == 1 and len(source) == 1
638
639 name = source[0].get_text_contents()
640 obj = all_enums[name]
641
642 code = code_formatter()
643 obj.cxx_decl(code)
644 code.write(target[0].abspath)
645
646def createSimObjectPyBindWrapper(target, source, env):
647 name = source[0].get_text_contents()
648 obj = sim_objects[name]
649
650 code = code_formatter()
651 obj.pybind_decl(code)
652 code.write(target[0].abspath)
653
654# Generate all of the SimObject param C++ struct header files
655params_hh_files = []
656for name,simobj in sorted(sim_objects.iteritems()):
657 py_source = PySource.modules[simobj.__module__]
658 extra_deps = [ py_source.tnode ]
659
660 hh_file = File('params/%s.hh' % name)
661 params_hh_files.append(hh_file)
662 env.Command(hh_file, Value(name),
663 MakeAction(createSimObjectParamStruct, Transform("SO PARAM")))
664 env.Depends(hh_file, depends + extra_deps)
665
666# C++ parameter description files
667if GetOption('with_cxx_config'):
668 for name,simobj in sorted(sim_objects.iteritems()):
669 py_source = PySource.modules[simobj.__module__]
670 extra_deps = [ py_source.tnode ]
671
672 cxx_config_hh_file = File('cxx_config/%s.hh' % name)
673 cxx_config_cc_file = File('cxx_config/%s.cc' % name)
674 env.Command(cxx_config_hh_file, Value(name),
675 MakeAction(createSimObjectCxxConfig(True),
676 Transform("CXXCPRHH")))
677 env.Command(cxx_config_cc_file, Value(name),
678 MakeAction(createSimObjectCxxConfig(False),
679 Transform("CXXCPRCC")))
680 env.Depends(cxx_config_hh_file, depends + extra_deps +
681 [File('params/%s.hh' % name), File('sim/cxx_config.hh')])
682 env.Depends(cxx_config_cc_file, depends + extra_deps +
683 [cxx_config_hh_file])
684 Source(cxx_config_cc_file)
685
686 cxx_config_init_cc_file = File('cxx_config/init.cc')
687
688 def createCxxConfigInitCC(target, source, env):
689 assert len(target) == 1 and len(source) == 1
690
691 code = code_formatter()
692
693 for name,simobj in sorted(sim_objects.iteritems()):
694 if not hasattr(simobj, 'abstract') or not simobj.abstract:
695 code('#include "cxx_config/${name}.hh"')
696 code()
697 code('void cxxConfigInit()')
698 code('{')
699 code.indent()
700 for name,simobj in sorted(sim_objects.iteritems()):
701 not_abstract = not hasattr(simobj, 'abstract') or \
702 not simobj.abstract
703 if not_abstract and 'type' in simobj.__dict__:
704 code('cxx_config_directory["${name}"] = '
705 '${name}CxxConfigParams::makeDirectoryEntry();')
706 code.dedent()
707 code('}')
708 code.write(target[0].abspath)
709
710 py_source = PySource.modules[simobj.__module__]
711 extra_deps = [ py_source.tnode ]
712 env.Command(cxx_config_init_cc_file, Value(name),
713 MakeAction(createCxxConfigInitCC, Transform("CXXCINIT")))
714 cxx_param_hh_files = ["cxx_config/%s.hh" % simobj
715 for name,simobj in sorted(sim_objects.iteritems())
716 if not hasattr(simobj, 'abstract') or not simobj.abstract]
717 Depends(cxx_config_init_cc_file, cxx_param_hh_files +
718 [File('sim/cxx_config.hh')])
719 Source(cxx_config_init_cc_file)
720
721# Generate all enum header files
722for name,enum in sorted(all_enums.iteritems()):
723 py_source = PySource.modules[enum.__module__]
724 extra_deps = [ py_source.tnode ]
725
726 cc_file = File('enums/%s.cc' % name)
727 env.Command(cc_file, [Value(name), Value(env['USE_PYTHON'])],
728 MakeAction(createEnumStrings, Transform("ENUM STR")))
729 env.Depends(cc_file, depends + extra_deps)
730 Source(cc_file)
731
732 hh_file = File('enums/%s.hh' % name)
733 env.Command(hh_file, Value(name),
734 MakeAction(createEnumDecls, Transform("ENUMDECL")))
735 env.Depends(hh_file, depends + extra_deps)
736
737# Generate SimObject Python bindings wrapper files
738if env['USE_PYTHON']:
739 for name,simobj in sorted(sim_objects.iteritems()):
740 py_source = PySource.modules[simobj.__module__]
741 extra_deps = [ py_source.tnode ]
742 cc_file = File('python/_m5/param_%s.cc' % name)
743 env.Command(cc_file, Value(name),
744 MakeAction(createSimObjectPyBindWrapper,
745 Transform("SO PyBind")))
746 env.Depends(cc_file, depends + extra_deps)
747 Source(cc_file)
748
749# Build all protocol buffers if we have got protoc and protobuf available
750if env['HAVE_PROTOBUF']:
751 for proto in ProtoBuf.all:
752 # Use both the source and header as the target, and the .proto
753 # file as the source. When executing the protoc compiler, also
754 # specify the proto_path to avoid having the generated files
755 # include the path.
756 env.Command([proto.cc_file, proto.hh_file], proto.tnode,
757 MakeAction('$PROTOC --cpp_out ${TARGET.dir} '
758 '--proto_path ${SOURCE.dir} $SOURCE',
759 Transform("PROTOC")))
760
761 # Add the C++ source file
762 Source(proto.cc_file, tags=proto.tags)
763elif ProtoBuf.all:
764 print 'Got protobuf to build, but lacks support!'
765 Exit(1)
766
767#
768# Handle debug flags
769#
770def makeDebugFlagCC(target, source, env):
771 assert(len(target) == 1 and len(source) == 1)
772
773 code = code_formatter()
774
775 # delay definition of CompoundFlags until after all the definition
776 # of all constituent SimpleFlags
777 comp_code = code_formatter()
778
779 # file header
780 code('''
781/*
782 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
783 */
784
785#include "base/debug.hh"
786
787namespace Debug {
788
789''')
790
791 for name, flag in sorted(source[0].read().iteritems()):
792 n, compound, desc = flag
793 assert n == name
794
795 if not compound:
796 code('SimpleFlag $name("$name", "$desc");')
797 else:
798 comp_code('CompoundFlag $name("$name", "$desc",')
799 comp_code.indent()
800 last = len(compound) - 1
801 for i,flag in enumerate(compound):
802 if i != last:
803 comp_code('&$flag,')
804 else:
805 comp_code('&$flag);')
806 comp_code.dedent()
807
808 code.append(comp_code)
809 code()
810 code('} // namespace Debug')
811
812 code.write(str(target[0]))
813
814def makeDebugFlagHH(target, source, env):
815 assert(len(target) == 1 and len(source) == 1)
816
817 val = eval(source[0].get_contents())
818 name, compound, desc = val
819
820 code = code_formatter()
821
822 # file header boilerplate
823 code('''\
824/*
825 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
826 */
827
828#ifndef __DEBUG_${name}_HH__
829#define __DEBUG_${name}_HH__
830
831namespace Debug {
832''')
833
834 if compound:
835 code('class CompoundFlag;')
836 code('class SimpleFlag;')
837
838 if compound:
839 code('extern CompoundFlag $name;')
840 for flag in compound:
841 code('extern SimpleFlag $flag;')
842 else:
843 code('extern SimpleFlag $name;')
844
845 code('''
846}
847
848#endif // __DEBUG_${name}_HH__
849''')
850
851 code.write(str(target[0]))
852
853for name,flag in sorted(debug_flags.iteritems()):
854 n, compound, desc = flag
855 assert n == name
856
857 hh_file = 'debug/%s.hh' % name
858 env.Command(hh_file, Value(flag),
859 MakeAction(makeDebugFlagHH, Transform("TRACING", 0)))
860
861env.Command('debug/flags.cc', Value(debug_flags),
862 MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
863Source('debug/flags.cc')
864
865# version tags
866tags = \
867env.Command('sim/tags.cc', None,
868 MakeAction('util/cpt_upgrader.py --get-cc-file > $TARGET',
869 Transform("VER TAGS")))
870env.AlwaysBuild(tags)
871
872# Embed python files. All .py files that have been indicated by a
873# PySource() call in a SConscript need to be embedded into the M5
874# library. To do that, we compile the file to byte code, marshal the
875# byte code, compress it, and then generate a c++ file that
876# inserts the result into an array.
877def embedPyFile(target, source, env):
878 def c_str(string):
879 if string is None:
880 return "0"
881 return '"%s"' % string
882
883 '''Action function to compile a .py into a code object, marshal
884 it, compress it, and stick it into an asm file so the code appears
885 as just bytes with a label in the data section'''
886
887 src = file(str(source[0]), 'r').read()
888
889 pysource = PySource.tnodes[source[0]]
890 compiled = compile(src, pysource.abspath, 'exec')
891 marshalled = marshal.dumps(compiled)
892 compressed = zlib.compress(marshalled)
893 data = compressed
894 sym = pysource.symname
895
896 code = code_formatter()
897 code('''\
898#include "sim/init.hh"
899
900namespace {
901
902const uint8_t data_${sym}[] = {
903''')
904 code.indent()
905 step = 16
906 for i in xrange(0, len(data), step):
907 x = array.array('B', data[i:i+step])
908 code(''.join('%d,' % d for d in x))
909 code.dedent()
910
911 code('''};
912
913EmbeddedPython embedded_${sym}(
914 ${{c_str(pysource.arcname)}},
915 ${{c_str(pysource.abspath)}},
916 ${{c_str(pysource.modpath)}},
917 data_${sym},
918 ${{len(data)}},
919 ${{len(marshalled)}});
920
921} // anonymous namespace
922''')
923 code.write(str(target[0]))
924
925for source in PySource.all:
926 env.Command(source.cpp, source.tnode,
927 MakeAction(embedPyFile, Transform("EMBED PY")))
928 Source(source.cpp, tags=source.tags, add_tags='python')
929
930########################################################################
931#
932# Define binaries. Each different build type (debug, opt, etc.) gets
933# a slightly different build environment.
934#
935
936# List of constructed environments to pass back to SConstruct
937date_source = Source('base/date.cc', tags=[])
938
939# Function to create a new build environment as clone of current
940# environment 'env' with modified object suffix and optional stripped
941# binary. Additional keyword arguments are appended to corresponding
942# build environment vars.
943def makeEnv(env, label, objsfx, strip=False, disable_partial=False, **kwargs):
944 # SCons doesn't know to append a library suffix when there is a '.' in the
945 # name. Use '_' instead.
946 libname = 'gem5_' + label
947 exename = 'gem5.' + label
948 secondary_exename = 'm5.' + label
949
950 new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
951 new_env.Label = label
952 new_env.Append(**kwargs)
953
955 werror_env = new_env.Clone()
956 # Treat warnings as errors but white list some warnings that we
957 # want to allow (e.g., deprecation warnings).
958 werror_env.Append(CCFLAGS=['-Werror',
959 '-Wno-error=deprecated-declarations',
960 '-Wno-error=deprecated',
961 ])
954 def make_obj(source, static, extra_deps=None):
955 '''This function creates a scons node of the requested type, and sets
956 up any additional dependencies.'''
957
963 def make_obj(source, static, extra_deps = None):
964 '''This function adds the specified source to the correct
965 build environment, and returns the corresponding SCons Object
966 nodes'''
967
968 if source.Werror:
969 env = werror_env
970 else:
971 env = new_env
972
958 if static:
974 obj = env.StaticObject(source.tnode)
959 obj = new_env.StaticObject(source.tnode)
960 else:
976 obj = env.SharedObject(source.tnode)
961 obj = new_env.SharedObject(source.tnode)
962
963 if extra_deps:
979 env.Depends(obj, extra_deps)
964 new_env.Depends(obj, extra_deps)
965
966 return obj
967
968 lib_sources = Source.all.with_tag('gem5 lib')
969
970 # Without Python, leave out all Python content from the library
971 # builds. The option doesn't affect gem5 built as a program
972 if GetOption('without_python'):
973 lib_sources = lib_sources.without_tag('python')
974
975 static_objs = []
976 shared_objs = []
977
978 for s in lib_sources.with_tag(Source.ungrouped_tag):
979 static_objs.append(make_obj(s, True))
980 shared_objs.append(make_obj(s, False))
981
982 partial_objs = []
983
984 for group in Source.source_groups:
985 srcs = lib_sources.with_tag(Source.link_group_tag(group))
986 if not srcs:
987 continue
988
989 # If partial linking is disabled, add these sources to the build
990 # directly, and short circuit this loop.
991 if disable_partial:
992 for s in srcs:
993 static_objs.append(make_obj(s, True))
994 shared_objs.append(make_obj(s, False))
995 continue
996
997 # Set up the static partially linked objects.
998 source_objs = [ make_obj(s, True) for s in srcs ]
999 file_name = new_env.subst("${OBJPREFIX}lib${OBJSUFFIX}.partial")
1000 target = File(joinpath(group, file_name))
1001 partial = env.PartialStatic(target=target, source=source_objs)
1002 static_objs.append(partial)
1003
1004 # Set up the shared partially linked objects.
1005 source_objs = [ make_obj(s, False) for s in srcs ]
1006 file_name = new_env.subst("${SHOBJPREFIX}lib${SHOBJSUFFIX}.partial")
1007 target = File(joinpath(group, file_name))
1008 partial = env.PartialShared(target=target, source=source_objs)
1009 shared_objs.append(partial)
1010
1011 static_date = make_obj(date_source, static=True, extra_deps=static_objs)
1012 static_objs.append(static_date)
1013
1014 shared_date = make_obj(date_source, static=False, extra_deps=shared_objs)
1015 shared_objs.append(shared_date)
1016
1017 # First make a library of everything but main() so other programs can
1018 # link against m5.
1019 static_lib = new_env.StaticLibrary(libname, static_objs)
1020 shared_lib = new_env.SharedLibrary(libname, shared_objs)
1021
1022 # Now link a stub with main() and the static library.
1023 main_objs = [ make_obj(s, True) for s in Source.all.with_tag('main') ]
1024
1025 for test in UnitTest.all:
1026 test_sources = Source.all.with_tag(str(test.target))
1027 test_objs = [ make_obj(s, static=True) for s in test_sources ]
1028 if test.main:
1029 test_objs += main_objs
1030 path = 'unittest/%s.%s' % (test.target, label)
1031 new_env.Program(path, test_objs + static_objs)
1032
1033 progname = exename
1034 if strip:
1035 progname += '.unstripped'
1036
1037 targets = new_env.Program(progname, main_objs + static_objs)
1038
1039 if strip:
1040 if sys.platform == 'sunos5':
1041 cmd = 'cp $SOURCE $TARGET; strip $TARGET'
1042 else:
1043 cmd = 'strip $SOURCE -o $TARGET'
1044 targets = new_env.Command(exename, progname,
1045 MakeAction(cmd, Transform("STRIP")))
1046
1047 new_env.Command(secondary_exename, exename,
1048 MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK")))
1049
1050 new_env.M5Binary = targets[0]
1051
1052 # Set up regression tests.
1053 SConscript(os.path.join(env.root.abspath, 'tests', 'SConscript'),
1054 variant_dir=Dir('tests').Dir(new_env.Label),
1055 exports={ 'env' : new_env }, duplicate=False)
1056
1057# Start out with the compiler flags common to all compilers,
1058# i.e. they all use -g for opt and -g -pg for prof
1059ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'],
1060 'perf' : ['-g']}
1061
1062# Start out with the linker flags common to all linkers, i.e. -pg for
1063# prof, and -lprofiler for perf. The -lprofile flag is surrounded by
1064# no-as-needed and as-needed as the binutils linker is too clever and
1065# simply doesn't link to the library otherwise.
1066ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'],
1067 'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']}
1068
1069# For Link Time Optimization, the optimisation flags used to compile
1070# individual files are decoupled from those used at link time
1071# (i.e. you can compile with -O3 and perform LTO with -O0), so we need
1072# to also update the linker flags based on the target.
1073if env['GCC']:
1074 if sys.platform == 'sunos5':
1075 ccflags['debug'] += ['-gstabs+']
1076 else:
1077 ccflags['debug'] += ['-ggdb3']
1078 ldflags['debug'] += ['-O0']
1079 # opt, fast, prof and perf all share the same cc flags, also add
1080 # the optimization to the ldflags as LTO defers the optimization
1081 # to link time
1082 for target in ['opt', 'fast', 'prof', 'perf']:
1083 ccflags[target] += ['-O3']
1084 ldflags[target] += ['-O3']
1085
1086 ccflags['fast'] += env['LTO_CCFLAGS']
1087 ldflags['fast'] += env['LTO_LDFLAGS']
1088elif env['CLANG']:
1089 ccflags['debug'] += ['-g', '-O0']
1090 # opt, fast, prof and perf all share the same cc flags
1091 for target in ['opt', 'fast', 'prof', 'perf']:
1092 ccflags[target] += ['-O3']
1093else:
1094 print 'Unknown compiler, please fix compiler options'
1095 Exit(1)
1096
1097
1098# To speed things up, we only instantiate the build environments we
1099# need. We try to identify the needed environment for each target; if
1100# we can't, we fall back on instantiating all the environments just to
1101# be safe.
1102target_types = ['debug', 'opt', 'fast', 'prof', 'perf']
1103obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof',
1104 'gpo' : 'perf'}
1105
1106def identifyTarget(t):
1107 ext = t.split('.')[-1]
1108 if ext in target_types:
1109 return ext
1110 if obj2target.has_key(ext):
1111 return obj2target[ext]
1112 match = re.search(r'/tests/([^/]+)/', t)
1113 if match and match.group(1) in target_types:
1114 return match.group(1)
1115 return 'all'
1116
1117needed_envs = [identifyTarget(target) for target in BUILD_TARGETS]
1118if 'all' in needed_envs:
1119 needed_envs += target_types
1120
1121# Debug binary
1122if 'debug' in needed_envs:
1123 makeEnv(env, 'debug', '.do',
1124 CCFLAGS = Split(ccflags['debug']),
1125 CPPDEFINES = ['DEBUG', 'TRACING_ON=1'],
1126 LINKFLAGS = Split(ldflags['debug']))
1127
1128# Optimized binary
1129if 'opt' in needed_envs:
1130 makeEnv(env, 'opt', '.o',
1131 CCFLAGS = Split(ccflags['opt']),
1132 CPPDEFINES = ['TRACING_ON=1'],
1133 LINKFLAGS = Split(ldflags['opt']))
1134
1135# "Fast" binary
1136if 'fast' in needed_envs:
1137 disable_partial = \
1138 env.get('BROKEN_INCREMENTAL_LTO', False) and \
1139 GetOption('force_lto')
1140 makeEnv(env, 'fast', '.fo', strip = True,
1141 CCFLAGS = Split(ccflags['fast']),
1142 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1143 LINKFLAGS = Split(ldflags['fast']),
1144 disable_partial=disable_partial)
1145
1146# Profiled binary using gprof
1147if 'prof' in needed_envs:
1148 makeEnv(env, 'prof', '.po',
1149 CCFLAGS = Split(ccflags['prof']),
1150 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1151 LINKFLAGS = Split(ldflags['prof']))
1152
1153# Profiled binary using google-pprof
1154if 'perf' in needed_envs:
1155 makeEnv(env, 'perf', '.gpo',
1156 CCFLAGS = Split(ccflags['perf']),
1157 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1158 LINKFLAGS = Split(ldflags['perf']))