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