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