SConscript revision 5522
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 = []
546for source,package in swig_sources:
547    filename = str(source)
548    assert filename.endswith('.i')
549
550    base = '.'.join(filename.split('.')[:-1])
551    module = basename(base)
552    cc_file = base + '_wrap.cc'
553    py_file = base + '.py'
554
555    env.Command([cc_file, py_file], source,
556                '$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} '
557                '-o ${TARGETS[0]} $SOURCES')
558    env.Depends(py_file, source)
559    env.Depends(cc_file, source)
560
561    swig_modules.append(Value(module))
562    Source(cc_file)
563    PySource(package, py_file)
564
565# Generate the main swig init file
566def makeSwigInit(target, source, env):
567    f = file(str(target[0]), 'w')
568    print >>f, 'extern "C" {'
569    for module in source:
570        print >>f, '    void init_%s();' % module.get_contents()
571    print >>f, '}'
572    print >>f, 'void initSwig() {'
573    for module in source:
574        print >>f, '    init_%s();' % module.get_contents()
575    print >>f, '}'
576    f.close()
577
578env.Command('python/swig/init.cc', swig_modules, makeSwigInit)
579Source('python/swig/init.cc')
580
581# Generate traceflags.py
582def traceFlagsPy(target, source, env):
583    assert(len(target) == 1)
584
585    f = file(str(target[0]), 'w')
586
587    allFlags = []
588    for s in source:
589        val = eval(s.get_contents())
590        allFlags.append(val)
591
592    print >>f, 'baseFlags = ['
593    for flag, compound, desc in allFlags:
594        if not compound:
595            print >>f, "    '%s'," % flag
596    print >>f, "    ]"
597    print >>f
598
599    print >>f, 'compoundFlags = ['
600    print >>f, "    'All',"
601    for flag, compound, desc in allFlags:
602        if compound:
603            print >>f, "    '%s'," % flag
604    print >>f, "    ]"
605    print >>f
606
607    print >>f, "allFlags = frozenset(baseFlags + compoundFlags)"
608    print >>f
609
610    print >>f, 'compoundFlagMap = {'
611    all = tuple([flag for flag,compound,desc in allFlags if not compound])
612    print >>f, "    'All' : %s," % (all, )
613    for flag, compound, desc in allFlags:
614        if compound:
615            print >>f, "    '%s' : %s," % (flag, compound)
616    print >>f, "    }"
617    print >>f
618
619    print >>f, 'flagDescriptions = {'
620    print >>f, "    'All' : 'All flags',"
621    for flag, compound, desc in allFlags:
622        print >>f, "    '%s' : '%s'," % (flag, desc)
623    print >>f, "    }"
624
625    f.close()
626
627def traceFlagsCC(target, source, env):
628    assert(len(target) == 1)
629
630    f = file(str(target[0]), 'w')
631
632    allFlags = []
633    for s in source:
634        val = eval(s.get_contents())
635        allFlags.append(val)
636
637    # file header
638    print >>f, '''
639/*
640 * DO NOT EDIT THIS FILE! Automatically generated
641 */
642
643#include "base/traceflags.hh"
644
645using namespace Trace;
646
647const char *Trace::flagStrings[] =
648{'''
649
650    # The string array is used by SimpleEnumParam to map the strings
651    # provided by the user to enum values.
652    for flag, compound, desc in allFlags:
653        if not compound:
654            print >>f, '    "%s",' % flag
655
656    print >>f, '    "All",'
657    for flag, compound, desc in allFlags:
658        if compound:
659            print >>f, '    "%s",' % flag
660
661    print >>f, '};'
662    print >>f
663    print >>f, 'const int Trace::numFlagStrings = %d;' % (len(allFlags) + 1)
664    print >>f
665
666    #
667    # Now define the individual compound flag arrays.  There is an array
668    # for each compound flag listing the component base flags.
669    #
670    all = tuple([flag for flag,compound,desc in allFlags if not compound])
671    print >>f, 'static const Flags AllMap[] = {'
672    for flag, compound, desc in allFlags:
673        if not compound:
674            print >>f, "    %s," % flag
675    print >>f, '};'
676    print >>f
677
678    for flag, compound, desc in allFlags:
679        if not compound:
680            continue
681        print >>f, 'static const Flags %sMap[] = {' % flag
682        for flag in compound:
683            print >>f, "    %s," % flag
684        print >>f, "    (Flags)-1"
685        print >>f, '};'
686        print >>f
687
688    #
689    # Finally the compoundFlags[] array maps the compound flags
690    # to their individual arrays/
691    #
692    print >>f, 'const Flags *Trace::compoundFlags[] ='
693    print >>f, '{'
694    print >>f, '    AllMap,'
695    for flag, compound, desc in allFlags:
696        if compound:
697            print >>f, '    %sMap,' % flag
698    # file trailer
699    print >>f, '};'
700
701    f.close()
702
703def traceFlagsHH(target, source, env):
704    assert(len(target) == 1)
705
706    f = file(str(target[0]), 'w')
707
708    allFlags = []
709    for s in source:
710        val = eval(s.get_contents())
711        allFlags.append(val)
712
713    # file header boilerplate
714    print >>f, '''
715/*
716 * DO NOT EDIT THIS FILE!
717 *
718 * Automatically generated from traceflags.py
719 */
720
721#ifndef __BASE_TRACE_FLAGS_HH__
722#define __BASE_TRACE_FLAGS_HH__
723
724namespace Trace {
725
726enum Flags {'''
727
728    # Generate the enum.  Base flags come first, then compound flags.
729    idx = 0
730    for flag, compound, desc in allFlags:
731        if not compound:
732            print >>f, '    %s = %d,' % (flag, idx)
733            idx += 1
734
735    numBaseFlags = idx
736    print >>f, '    NumFlags = %d,' % idx
737
738    # put a comment in here to separate base from compound flags
739    print >>f, '''
740// The remaining enum values are *not* valid indices for Trace::flags.
741// They are "compound" flags, which correspond to sets of base
742// flags, and are used by changeFlag.'''
743
744    print >>f, '    All = %d,' % idx
745    idx += 1
746    for flag, compound, desc in allFlags:
747        if compound:
748            print >>f, '    %s = %d,' % (flag, idx)
749            idx += 1
750
751    numCompoundFlags = idx - numBaseFlags
752    print >>f, '    NumCompoundFlags = %d' % numCompoundFlags
753
754    # trailer boilerplate
755    print >>f, '''\
756}; // enum Flags
757
758// Array of strings for SimpleEnumParam
759extern const char *flagStrings[];
760extern const int numFlagStrings;
761
762// Array of arraay pointers: for each compound flag, gives the list of
763// base flags to set.  Inidividual flag arrays are terminated by -1.
764extern const Flags *compoundFlags[];
765
766/* namespace Trace */ }
767
768#endif // __BASE_TRACE_FLAGS_HH__
769'''
770
771    f.close()
772
773flags = [ Value(f) for f in trace_flags ]
774env.Command('base/traceflags.py', flags, traceFlagsPy)
775PySource('m5', 'base/traceflags.py')
776
777env.Command('base/traceflags.hh', flags, traceFlagsHH)
778env.Command('base/traceflags.cc', flags, traceFlagsCC)
779Source('base/traceflags.cc')
780
781# Generate program_info.cc
782def programInfo(target, source, env):
783    def gen_file(target, rev, node, date):
784        pi_stats = file(target, 'w')
785        print >>pi_stats, 'const char *hgRev = "%s:%s";' %  (rev, node)
786        print >>pi_stats, 'const char *hgDate = "%s";' % date
787        pi_stats.close()
788
789    target = str(target[0])
790    scons_dir = str(source[0].get_contents())
791    try:
792        import mercurial.demandimport, mercurial.hg, mercurial.ui
793        import mercurial.util, mercurial.node
794        if not exists(scons_dir) or not isdir(scons_dir) or \
795               not exists(joinpath(scons_dir, ".hg")):
796            raise ValueError
797        repo = mercurial.hg.repository(mercurial.ui.ui(), scons_dir)
798        rev = mercurial.node.nullrev + repo.changelog.count()
799        changenode = repo.changelog.node(rev)
800        changes = repo.changelog.read(changenode)
801        date = mercurial.util.datestr(changes[2])
802
803        gen_file(target, rev, mercurial.node.hex(changenode), date)
804
805        mercurial.demandimport.disable()
806    except ImportError:
807        gen_file(target, "Unknown", "Unknown", "Unknown")
808
809    except:
810        print "in except"
811        gen_file(target, "Unknown", "Unknown", "Unknown")
812        mercurial.demandimport.disable()
813
814env.Command('base/program_info.cc',
815            Value(str(SCons.Node.FS.default_fs.SConstruct_dir)),
816            programInfo)
817
818# embed python files.  All .py files that have been indicated by a
819# PySource() call in a SConscript need to be embedded into the M5
820# library.  To do that, we compile the file to byte code, marshal the
821# byte code, compress it, and then generate an assembly file that
822# inserts the result into the data section with symbols indicating the
823# beginning, and end (and with the size at the end)
824py_sources_tnodes = {}
825for pysource in py_sources:
826    py_sources_tnodes[pysource.tnode] = pysource
827
828def objectifyPyFile(target, source, env):
829    '''Action function to compile a .py into a code object, marshal
830    it, compress it, and stick it into an asm file so the code appears
831    as just bytes with a label in the data section'''
832
833    src = file(str(source[0]), 'r').read()
834    dst = file(str(target[0]), 'w')
835
836    pysource = py_sources_tnodes[source[0]]
837    compiled = compile(src, pysource.snode.path, 'exec')
838    marshalled = marshal.dumps(compiled)
839    compressed = zlib.compress(marshalled)
840    data = compressed
841
842    # Some C/C++ compilers prepend an underscore to global symbol
843    # names, so if they're going to do that, we need to prepend that
844    # leading underscore to globals in the assembly file.
845    if env['LEADING_UNDERSCORE']:
846        sym = '_' + pysource.symname
847    else:
848        sym = pysource.symname
849
850    step = 16
851    print >>dst, ".data"
852    print >>dst, ".globl %s_beg" % sym
853    print >>dst, ".globl %s_end" % sym
854    print >>dst, "%s_beg:" % sym
855    for i in xrange(0, len(data), step):
856        x = array.array('B', data[i:i+step])
857        print >>dst, ".byte", ','.join([str(d) for d in x])
858    print >>dst, "%s_end:" % sym
859    print >>dst, ".long %d" % len(marshalled)
860
861for source in py_sources:
862    env.Command(source.assembly, source.tnode, objectifyPyFile)
863    Source(source.assembly)
864
865# Generate init_python.cc which creates a bunch of EmbeddedPyModule
866# structs that describe the embedded python code.  One such struct
867# contains information about the importer that python uses to get at
868# the embedded files, and then there's a list of all of the rest that
869# the importer uses to load the rest on demand.
870py_sources_symbols = {}
871for pysource in py_sources:
872    py_sources_symbols[pysource.symname] = pysource
873def pythonInit(target, source, env):
874    dst = file(str(target[0]), 'w')
875
876    def dump_mod(sym, endchar=','):
877        pysource = py_sources_symbols[sym]
878        print >>dst, '    { "%s",' % pysource.arcname
879        print >>dst, '      "%s",' % pysource.modpath
880        print >>dst, '       %s_beg, %s_end,' % (sym, sym)
881        print >>dst, '       %s_end - %s_beg,' % (sym, sym)
882        print >>dst, '       *(int *)%s_end }%s'  % (sym, endchar)
883    
884    print >>dst, '#include "sim/init.hh"'
885
886    for sym in source:
887        sym = sym.get_contents()
888        print >>dst, "extern const char %s_beg[], %s_end[];" % (sym, sym)
889
890    print >>dst, "const EmbeddedPyModule embeddedPyImporter = "
891    dump_mod("PyEMB_importer", endchar=';');
892    print >>dst
893
894    print >>dst, "const EmbeddedPyModule embeddedPyModules[] = {"
895    for i,sym in enumerate(source):
896        sym = sym.get_contents()
897        if sym == "PyEMB_importer":
898            # Skip the importer since we've already exported it
899            continue
900        dump_mod(sym)
901    print >>dst, "    { 0, 0, 0, 0, 0, 0 }"
902    print >>dst, "};"
903
904symbols = [Value(s.symname) for s in py_sources]
905env.Command('sim/init_python.cc', symbols, pythonInit)
906Source('sim/init_python.cc')
907
908########################################################################
909#
910# Define binaries.  Each different build type (debug, opt, etc.) gets
911# a slightly different build environment.
912#
913
914# List of constructed environments to pass back to SConstruct
915envList = []
916
917# This function adds the specified sources to the given build
918# environment, and returns a list of all the corresponding SCons
919# Object nodes (including an extra one for date.cc).  We explicitly
920# add the Object nodes so we can set up special dependencies for
921# date.cc.
922def make_objs(sources, env):
923    objs = [env.Object(s) for s in sources]
924  
925    # make date.cc depend on all other objects so it always gets
926    # recompiled whenever anything else does
927    date_obj = env.Object('base/date.cc')
928
929    # Make the generation of program_info.cc dependend on all 
930    # the other cc files and the compiling of program_info.cc 
931    # dependent on all the objects but program_info.o 
932    pinfo_obj = env.Object('base/program_info.cc')
933    env.Depends('base/program_info.cc', sources)
934    env.Depends(date_obj, objs)
935    env.Depends(pinfo_obj, objs)
936    objs.extend([date_obj,pinfo_obj])
937    return objs
938
939# Function to create a new build environment as clone of current
940# environment 'env' with modified object suffix and optional stripped
941# binary.  Additional keyword arguments are appended to corresponding
942# build environment vars.
943def makeEnv(label, objsfx, strip = False, **kwargs):
944    newEnv = env.Copy(OBJSUFFIX=objsfx)
945    newEnv.Label = label
946    newEnv.Append(**kwargs)
947
948    # First make a library of everything but main() so other programs can
949    # link against m5.
950    #
951    # SCons doesn't know to append a library suffix when there is a '.' in the
952    # name.  Use '_' instead.
953    m5lib = newEnv.Library('m5_' + label, make_objs(cc_lib_sources, newEnv))
954
955    # Now link a stub with main() and the library.
956    exe = 'm5.' + label  # final executable
957    objects = [newEnv.Object(s) for s in cc_bin_sources] + m5lib
958    if strip:
959        unstripped_exe = exe + '.unstripped'
960        newEnv.Program(unstripped_exe, objects)
961        if sys.platform == 'sunos5':
962            cmd = 'cp $SOURCE $TARGET; strip $TARGET'
963        else:
964            cmd = 'strip $SOURCE -o $TARGET'
965        targets = newEnv.Command(exe, unstripped_exe, cmd)
966    else:
967        targets = newEnv.Program(exe, objects)
968            
969    newEnv.M5Binary = targets[0]
970    envList.append(newEnv)
971
972# Debug binary
973ccflags = {}
974if env['GCC']:
975    if sys.platform == 'sunos5':
976        ccflags['debug'] = '-gstabs+'
977    else:
978        ccflags['debug'] = '-ggdb3'
979    ccflags['opt'] = '-g -O3'
980    ccflags['fast'] = '-O3'
981    ccflags['prof'] = '-O3 -g -pg'
982elif env['SUNCC']:
983    ccflags['debug'] = '-g0'
984    ccflags['opt'] = '-g -O'
985    ccflags['fast'] = '-fast'
986    ccflags['prof'] = '-fast -g -pg'
987elif env['ICC']:
988    ccflags['debug'] = '-g -O0'
989    ccflags['opt'] = '-g -O'
990    ccflags['fast'] = '-fast'
991    ccflags['prof'] = '-fast -g -pg'
992else:
993    print 'Unknown compiler, please fix compiler options'
994    Exit(1)
995
996makeEnv('debug', '.do',
997        CCFLAGS = Split(ccflags['debug']),
998        CPPDEFINES = ['DEBUG', 'TRACING_ON=1'])
999
1000# Optimized binary
1001makeEnv('opt', '.o',
1002        CCFLAGS = Split(ccflags['opt']),
1003        CPPDEFINES = ['TRACING_ON=1'])
1004
1005# "Fast" binary
1006makeEnv('fast', '.fo', strip = True,
1007        CCFLAGS = Split(ccflags['fast']),
1008        CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'])
1009
1010# Profiled binary
1011makeEnv('prof', '.po',
1012        CCFLAGS = Split(ccflags['prof']),
1013        CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1014        LINKFLAGS = '-pg')
1015
1016Return('envList')
1017