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