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