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