SConscript revision 7673:b28bd1fa9a35
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 sys
38import zlib
39
40from os.path import basename, dirname, exists, isdir, isfile, join as joinpath
41
42import SCons
43
44# This file defines how to build a particular configuration of M5
45# based on variable settings in the 'env' build environment.
46
47Import('*')
48
49# Children need to see the environment
50Export('env')
51
52build_env = [(opt, env[opt]) for opt in export_vars]
53
54########################################################################
55# Code for adding source files of various types
56#
57class SourceMeta(type):
58    def __init__(cls, name, bases, dict):
59        super(SourceMeta, cls).__init__(name, bases, dict)
60        cls.all = []
61        
62    def get(cls, **kwargs):
63        for src in cls.all:
64            for attr,value in kwargs.iteritems():
65                if getattr(src, attr) != value:
66                    break
67            else:
68                yield src
69
70class SourceFile(object):
71    __metaclass__ = SourceMeta
72    def __init__(self, source):
73        tnode = source
74        if not isinstance(source, SCons.Node.FS.File):
75            tnode = File(source)
76
77        self.tnode = tnode
78        self.snode = tnode.srcnode()
79        self.filename = str(tnode)
80        self.dirname = dirname(self.filename)
81        self.basename = basename(self.filename)
82        index = self.basename.rfind('.')
83        if index <= 0:
84            # dot files aren't extensions
85            self.extname = self.basename, None
86        else:
87            self.extname = self.basename[:index], self.basename[index+1:]
88
89        for base in type(self).__mro__:
90            if issubclass(base, SourceFile):
91                base.all.append(self)
92
93    def __lt__(self, other): return self.filename < other.filename
94    def __le__(self, other): return self.filename <= other.filename
95    def __gt__(self, other): return self.filename > other.filename
96    def __ge__(self, other): return self.filename >= other.filename
97    def __eq__(self, other): return self.filename == other.filename
98    def __ne__(self, other): return self.filename != other.filename
99        
100class Source(SourceFile):
101    '''Add a c/c++ source file to the build'''
102    def __init__(self, source, Werror=True, swig=False, bin_only=False,
103                 skip_lib=False):
104        super(Source, self).__init__(source)
105
106        self.Werror = Werror
107        self.swig = swig
108        self.bin_only = bin_only
109        self.skip_lib = bin_only or skip_lib
110
111class PySource(SourceFile):
112    '''Add a python source file to the named package'''
113    invalid_sym_char = re.compile('[^A-z0-9_]')
114    modules = {}
115    tnodes = {}
116    symnames = {}
117    
118    def __init__(self, package, source):
119        super(PySource, self).__init__(source)
120
121        modname,ext = self.extname
122        assert ext == 'py'
123
124        if package:
125            path = package.split('.')
126        else:
127            path = []
128
129        modpath = path[:]
130        if modname != '__init__':
131            modpath += [ modname ]
132        modpath = '.'.join(modpath)
133
134        arcpath = path + [ self.basename ]
135        abspath = self.snode.abspath
136        if not exists(abspath):
137            abspath = self.tnode.abspath
138
139        self.package = package
140        self.modname = modname
141        self.modpath = modpath
142        self.arcname = joinpath(*arcpath)
143        self.abspath = abspath
144        self.compiled = File(self.filename + 'c')
145        self.assembly = File(self.filename + '.s')
146        self.symname = "PyEMB_" + PySource.invalid_sym_char.sub('_', modpath)
147
148        PySource.modules[modpath] = self
149        PySource.tnodes[self.tnode] = self
150        PySource.symnames[self.symname] = self
151
152class SimObject(PySource):
153    '''Add a SimObject python file as a python source object and add
154    it to a list of sim object modules'''
155
156    fixed = False
157    modnames = []
158
159    def __init__(self, source):
160        super(SimObject, self).__init__('m5.objects', source)
161        if self.fixed:
162            raise AttributeError, "Too late to call SimObject now."
163
164        bisect.insort_right(SimObject.modnames, self.modname)
165
166class SwigSource(SourceFile):
167    '''Add a swig file to build'''
168
169    def __init__(self, package, source):
170        super(SwigSource, self).__init__(source)
171
172        modname,ext = self.extname
173        assert ext == 'i'
174
175        self.module = modname
176        cc_file = joinpath(self.dirname, modname + '_wrap.cc')
177        py_file = joinpath(self.dirname, modname + '.py')
178
179        self.cc_source = Source(cc_file, swig=True)
180        self.py_source = PySource(package, py_file)
181
182unit_tests = []
183def UnitTest(target, sources):
184    if not isinstance(sources, (list, tuple)):
185        sources = [ sources ]
186
187    sources = [ Source(src, skip_lib=True) for src in sources ]
188    unit_tests.append((target, sources))
189
190# Children should have access
191Export('Source')
192Export('PySource')
193Export('SimObject')
194Export('SwigSource')
195Export('UnitTest')
196
197########################################################################
198#
199# Trace Flags
200#
201trace_flags = {}
202def TraceFlag(name, desc=None):
203    if name in trace_flags:
204        raise AttributeError, "Flag %s already specified" % name
205    trace_flags[name] = (name, (), desc)
206
207def CompoundFlag(name, flags, desc=None):
208    if name in trace_flags:
209        raise AttributeError, "Flag %s already specified" % name
210
211    compound = tuple(flags)
212    trace_flags[name] = (name, compound, desc)
213
214Export('TraceFlag')
215Export('CompoundFlag')
216
217########################################################################
218#
219# Set some compiler variables
220#
221
222# Include file paths are rooted in this directory.  SCons will
223# automatically expand '.' to refer to both the source directory and
224# the corresponding build directory to pick up generated include
225# files.
226env.Append(CPPPATH=Dir('.'))
227
228for extra_dir in extras_dir_list:
229    env.Append(CPPPATH=Dir(extra_dir))
230
231# Workaround for bug in SCons version > 0.97d20071212
232# Scons bug id: 2006 M5 Bug id: 308 
233for root, dirs, files in os.walk(base_dir, topdown=True):
234    Dir(root[len(base_dir) + 1:])
235
236########################################################################
237#
238# Walk the tree and execute all SConscripts in subdirectories
239#
240
241here = Dir('.').srcnode().abspath
242for root, dirs, files in os.walk(base_dir, topdown=True):
243    if root == here:
244        # we don't want to recurse back into this SConscript
245        continue
246
247    if 'SConscript' in files:
248        build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
249        SConscript(joinpath(root, 'SConscript'), build_dir=build_dir)
250
251for extra_dir in extras_dir_list:
252    prefix_len = len(dirname(extra_dir)) + 1
253    for root, dirs, files in os.walk(extra_dir, topdown=True):
254        if 'SConscript' in files:
255            build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
256            SConscript(joinpath(root, 'SConscript'), build_dir=build_dir)
257
258for opt in export_vars:
259    env.ConfigFile(opt)
260
261def makeTheISA(source, target, env):
262    isas = [ src.get_contents() for src in source ]
263    target_isa = env['TARGET_ISA']
264    def define(isa):
265        return isa.upper() + '_ISA'
266    
267    def namespace(isa):
268        return isa[0].upper() + isa[1:].lower() + 'ISA' 
269
270
271    code = code_formatter()
272    code('''\
273#ifndef __CONFIG_THE_ISA_HH__
274#define __CONFIG_THE_ISA_HH__
275
276''')
277
278    for i,isa in enumerate(isas):
279        code('#define $0 $1', define(isa), i + 1)
280
281    code('''
282
283#define THE_ISA ${{define(target_isa)}}
284#define TheISA ${{namespace(target_isa)}}
285
286#endif // __CONFIG_THE_ISA_HH__''')
287
288    code.write(str(target[0]))
289
290env.Command('config/the_isa.hh', map(Value, all_isa_list), makeTheISA)
291
292########################################################################
293#
294# Prevent any SimObjects from being added after this point, they
295# should all have been added in the SConscripts above
296#
297SimObject.fixed = True
298
299class DictImporter(object):
300    '''This importer takes a dictionary of arbitrary module names that
301    map to arbitrary filenames.'''
302    def __init__(self, modules):
303        self.modules = modules
304        self.installed = set()
305
306    def __del__(self):
307        self.unload()
308
309    def unload(self):
310        import sys
311        for module in self.installed:
312            del sys.modules[module]
313        self.installed = set()
314
315    def find_module(self, fullname, path):
316        if fullname == 'm5.defines':
317            return self
318
319        if fullname == 'm5.objects':
320            return self
321
322        if fullname.startswith('m5.internal'):
323            return None
324
325        source = self.modules.get(fullname, None)
326        if source is not None and fullname.startswith('m5.objects'):
327            return self
328
329        return None
330
331    def load_module(self, fullname):
332        mod = imp.new_module(fullname)
333        sys.modules[fullname] = mod
334        self.installed.add(fullname)
335
336        mod.__loader__ = self
337        if fullname == 'm5.objects':
338            mod.__path__ = fullname.split('.')
339            return mod
340
341        if fullname == 'm5.defines':
342            mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
343            return mod
344
345        source = self.modules[fullname]
346        if source.modname == '__init__':
347            mod.__path__ = source.modpath
348        mod.__file__ = source.abspath
349
350        exec file(source.abspath, 'r') in mod.__dict__
351
352        return mod
353
354import m5.SimObject
355import m5.params
356from m5.util import code_formatter
357
358m5.SimObject.clear()
359m5.params.clear()
360
361# install the python importer so we can grab stuff from the source
362# tree itself.  We can't have SimObjects added after this point or
363# else we won't know about them for the rest of the stuff.
364importer = DictImporter(PySource.modules)
365sys.meta_path[0:0] = [ importer ]
366
367# import all sim objects so we can populate the all_objects list
368# make sure that we're working with a list, then let's sort it
369for modname in SimObject.modnames:
370    exec('from m5.objects import %s' % modname)
371
372# we need to unload all of the currently imported modules so that they
373# will be re-imported the next time the sconscript is run
374importer.unload()
375sys.meta_path.remove(importer)
376
377sim_objects = m5.SimObject.allClasses
378all_enums = m5.params.allEnums
379
380all_params = {}
381for name,obj in sorted(sim_objects.iteritems()):
382    for param in obj._params.local.values():
383        # load the ptype attribute now because it depends on the
384        # current version of SimObject.allClasses, but when scons
385        # actually uses the value, all versions of
386        # SimObject.allClasses will have been loaded
387        param.ptype
388
389        if not hasattr(param, 'swig_decl'):
390            continue
391        pname = param.ptype_str
392        if pname not in all_params:
393            all_params[pname] = param
394
395########################################################################
396#
397# calculate extra dependencies
398#
399module_depends = ["m5", "m5.SimObject", "m5.params"]
400depends = [ PySource.modules[dep].tnode for dep in module_depends ]
401
402########################################################################
403#
404# Commands for the basic automatically generated python files
405#
406
407# Generate Python file containing a dict specifying the current
408# buildEnv flags.
409def makeDefinesPyFile(target, source, env):
410    build_env, hg_info = [ x.get_contents() for x in source ]
411
412    code = code_formatter()
413    code("""
414import m5.internal
415import m5.util
416
417buildEnv = m5.util.SmartDict($build_env)
418hgRev = '$hg_info'
419
420compileDate = m5.internal.core.compileDate
421_globals = globals()
422for key,val in m5.internal.core.__dict__.iteritems():
423    if key.startswith('flag_'):
424        flag = key[5:]
425        _globals[flag] = val
426del _globals
427""")
428    code.write(target[0].abspath)
429
430defines_info = [ Value(build_env), Value(env['HG_INFO']) ]
431# Generate a file with all of the compile options in it
432env.Command('python/m5/defines.py', defines_info, makeDefinesPyFile)
433PySource('m5', 'python/m5/defines.py')
434
435# Generate python file containing info about the M5 source code
436def makeInfoPyFile(target, source, env):
437    code = code_formatter()
438    for src in source:
439        data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
440        code('$src = ${{repr(data)}}')
441    code.write(str(target[0]))
442
443# Generate a file that wraps the basic top level files
444env.Command('python/m5/info.py',
445            [ '#/AUTHORS', '#/LICENSE', '#/README', '#/RELEASE_NOTES' ],
446            makeInfoPyFile)
447PySource('m5', 'python/m5/info.py')
448
449# Generate the __init__.py file for m5.objects
450def makeObjectsInitFile(target, source, env):
451    code = code_formatter()
452    code('''\
453from params import *
454from m5.SimObject import *
455''')
456
457    for module in source:
458        code('from $0 import *', module.get_contents())
459    code.write(str(target[0]))
460
461# Generate an __init__.py file for the objects package
462env.Command('python/m5/objects/__init__.py',
463            map(Value, SimObject.modnames),
464            makeObjectsInitFile)
465PySource('m5.objects', 'python/m5/objects/__init__.py')
466
467########################################################################
468#
469# Create all of the SimObject param headers and enum headers
470#
471
472def createSimObjectParam(target, source, env):
473    assert len(target) == 1 and len(source) == 1
474
475    name = str(source[0].get_contents())
476    obj = sim_objects[name]
477
478    code = code_formatter()
479    obj.cxx_decl(code)
480    code.write(target[0].abspath)
481
482def createSwigParam(target, source, env):
483    assert len(target) == 1 and len(source) == 1
484
485    name = str(source[0].get_contents())
486    param = all_params[name]
487
488    code = code_formatter()
489    param.swig_decl(code)
490    code.write(target[0].abspath)
491
492def createEnumStrings(target, source, env):
493    assert len(target) == 1 and len(source) == 1
494
495    name = str(source[0].get_contents())
496    obj = all_enums[name]
497
498    code = code_formatter()
499    obj.cxx_def(code)
500    code.write(target[0].abspath)
501
502def createEnumParam(target, source, env):
503    assert len(target) == 1 and len(source) == 1
504
505    name = str(source[0].get_contents())
506    obj = all_enums[name]
507
508    code = code_formatter()
509    obj.cxx_decl(code)
510    code.write(target[0].abspath)
511
512# Generate all of the SimObject param struct header files
513params_hh_files = []
514for name,simobj in sorted(sim_objects.iteritems()):
515    py_source = PySource.modules[simobj.__module__]
516    extra_deps = [ py_source.tnode ]
517
518    hh_file = File('params/%s.hh' % name)
519    params_hh_files.append(hh_file)
520    env.Command(hh_file, Value(name), createSimObjectParam)
521    env.Depends(hh_file, depends + extra_deps)
522
523# Generate any parameter header files needed
524params_i_files = []
525for name,param in all_params.iteritems():
526    i_file = File('params/%s_%s.i' % (name, param.file_ext))
527    params_i_files.append(i_file)
528    env.Command(i_file, Value(name), createSwigParam)
529    env.Depends(i_file, depends)
530
531# Generate all enum header files
532for name,enum in sorted(all_enums.iteritems()):
533    py_source = PySource.modules[enum.__module__]
534    extra_deps = [ py_source.tnode ]
535
536    cc_file = File('enums/%s.cc' % name)
537    env.Command(cc_file, Value(name), createEnumStrings)
538    env.Depends(cc_file, depends + extra_deps)
539    Source(cc_file)
540
541    hh_file = File('enums/%s.hh' % name)
542    env.Command(hh_file, Value(name), createEnumParam)
543    env.Depends(hh_file, depends + extra_deps)
544
545# Build the big monolithic swigged params module (wraps all SimObject
546# param structs and enum structs)
547def buildParams(target, source, env):
548    names = [ s.get_contents() for s in source ]
549    objs = [ sim_objects[name] for name in names ]
550
551    ordered_objs = []
552    obj_seen = set()
553    def order_obj(obj):
554        name = str(obj)
555        if name in obj_seen:
556            return
557
558        obj_seen.add(name)
559        if str(obj) != 'SimObject':
560            order_obj(obj.__bases__[0])
561
562        ordered_objs.append(obj)
563
564    for obj in objs:
565        order_obj(obj)
566
567    code = code_formatter()
568    code('%module params')
569
570    code('%{')
571    for obj in ordered_objs:
572        code('#include "params/$obj.hh"')
573    code('%}')
574
575    for obj in ordered_objs:
576        params = obj._params.local.values()
577        for param in params:
578            param.swig_predecls(code)
579
580    enums = set()
581    for obj in ordered_objs:
582        params = obj._params.local.values()
583        for param in params:
584            ptype = param.ptype
585            if issubclass(ptype, m5.params.Enum) and ptype not in enums:
586                enums.add(ptype)
587                code('%include "enums/$0.hh"', ptype.__name__)
588    
589    for obj in ordered_objs:
590        obj.swig_objdecls(code)
591        code()
592
593    for obj in ordered_objs:
594        continue
595        if obj.swig_objdecls:
596            obj.swig_objdecls(code)
597            continue
598
599        class_path = obj.cxx_class.split('::')
600        classname = class_path[-1]
601        namespaces = class_path[:-1]
602
603        for ns in namespaces:
604            code('namespace $ns {')
605
606        if namespaces:
607            code('// avoid name conflicts')
608            sep_string = '_COLONS_'
609            flat_name = sep_string.join(class_path)
610            code('%rename($flat_name) $classname;')
611
612        code('// stop swig from creating/wrapping default ctor/dtor')
613        code('%nodefault $classname;')
614        if obj._base:
615            code('class $classname : public ${{obj._base.cxx_class}} {};')
616        else:
617            code('class $classname {};')
618
619        for ns in reversed(namespaces):
620            code('/* namespace $ns */ }')
621        code()
622
623    code('%include "src/sim/sim_object_params.hh"')
624    for obj in ordered_objs:
625        code('%include "params/$obj.hh"')
626
627    code.write(target[0].abspath)
628
629params_file = File('params/params.i')
630names = sorted(sim_objects.keys())
631env.Command(params_file, map(Value, names), buildParams)
632env.Depends(params_file, params_hh_files + params_i_files + depends)
633SwigSource('m5.objects', params_file)
634
635# Build all swig modules
636for swig in SwigSource.all:
637    env.Command([swig.cc_source.tnode, swig.py_source.tnode], swig.tnode,
638                '$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} '
639                '-o ${TARGETS[0]} $SOURCES')
640    env.Depends(swig.py_source.tnode, swig.tnode)
641    env.Depends(swig.cc_source.tnode, swig.tnode)
642
643# Generate the main swig init file
644def makeSwigInit(target, source, env):
645    code = code_formatter()
646
647    code('extern "C" {')
648    code.indent()
649    for module in source:
650        code('void init_$0();', module.get_contents())
651    code.dedent()
652    code('}')
653
654    code('void initSwig() {')
655    code.indent()
656    for module in source:
657        code('init_$0();', module.get_contents())
658    code.dedent()
659    code('}')
660
661    code.write(str(target[0]))
662
663env.Command('python/swig/init.cc',
664            map(Value, sorted(s.module for s in SwigSource.all)),
665            makeSwigInit)
666Source('python/swig/init.cc')
667
668def getFlags(source_flags):
669    flagsMap = {}
670    flagsList = []
671    for s in source_flags:
672        val = eval(s.get_contents())
673        name, compound, desc = val
674        flagsList.append(val)
675        flagsMap[name] = bool(compound)
676    
677    for name, compound, desc in flagsList:
678        for flag in compound:
679            if flag not in flagsMap:
680                raise AttributeError, "Trace flag %s not found" % flag
681            if flagsMap[flag]:
682                raise AttributeError, \
683                    "Compound flag can't point to another compound flag"
684
685    flagsList.sort()
686    return flagsList
687
688
689# Generate traceflags.py
690def traceFlagsPy(target, source, env):
691    assert(len(target) == 1)
692    code = code_formatter()
693
694    allFlags = getFlags(source)
695
696    code('basic = [')
697    code.indent()
698    for flag, compound, desc in allFlags:
699        if not compound:
700            code("'$flag',")
701    code(']')
702    code.dedent()
703    code()
704
705    code('compound = [')
706    code.indent()
707    code("'All',")
708    for flag, compound, desc in allFlags:
709        if compound:
710            code("'$flag',")
711    code("]")
712    code.dedent()
713    code()
714
715    code("all = frozenset(basic + compound)")
716    code()
717
718    code('compoundMap = {')
719    code.indent()
720    all = tuple([flag for flag,compound,desc in allFlags if not compound])
721    code("'All' : $all,")
722    for flag, compound, desc in allFlags:
723        if compound:
724            code("'$flag' : $compound,")
725    code('}')
726    code.dedent()
727    code()
728
729    code('descriptions = {')
730    code.indent()
731    code("'All' : 'All flags',")
732    for flag, compound, desc in allFlags:
733        code("'$flag' : '$desc',")
734    code("}")
735    code.dedent()
736
737    code.write(str(target[0]))
738
739def traceFlagsCC(target, source, env):
740    assert(len(target) == 1)
741
742    allFlags = getFlags(source)
743    code = code_formatter()
744
745    # file header
746    code('''
747/*
748 * DO NOT EDIT THIS FILE! Automatically generated
749 */
750
751#include "base/traceflags.hh"
752
753using namespace Trace;
754
755const char *Trace::flagStrings[] =
756{''')
757
758    code.indent()
759    # The string array is used by SimpleEnumParam to map the strings
760    # provided by the user to enum values.
761    for flag, compound, desc in allFlags:
762        if not compound:
763            code('"$flag",')
764
765    code('"All",')
766    for flag, compound, desc in allFlags:
767        if compound:
768            code('"$flag",')
769    code.dedent()
770
771    code('''\
772};
773
774const int Trace::numFlagStrings = ${{len(allFlags) + 1}};
775
776''')
777
778    # Now define the individual compound flag arrays.  There is an array
779    # for each compound flag listing the component base flags.
780    all = tuple([flag for flag,compound,desc in allFlags if not compound])
781    code('static const Flags AllMap[] = {')
782    code.indent()
783    for flag, compound, desc in allFlags:
784        if not compound:
785            code('$flag,')
786    code.dedent()
787    code('};')
788    code()
789
790    for flag, compound, desc in allFlags:
791        if not compound:
792            continue
793        code('static const Flags ${flag}Map[] = {')
794        code.indent()
795        for flag in compound:
796            code('$flag,')
797        code('(Flags)-1')
798        code.dedent()
799        code('};')
800        code()
801
802    # Finally the compoundFlags[] array maps the compound flags
803    # to their individual arrays/
804    code('const Flags *Trace::compoundFlags[] = {')
805    code.indent()
806    code('AllMap,')
807    for flag, compound, desc in allFlags:
808        if compound:
809            code('${flag}Map,')
810    # file trailer
811    code.dedent()
812    code('};')
813
814    code.write(str(target[0]))
815
816def traceFlagsHH(target, source, env):
817    assert(len(target) == 1)
818
819    allFlags = getFlags(source)
820    code = code_formatter()
821
822    # file header boilerplate
823    code('''\
824/*
825 * DO NOT EDIT THIS FILE!
826 *
827 * Automatically generated from traceflags.py
828 */
829
830#ifndef __BASE_TRACE_FLAGS_HH__
831#define __BASE_TRACE_FLAGS_HH__
832
833namespace Trace {
834
835enum Flags {''')
836
837    # Generate the enum.  Base flags come first, then compound flags.
838    idx = 0
839    code.indent()
840    for flag, compound, desc in allFlags:
841        if not compound:
842            code('$flag = $idx,')
843            idx += 1
844
845    numBaseFlags = idx
846    code('NumFlags = $idx,')
847    code.dedent()
848    code()
849
850    # put a comment in here to separate base from compound flags
851    code('''
852// The remaining enum values are *not* valid indices for Trace::flags.
853// They are "compound" flags, which correspond to sets of base
854// flags, and are used by changeFlag.''')
855
856    code.indent()
857    code('All = $idx,')
858    idx += 1
859    for flag, compound, desc in allFlags:
860        if compound:
861            code('$flag = $idx,')
862            idx += 1
863
864    numCompoundFlags = idx - numBaseFlags
865    code('NumCompoundFlags = $numCompoundFlags')
866    code.dedent()
867
868    # trailer boilerplate
869    code('''\
870}; // enum Flags
871
872// Array of strings for SimpleEnumParam
873extern const char *flagStrings[];
874extern const int numFlagStrings;
875
876// Array of arraay pointers: for each compound flag, gives the list of
877// base flags to set.  Inidividual flag arrays are terminated by -1.
878extern const Flags *compoundFlags[];
879
880/* namespace Trace */ }
881
882#endif // __BASE_TRACE_FLAGS_HH__
883''')
884
885    code.write(str(target[0]))
886
887flags = map(Value, trace_flags.values())
888env.Command('base/traceflags.py', flags, traceFlagsPy)
889PySource('m5', 'base/traceflags.py')
890
891env.Command('base/traceflags.hh', flags, traceFlagsHH)
892env.Command('base/traceflags.cc', flags, traceFlagsCC)
893Source('base/traceflags.cc')
894
895# embed python files.  All .py files that have been indicated by a
896# PySource() call in a SConscript need to be embedded into the M5
897# library.  To do that, we compile the file to byte code, marshal the
898# byte code, compress it, and then generate an assembly file that
899# inserts the result into the data section with symbols indicating the
900# beginning, and end (and with the size at the end)
901def objectifyPyFile(target, source, env):
902    '''Action function to compile a .py into a code object, marshal
903    it, compress it, and stick it into an asm file so the code appears
904    as just bytes with a label in the data section'''
905
906    src = file(str(source[0]), 'r').read()
907
908    pysource = PySource.tnodes[source[0]]
909    compiled = compile(src, pysource.abspath, 'exec')
910    marshalled = marshal.dumps(compiled)
911    compressed = zlib.compress(marshalled)
912    data = compressed
913
914    # Some C/C++ compilers prepend an underscore to global symbol
915    # names, so if they're going to do that, we need to prepend that
916    # leading underscore to globals in the assembly file.
917    if env['LEADING_UNDERSCORE']:
918        sym = '_' + pysource.symname
919    else:
920        sym = pysource.symname
921
922    step = 16
923    code = code_formatter()
924    code('''\
925.data
926.globl ${sym}_beg
927.globl ${sym}_end
928${sym}_beg:''')
929
930    for i in xrange(0, len(data), step):
931        x = array.array('B', data[i:i+step])
932        bytes = ','.join([str(d) for d in x])
933        code('.byte $bytes')
934    code('${sym}_end:')
935    code('.long $0', len(marshalled))
936
937    code.write(str(target[0]))
938
939for source in PySource.all:
940    env.Command(source.assembly, source.tnode, objectifyPyFile)
941    Source(source.assembly)
942
943# Generate init_python.cc which creates a bunch of EmbeddedPyModule
944# structs that describe the embedded python code.  One such struct
945# contains information about the importer that python uses to get at
946# the embedded files, and then there's a list of all of the rest that
947# the importer uses to load the rest on demand.
948def pythonInit(target, source, env):
949    code = code_formatter()
950
951    def dump_mod(sym, endchar=','):
952        def c_str(string):
953            if string is None:
954                return "0"
955            return '"%s"' % string
956
957        pysource = PySource.symnames[sym]
958        arcname = c_str(pysource.arcname)
959        abspath = c_str(pysource.abspath)
960        modpath = c_str(pysource.modpath)
961        code.indent()
962        code('''\
963{ $arcname,
964  $abspath,
965  $modpath,
966  ${sym}_beg, ${sym}_end,
967  ${sym}_end - ${sym}_beg,
968  *(int *)${sym}_end }$endchar
969''')
970        code.dedent()
971
972    code('#include "sim/init.hh"')
973    for sym in source:
974        sym = sym.get_contents()
975        code('extern const char ${sym}_beg[], ${sym}_end[];')
976
977    code('const EmbeddedPyModule embeddedPyImporter = ')
978    dump_mod("PyEMB_importer", endchar=';')
979    code()
980
981    code('const EmbeddedPyModule embeddedPyModules[] = {')
982    for i,sym in enumerate(source):
983        sym = sym.get_contents()
984        if sym == "PyEMB_importer":
985            # Skip the importer since we've already exported it
986            continue
987        dump_mod(sym)
988    code('    { 0, 0, 0, 0, 0, 0, 0 }')
989    code('};')
990
991    code.write(str(target[0]))
992
993env.Command('sim/init_python.cc',
994            map(Value, (s.symname for s in PySource.all)),
995            pythonInit)
996Source('sim/init_python.cc')
997
998########################################################################
999#
1000# Define binaries.  Each different build type (debug, opt, etc.) gets
1001# a slightly different build environment.
1002#
1003
1004# List of constructed environments to pass back to SConstruct
1005envList = []
1006
1007date_source = Source('base/date.cc', skip_lib=True)
1008
1009# Function to create a new build environment as clone of current
1010# environment 'env' with modified object suffix and optional stripped
1011# binary.  Additional keyword arguments are appended to corresponding
1012# build environment vars.
1013def makeEnv(label, objsfx, strip = False, **kwargs):
1014    # SCons doesn't know to append a library suffix when there is a '.' in the
1015    # name.  Use '_' instead.
1016    libname = 'm5_' + label
1017    exename = 'm5.' + label
1018
1019    new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
1020    new_env.Label = label
1021    new_env.Append(**kwargs)
1022
1023    swig_env = new_env.Clone()
1024    swig_env.Append(CCFLAGS='-Werror')
1025    if env['GCC']:
1026        swig_env.Append(CCFLAGS='-Wno-uninitialized')
1027        swig_env.Append(CCFLAGS='-Wno-sign-compare')
1028        swig_env.Append(CCFLAGS='-Wno-parentheses')
1029
1030    werror_env = new_env.Clone()
1031    werror_env.Append(CCFLAGS='-Werror')
1032
1033    def make_obj(source, static, extra_deps = None):
1034        '''This function adds the specified source to the correct
1035        build environment, and returns the corresponding SCons Object
1036        nodes'''
1037
1038        if source.swig:
1039            env = swig_env
1040        elif source.Werror:
1041            env = werror_env
1042        else:
1043            env = new_env
1044
1045        if static:
1046            obj = env.StaticObject(source.tnode)
1047        else:
1048            obj = env.SharedObject(source.tnode)
1049
1050        if extra_deps:
1051            env.Depends(obj, extra_deps)
1052
1053        return obj
1054
1055    static_objs = [ make_obj(s, True) for s in Source.get(skip_lib=False)]
1056    shared_objs = [ make_obj(s, False) for s in Source.get(skip_lib=False)]
1057
1058    static_date = make_obj(date_source, static=True, extra_deps=static_objs)
1059    static_objs.append(static_date)
1060    
1061    shared_date = make_obj(date_source, static=False, extra_deps=shared_objs)
1062    shared_objs.append(shared_date)
1063
1064    # First make a library of everything but main() so other programs can
1065    # link against m5.
1066    static_lib = new_env.StaticLibrary(libname, static_objs)
1067    shared_lib = new_env.SharedLibrary(libname, shared_objs)
1068
1069    for target, sources in unit_tests:
1070        objs = [ make_obj(s, static=True) for s in sources ]
1071        new_env.Program("unittest/%s.%s" % (target, label), objs + static_objs)
1072
1073    # Now link a stub with main() and the static library.
1074    bin_objs = [make_obj(s, True) for s in Source.get(bin_only=True) ]
1075    progname = exename
1076    if strip:
1077        progname += '.unstripped'
1078
1079    targets = new_env.Program(progname, bin_objs + static_objs)
1080
1081    if strip:
1082        if sys.platform == 'sunos5':
1083            cmd = 'cp $SOURCE $TARGET; strip $TARGET'
1084        else:
1085            cmd = 'strip $SOURCE -o $TARGET'
1086        targets = new_env.Command(exename, progname, cmd)
1087            
1088    new_env.M5Binary = targets[0]
1089    envList.append(new_env)
1090
1091# Debug binary
1092ccflags = {}
1093if env['GCC']:
1094    if sys.platform == 'sunos5':
1095        ccflags['debug'] = '-gstabs+'
1096    else:
1097        ccflags['debug'] = '-ggdb3'
1098    ccflags['opt'] = '-g -O3'
1099    ccflags['fast'] = '-O3'
1100    ccflags['prof'] = '-O3 -g -pg'
1101elif env['SUNCC']:
1102    ccflags['debug'] = '-g0'
1103    ccflags['opt'] = '-g -O'
1104    ccflags['fast'] = '-fast'
1105    ccflags['prof'] = '-fast -g -pg'
1106elif env['ICC']:
1107    ccflags['debug'] = '-g -O0'
1108    ccflags['opt'] = '-g -O'
1109    ccflags['fast'] = '-fast'
1110    ccflags['prof'] = '-fast -g -pg'
1111else:
1112    print 'Unknown compiler, please fix compiler options'
1113    Exit(1)
1114
1115makeEnv('debug', '.do',
1116        CCFLAGS = Split(ccflags['debug']),
1117        CPPDEFINES = ['DEBUG', 'TRACING_ON=1'])
1118
1119# Optimized binary
1120makeEnv('opt', '.o',
1121        CCFLAGS = Split(ccflags['opt']),
1122        CPPDEFINES = ['TRACING_ON=1'])
1123
1124# "Fast" binary
1125makeEnv('fast', '.fo', strip = True,
1126        CCFLAGS = Split(ccflags['fast']),
1127        CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'])
1128
1129# Profiled binary
1130makeEnv('prof', '.po',
1131        CCFLAGS = Split(ccflags['prof']),
1132        CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1133        LINKFLAGS = '-pg')
1134
1135Return('envList')
1136