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