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