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