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