SConscript revision 12303:70f8400b7ed3
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']))
1212