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