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