SConscript revision 8235
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 bisect
33import imp
34import marshal
35import os
36import re
37import sys
38import zlib
39
40from os.path import basename, dirname, exists, isdir, isfile, join as joinpath
41
42import SCons
43
44# This file defines how to build a particular configuration of M5
45# based on variable settings in the 'env' build environment.
46
47Import('*')
48
49# Children need to see the environment
50Export('env')
51
52build_env = [(opt, env[opt]) for opt in export_vars]
53
54from m5.util import code_formatter
55
56########################################################################
57# Code for adding source files of various types
58#
59# When specifying a source file of some type, a set of guards can be
60# specified for that file.  When get() is used to find the files, if
61# get specifies a set of filters, only files that match those filters
62# will be accepted (unspecified filters on files are assumed to be
63# false).  Current filters are:
64#     main -- specifies the m5 main() function
65#     skip_lib -- do not put this file into the m5 library
66#     <unittest> -- unit tests use filters based on the unit test name
67#
68# A parent can now be specified for a source file and default filter
69# values will be retrieved recursively from parents (children override
70# parents).
71#
72class SourceMeta(type):
73    '''Meta class for source files that keeps track of all files of a
74    particular type and has a get function for finding all functions
75    of a certain type that match a set of guards'''
76    def __init__(cls, name, bases, dict):
77        super(SourceMeta, cls).__init__(name, bases, dict)
78        cls.all = []
79        
80    def get(cls, **guards):
81        '''Find all files that match the specified guards.  If a source
82        file does not specify a flag, the default is False'''
83        for src in cls.all:
84            for flag,value in guards.iteritems():
85                # if the flag is found and has a different value, skip
86                # this file
87                if src.all_guards.get(flag, False) != value:
88                    break
89            else:
90                yield src
91
92class SourceFile(object):
93    '''Base object that encapsulates the notion of a source file.
94    This includes, the source node, target node, various manipulations
95    of those.  A source file also specifies a set of guards which
96    describing which builds the source file applies to.  A parent can
97    also be specified to get default guards from'''
98    __metaclass__ = SourceMeta
99    def __init__(self, source, parent=None, **guards):
100        self.guards = guards
101        self.parent = parent
102
103        tnode = source
104        if not isinstance(source, SCons.Node.FS.File):
105            tnode = File(source)
106
107        self.tnode = tnode
108        self.snode = tnode.srcnode()
109
110        for base in type(self).__mro__:
111            if issubclass(base, SourceFile):
112                base.all.append(self)
113
114    @property
115    def filename(self):
116        return str(self.tnode)
117
118    @property
119    def dirname(self):
120        return dirname(self.filename)
121
122    @property
123    def basename(self):
124        return basename(self.filename)
125
126    @property
127    def extname(self):
128        index = self.basename.rfind('.')
129        if index <= 0:
130            # dot files aren't extensions
131            return self.basename, None
132
133        return self.basename[:index], self.basename[index+1:]
134
135    @property
136    def all_guards(self):
137        '''find all guards for this object getting default values
138        recursively from its parents'''
139        guards = {}
140        if self.parent:
141            guards.update(self.parent.guards)
142        guards.update(self.guards)
143        return guards
144
145    def __lt__(self, other): return self.filename < other.filename
146    def __le__(self, other): return self.filename <= other.filename
147    def __gt__(self, other): return self.filename > other.filename
148    def __ge__(self, other): return self.filename >= other.filename
149    def __eq__(self, other): return self.filename == other.filename
150    def __ne__(self, other): return self.filename != other.filename
151        
152class Source(SourceFile):
153    '''Add a c/c++ source file to the build'''
154    def __init__(self, source, Werror=True, swig=False, **guards):
155        '''specify the source file, and any guards'''
156        super(Source, self).__init__(source, **guards)
157
158        self.Werror = Werror
159        self.swig = swig
160
161class PySource(SourceFile):
162    '''Add a python source file to the named package'''
163    invalid_sym_char = re.compile('[^A-z0-9_]')
164    modules = {}
165    tnodes = {}
166    symnames = {}
167    
168    def __init__(self, package, source, **guards):
169        '''specify the python package, the source file, and any guards'''
170        super(PySource, self).__init__(source, **guards)
171
172        modname,ext = self.extname
173        assert ext == 'py'
174
175        if package:
176            path = package.split('.')
177        else:
178            path = []
179
180        modpath = path[:]
181        if modname != '__init__':
182            modpath += [ modname ]
183        modpath = '.'.join(modpath)
184
185        arcpath = path + [ self.basename ]
186        abspath = self.snode.abspath
187        if not exists(abspath):
188            abspath = self.tnode.abspath
189
190        self.package = package
191        self.modname = modname
192        self.modpath = modpath
193        self.arcname = joinpath(*arcpath)
194        self.abspath = abspath
195        self.compiled = File(self.filename + 'c')
196        self.cpp = File(self.filename + '.cc')
197        self.symname = PySource.invalid_sym_char.sub('_', modpath)
198
199        PySource.modules[modpath] = self
200        PySource.tnodes[self.tnode] = self
201        PySource.symnames[self.symname] = self
202
203class SimObject(PySource):
204    '''Add a SimObject python file as a python source object and add
205    it to a list of sim object modules'''
206
207    fixed = False
208    modnames = []
209
210    def __init__(self, source, **guards):
211        '''Specify the source file and any guards (automatically in
212        the m5.objects package)'''
213        super(SimObject, self).__init__('m5.objects', source, **guards)
214        if self.fixed:
215            raise AttributeError, "Too late to call SimObject now."
216
217        bisect.insort_right(SimObject.modnames, self.modname)
218
219class SwigSource(SourceFile):
220    '''Add a swig file to build'''
221
222    def __init__(self, package, source, **guards):
223        '''Specify the python package, the source file, and any guards'''
224        super(SwigSource, self).__init__(source, **guards)
225
226        modname,ext = self.extname
227        assert ext == 'i'
228
229        self.module = modname
230        cc_file = joinpath(self.dirname, modname + '_wrap.cc')
231        py_file = joinpath(self.dirname, modname + '.py')
232
233        self.cc_source = Source(cc_file, swig=True, parent=self)
234        self.py_source = PySource(package, py_file, parent=self)
235
236class UnitTest(object):
237    '''Create a UnitTest'''
238
239    all = []
240    def __init__(self, target, *sources):
241        '''Specify the target name and any sources.  Sources that are
242        not SourceFiles are evalued with Source().  All files are
243        guarded with a guard of the same name as the UnitTest
244        target.'''
245
246        srcs = []
247        for src in sources:
248            if not isinstance(src, SourceFile):
249                src = Source(src, skip_lib=True)
250            src.guards[target] = True
251            srcs.append(src)
252
253        self.sources = srcs
254        self.target = target
255        UnitTest.all.append(self)
256
257# Children should have access
258Export('Source')
259Export('PySource')
260Export('SimObject')
261Export('SwigSource')
262Export('UnitTest')
263
264########################################################################
265#
266# Debug Flags
267#
268debug_flags = {}
269def DebugFlag(name, desc=None):
270    if name in debug_flags:
271        raise AttributeError, "Flag %s already specified" % name
272    debug_flags[name] = (name, (), desc)
273TraceFlag = DebugFlag
274
275def CompoundFlag(name, flags, desc=None):
276    if name in debug_flags:
277        raise AttributeError, "Flag %s already specified" % name
278
279    compound = tuple(flags)
280    debug_flags[name] = (name, compound, desc)
281
282Export('DebugFlag')
283Export('TraceFlag')
284Export('CompoundFlag')
285
286########################################################################
287#
288# Set some compiler variables
289#
290
291# Include file paths are rooted in this directory.  SCons will
292# automatically expand '.' to refer to both the source directory and
293# the corresponding build directory to pick up generated include
294# files.
295env.Append(CPPPATH=Dir('.'))
296
297for extra_dir in extras_dir_list:
298    env.Append(CPPPATH=Dir(extra_dir))
299
300# Workaround for bug in SCons version > 0.97d20071212
301# Scons bug id: 2006 M5 Bug id: 308 
302for root, dirs, files in os.walk(base_dir, topdown=True):
303    Dir(root[len(base_dir) + 1:])
304
305########################################################################
306#
307# Walk the tree and execute all SConscripts in subdirectories
308#
309
310here = Dir('.').srcnode().abspath
311for root, dirs, files in os.walk(base_dir, topdown=True):
312    if root == here:
313        # we don't want to recurse back into this SConscript
314        continue
315
316    if 'SConscript' in files:
317        build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
318        SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
319
320for extra_dir in extras_dir_list:
321    prefix_len = len(dirname(extra_dir)) + 1
322    for root, dirs, files in os.walk(extra_dir, topdown=True):
323        if 'SConscript' in files:
324            build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
325            SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
326
327for opt in export_vars:
328    env.ConfigFile(opt)
329
330def makeTheISA(source, target, env):
331    isas = [ src.get_contents() for src in source ]
332    target_isa = env['TARGET_ISA']
333    def define(isa):
334        return isa.upper() + '_ISA'
335    
336    def namespace(isa):
337        return isa[0].upper() + isa[1:].lower() + 'ISA' 
338
339
340    code = code_formatter()
341    code('''\
342#ifndef __CONFIG_THE_ISA_HH__
343#define __CONFIG_THE_ISA_HH__
344
345''')
346
347    for i,isa in enumerate(isas):
348        code('#define $0 $1', define(isa), i + 1)
349
350    code('''
351
352#define THE_ISA ${{define(target_isa)}}
353#define TheISA ${{namespace(target_isa)}}
354
355#endif // __CONFIG_THE_ISA_HH__''')
356
357    code.write(str(target[0]))
358
359env.Command('config/the_isa.hh', map(Value, all_isa_list),
360            MakeAction(makeTheISA, Transform("CFG ISA", 0)))
361
362########################################################################
363#
364# Prevent any SimObjects from being added after this point, they
365# should all have been added in the SConscripts above
366#
367SimObject.fixed = True
368
369class DictImporter(object):
370    '''This importer takes a dictionary of arbitrary module names that
371    map to arbitrary filenames.'''
372    def __init__(self, modules):
373        self.modules = modules
374        self.installed = set()
375
376    def __del__(self):
377        self.unload()
378
379    def unload(self):
380        import sys
381        for module in self.installed:
382            del sys.modules[module]
383        self.installed = set()
384
385    def find_module(self, fullname, path):
386        if fullname == 'm5.defines':
387            return self
388
389        if fullname == 'm5.objects':
390            return self
391
392        if fullname.startswith('m5.internal'):
393            return None
394
395        source = self.modules.get(fullname, None)
396        if source is not None and fullname.startswith('m5.objects'):
397            return self
398
399        return None
400
401    def load_module(self, fullname):
402        mod = imp.new_module(fullname)
403        sys.modules[fullname] = mod
404        self.installed.add(fullname)
405
406        mod.__loader__ = self
407        if fullname == 'm5.objects':
408            mod.__path__ = fullname.split('.')
409            return mod
410
411        if fullname == 'm5.defines':
412            mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
413            return mod
414
415        source = self.modules[fullname]
416        if source.modname == '__init__':
417            mod.__path__ = source.modpath
418        mod.__file__ = source.abspath
419
420        exec file(source.abspath, 'r') in mod.__dict__
421
422        return mod
423
424import m5.SimObject
425import m5.params
426from m5.util import code_formatter
427
428m5.SimObject.clear()
429m5.params.clear()
430
431# install the python importer so we can grab stuff from the source
432# tree itself.  We can't have SimObjects added after this point or
433# else we won't know about them for the rest of the stuff.
434importer = DictImporter(PySource.modules)
435sys.meta_path[0:0] = [ importer ]
436
437# import all sim objects so we can populate the all_objects list
438# make sure that we're working with a list, then let's sort it
439for modname in SimObject.modnames:
440    exec('from m5.objects import %s' % modname)
441
442# we need to unload all of the currently imported modules so that they
443# will be re-imported the next time the sconscript is run
444importer.unload()
445sys.meta_path.remove(importer)
446
447sim_objects = m5.SimObject.allClasses
448all_enums = m5.params.allEnums
449
450all_params = {}
451for name,obj in sorted(sim_objects.iteritems()):
452    for param in obj._params.local.values():
453        # load the ptype attribute now because it depends on the
454        # current version of SimObject.allClasses, but when scons
455        # actually uses the value, all versions of
456        # SimObject.allClasses will have been loaded
457        param.ptype
458
459        if not hasattr(param, 'swig_decl'):
460            continue
461        pname = param.ptype_str
462        if pname not in all_params:
463            all_params[pname] = param
464
465########################################################################
466#
467# calculate extra dependencies
468#
469module_depends = ["m5", "m5.SimObject", "m5.params"]
470depends = [ PySource.modules[dep].snode for dep in module_depends ]
471
472########################################################################
473#
474# Commands for the basic automatically generated python files
475#
476
477# Generate Python file containing a dict specifying the current
478# buildEnv flags.
479def makeDefinesPyFile(target, source, env):
480    build_env = source[0].get_contents()
481
482    code = code_formatter()
483    code("""
484import m5.internal
485import m5.util
486
487buildEnv = m5.util.SmartDict($build_env)
488
489compileDate = m5.internal.core.compileDate
490_globals = globals()
491for key,val in m5.internal.core.__dict__.iteritems():
492    if key.startswith('flag_'):
493        flag = key[5:]
494        _globals[flag] = val
495del _globals
496""")
497    code.write(target[0].abspath)
498
499defines_info = Value(build_env)
500# Generate a file with all of the compile options in it
501env.Command('python/m5/defines.py', defines_info,
502            MakeAction(makeDefinesPyFile, Transform("DEFINES", 0)))
503PySource('m5', 'python/m5/defines.py')
504
505# Generate python file containing info about the M5 source code
506def makeInfoPyFile(target, source, env):
507    code = code_formatter()
508    for src in source:
509        data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
510        code('$src = ${{repr(data)}}')
511    code.write(str(target[0]))
512
513# Generate a file that wraps the basic top level files
514env.Command('python/m5/info.py',
515            [ '#/AUTHORS', '#/LICENSE', '#/README', ],
516            MakeAction(makeInfoPyFile, Transform("INFO")))
517PySource('m5', 'python/m5/info.py')
518
519########################################################################
520#
521# Create all of the SimObject param headers and enum headers
522#
523
524def createSimObjectParam(target, source, env):
525    assert len(target) == 1 and len(source) == 1
526
527    name = str(source[0].get_contents())
528    obj = sim_objects[name]
529
530    code = code_formatter()
531    obj.cxx_decl(code)
532    code.write(target[0].abspath)
533
534def createSwigParam(target, source, env):
535    assert len(target) == 1 and len(source) == 1
536
537    name = str(source[0].get_contents())
538    param = all_params[name]
539
540    code = code_formatter()
541    code('%module(package="m5.internal") $0_${name}', param.file_ext)
542    param.swig_decl(code)
543    code.write(target[0].abspath)
544
545def createEnumStrings(target, source, env):
546    assert len(target) == 1 and len(source) == 1
547
548    name = str(source[0].get_contents())
549    obj = all_enums[name]
550
551    code = code_formatter()
552    obj.cxx_def(code)
553    code.write(target[0].abspath)
554
555def createEnumParam(target, source, env):
556    assert len(target) == 1 and len(source) == 1
557
558    name = str(source[0].get_contents())
559    obj = all_enums[name]
560
561    code = code_formatter()
562    obj.cxx_decl(code)
563    code.write(target[0].abspath)
564
565def createEnumSwig(target, source, env):
566    assert len(target) == 1 and len(source) == 1
567
568    name = str(source[0].get_contents())
569    obj = all_enums[name]
570
571    code = code_formatter()
572    code('''\
573%module(package="m5.internal") enum_$name
574
575%{
576#include "enums/$name.hh"
577%}
578
579%include "enums/$name.hh"
580''')
581    code.write(target[0].abspath)
582
583# Generate all of the SimObject param struct header files
584params_hh_files = []
585for name,simobj in sorted(sim_objects.iteritems()):
586    py_source = PySource.modules[simobj.__module__]
587    extra_deps = [ py_source.tnode ]
588
589    hh_file = File('params/%s.hh' % name)
590    params_hh_files.append(hh_file)
591    env.Command(hh_file, Value(name),
592                MakeAction(createSimObjectParam, Transform("SO PARAM")))
593    env.Depends(hh_file, depends + extra_deps)
594
595# Generate any parameter header files needed
596params_i_files = []
597for name,param in all_params.iteritems():
598    i_file = File('python/m5/internal/%s_%s.i' % (param.file_ext, name))
599    params_i_files.append(i_file)
600    env.Command(i_file, Value(name),
601                MakeAction(createSwigParam, Transform("SW PARAM")))
602    env.Depends(i_file, depends)
603    SwigSource('m5.internal', i_file)
604
605# Generate all enum header files
606for name,enum in sorted(all_enums.iteritems()):
607    py_source = PySource.modules[enum.__module__]
608    extra_deps = [ py_source.tnode ]
609
610    cc_file = File('enums/%s.cc' % name)
611    env.Command(cc_file, Value(name),
612                MakeAction(createEnumStrings, Transform("ENUM STR")))
613    env.Depends(cc_file, depends + extra_deps)
614    Source(cc_file)
615
616    hh_file = File('enums/%s.hh' % name)
617    env.Command(hh_file, Value(name),
618                MakeAction(createEnumParam, Transform("EN PARAM")))
619    env.Depends(hh_file, depends + extra_deps)
620
621    i_file = File('python/m5/internal/enum_%s.i' % name)
622    env.Command(i_file, Value(name),
623                MakeAction(createEnumSwig, Transform("ENUMSWIG")))
624    env.Depends(i_file, depends + extra_deps)
625    SwigSource('m5.internal', i_file)
626
627def buildParam(target, source, env):
628    name = source[0].get_contents()
629    obj = sim_objects[name]
630    class_path = obj.cxx_class.split('::')
631    classname = class_path[-1]
632    namespaces = class_path[:-1]
633    params = obj._params.local.values()
634
635    code = code_formatter()
636
637    code('%module(package="m5.internal") param_$name')
638    code()
639    code('%{')
640    code('#include "params/$obj.hh"')
641    for param in params:
642        param.cxx_predecls(code)
643    code('%}')
644    code()
645
646    for param in params:
647        param.swig_predecls(code)
648
649    code()
650    if obj._base:
651        code('%import "python/m5/internal/param_${{obj._base}}.i"')
652    code()
653    obj.swig_objdecls(code)
654    code()
655
656    code('%include "params/$obj.hh"')
657
658    code.write(target[0].abspath)
659
660for name in sim_objects.iterkeys():
661    params_file = File('python/m5/internal/param_%s.i' % name)
662    env.Command(params_file, Value(name),
663                MakeAction(buildParam, Transform("BLDPARAM")))
664    env.Depends(params_file, depends)
665    SwigSource('m5.internal', params_file)
666
667# Generate the main swig init file
668def makeEmbeddedSwigInit(target, source, env):
669    code = code_formatter()
670    module = source[0].get_contents()
671    code('''\
672#include "sim/init.hh"
673
674extern "C" {
675    void init_${module}();
676}
677
678EmbeddedSwig embed_swig_${module}(init_${module});
679''')
680    code.write(str(target[0]))
681    
682# Build all swig modules
683for swig in SwigSource.all:
684    env.Command([swig.cc_source.tnode, swig.py_source.tnode], swig.tnode,
685                MakeAction('$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} '
686                '-o ${TARGETS[0]} $SOURCES', Transform("SWIG")))
687    cc_file = str(swig.tnode)
688    init_file = '%s/init_%s.cc' % (dirname(cc_file), basename(cc_file))
689    env.Command(init_file, Value(swig.module),
690                MakeAction(makeEmbeddedSwigInit, Transform("EMBED SW")))
691    Source(init_file, **swig.guards)
692
693#
694# Handle debug flags
695#
696def makeDebugFlagCC(target, source, env):
697    assert(len(target) == 1 and len(source) == 1)
698
699    val = eval(source[0].get_contents())
700    name, compound, desc = val
701    compound = list(sorted(compound))
702
703    code = code_formatter()
704
705    # file header
706    code('''
707/*
708 * DO NOT EDIT THIS FILE! Automatically generated
709 */
710
711#include "base/debug.hh"
712''')
713
714    for flag in compound:
715        code('#include "debug/$flag.hh"')
716    code()
717    code('namespace Debug {')
718    code()
719
720    if not compound:
721        code('SimpleFlag $name("$name", "$desc");')
722    else:
723        code('CompoundFlag $name("$name", "$desc",')
724        code.indent()
725        last = len(compound) - 1
726        for i,flag in enumerate(compound):
727            if i != last:
728                code('$flag,')
729            else:
730                code('$flag);')
731        code.dedent()
732
733    code()
734    code('} // namespace Debug')
735
736    code.write(str(target[0]))
737
738def makeDebugFlagHH(target, source, env):
739    assert(len(target) == 1 and len(source) == 1)
740
741    val = eval(source[0].get_contents())
742    name, compound, desc = val
743
744    code = code_formatter()
745
746    # file header boilerplate
747    code('''\
748/*
749 * DO NOT EDIT THIS FILE!
750 *
751 * Automatically generated by SCons
752 */
753
754#ifndef __DEBUG_${name}_HH__
755#define __DEBUG_${name}_HH__
756
757namespace Debug {
758''')
759
760    if compound:
761        code('class CompoundFlag;')
762    code('class SimpleFlag;')
763
764    if compound:
765        code('extern CompoundFlag $name;')
766        for flag in compound:
767            code('extern SimpleFlag $flag;')
768    else:
769        code('extern SimpleFlag $name;')
770
771    code('''
772}
773
774#endif // __DEBUG_${name}_HH__
775''')
776
777    code.write(str(target[0]))
778
779for name,flag in sorted(debug_flags.iteritems()):
780    n, compound, desc = flag
781    assert n == name
782
783    env.Command('debug/%s.hh' % name, Value(flag),
784                MakeAction(makeDebugFlagHH, Transform("TRACING", 0)))
785    env.Command('debug/%s.cc' % name, Value(flag),
786                MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
787    Source('debug/%s.cc' % name)
788
789# Embed python files.  All .py files that have been indicated by a
790# PySource() call in a SConscript need to be embedded into the M5
791# library.  To do that, we compile the file to byte code, marshal the
792# byte code, compress it, and then generate a c++ file that
793# inserts the result into an array.
794def embedPyFile(target, source, env):
795    def c_str(string):
796        if string is None:
797            return "0"
798        return '"%s"' % string
799
800    '''Action function to compile a .py into a code object, marshal
801    it, compress it, and stick it into an asm file so the code appears
802    as just bytes with a label in the data section'''
803
804    src = file(str(source[0]), 'r').read()
805
806    pysource = PySource.tnodes[source[0]]
807    compiled = compile(src, pysource.abspath, 'exec')
808    marshalled = marshal.dumps(compiled)
809    compressed = zlib.compress(marshalled)
810    data = compressed
811    sym = pysource.symname
812
813    code = code_formatter()
814    code('''\
815#include "sim/init.hh"
816
817namespace {
818
819const char data_${sym}[] = {
820''')
821    code.indent()
822    step = 16
823    for i in xrange(0, len(data), step):
824        x = array.array('B', data[i:i+step])
825        code(''.join('%d,' % d for d in x))
826    code.dedent()
827    
828    code('''};
829
830EmbeddedPython embedded_${sym}(
831    ${{c_str(pysource.arcname)}},
832    ${{c_str(pysource.abspath)}},
833    ${{c_str(pysource.modpath)}},
834    data_${sym},
835    ${{len(data)}},
836    ${{len(marshalled)}});
837
838} // anonymous namespace
839''')
840    code.write(str(target[0]))
841
842for source in PySource.all:
843    env.Command(source.cpp, source.tnode, 
844                MakeAction(embedPyFile, Transform("EMBED PY")))
845    Source(source.cpp)
846
847########################################################################
848#
849# Define binaries.  Each different build type (debug, opt, etc.) gets
850# a slightly different build environment.
851#
852
853# List of constructed environments to pass back to SConstruct
854envList = []
855
856date_source = Source('base/date.cc', skip_lib=True)
857
858# Function to create a new build environment as clone of current
859# environment 'env' with modified object suffix and optional stripped
860# binary.  Additional keyword arguments are appended to corresponding
861# build environment vars.
862def makeEnv(label, objsfx, strip = False, **kwargs):
863    # SCons doesn't know to append a library suffix when there is a '.' in the
864    # name.  Use '_' instead.
865    libname = 'm5_' + label
866    exename = 'm5.' + label
867
868    new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
869    new_env.Label = label
870    new_env.Append(**kwargs)
871
872    swig_env = new_env.Clone()
873    swig_env.Append(CCFLAGS='-Werror')
874    if env['GCC']:
875        swig_env.Append(CCFLAGS='-Wno-uninitialized')
876        swig_env.Append(CCFLAGS='-Wno-sign-compare')
877        swig_env.Append(CCFLAGS='-Wno-parentheses')
878
879    werror_env = new_env.Clone()
880    werror_env.Append(CCFLAGS='-Werror')
881
882    def make_obj(source, static, extra_deps = None):
883        '''This function adds the specified source to the correct
884        build environment, and returns the corresponding SCons Object
885        nodes'''
886
887        if source.swig:
888            env = swig_env
889        elif source.Werror:
890            env = werror_env
891        else:
892            env = new_env
893
894        if static:
895            obj = env.StaticObject(source.tnode)
896        else:
897            obj = env.SharedObject(source.tnode)
898
899        if extra_deps:
900            env.Depends(obj, extra_deps)
901
902        return obj
903
904    sources = Source.get(main=False, skip_lib=False)
905    static_objs = [ make_obj(s, True) for s in sources ]
906    shared_objs = [ make_obj(s, False) for s in sources ]
907
908    static_date = make_obj(date_source, static=True, extra_deps=static_objs)
909    static_objs.append(static_date)
910    
911    shared_date = make_obj(date_source, static=False, extra_deps=shared_objs)
912    shared_objs.append(shared_date)
913
914    # First make a library of everything but main() so other programs can
915    # link against m5.
916    static_lib = new_env.StaticLibrary(libname, static_objs)
917    shared_lib = new_env.SharedLibrary(libname, shared_objs)
918
919    # Now link a stub with main() and the static library.
920    main_objs = [ make_obj(s, True) for s in Source.get(main=True) ]
921
922    for test in UnitTest.all:
923        flags = { test.target : True }
924        test_sources = Source.get(**flags)
925        test_objs = [ make_obj(s, static=True) for s in test_sources ]
926        testname = "unittest/%s.%s" % (test.target, label)
927        new_env.Program(testname, main_objs + test_objs + static_objs)
928
929    progname = exename
930    if strip:
931        progname += '.unstripped'
932
933    targets = new_env.Program(progname, main_objs + static_objs)
934
935    if strip:
936        if sys.platform == 'sunos5':
937            cmd = 'cp $SOURCE $TARGET; strip $TARGET'
938        else:
939            cmd = 'strip $SOURCE -o $TARGET'
940        targets = new_env.Command(exename, progname,
941                    MakeAction(cmd, Transform("STRIP")))
942            
943    new_env.M5Binary = targets[0]
944    envList.append(new_env)
945
946# Debug binary
947ccflags = {}
948if env['GCC']:
949    if sys.platform == 'sunos5':
950        ccflags['debug'] = '-gstabs+'
951    else:
952        ccflags['debug'] = '-ggdb3'
953    ccflags['opt'] = '-g -O3'
954    ccflags['fast'] = '-O3'
955    ccflags['prof'] = '-O3 -g -pg'
956elif env['SUNCC']:
957    ccflags['debug'] = '-g0'
958    ccflags['opt'] = '-g -O'
959    ccflags['fast'] = '-fast'
960    ccflags['prof'] = '-fast -g -pg'
961elif env['ICC']:
962    ccflags['debug'] = '-g -O0'
963    ccflags['opt'] = '-g -O'
964    ccflags['fast'] = '-fast'
965    ccflags['prof'] = '-fast -g -pg'
966else:
967    print 'Unknown compiler, please fix compiler options'
968    Exit(1)
969
970makeEnv('debug', '.do',
971        CCFLAGS = Split(ccflags['debug']),
972        CPPDEFINES = ['DEBUG', 'TRACING_ON=1'])
973
974# Optimized binary
975makeEnv('opt', '.o',
976        CCFLAGS = Split(ccflags['opt']),
977        CPPDEFINES = ['TRACING_ON=1'])
978
979# "Fast" binary
980makeEnv('fast', '.fo', strip = True,
981        CCFLAGS = Split(ccflags['fast']),
982        CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'])
983
984# Profiled binary
985makeEnv('prof', '.po',
986        CCFLAGS = Split(ccflags['prof']),
987        CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
988        LINKFLAGS = '-pg')
989
990Return('envList')
991