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