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