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