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