SConscript revision 11985
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 subprocess
38import sys
39import zlib
40
41from os.path import basename, dirname, exists, isdir, isfile, join as joinpath
42
43import SCons
44
45# This file defines how to build a particular configuration of gem5
46# based on variable settings in the 'env' build environment.
47
48Import('*')
49
50# Children need to see the environment
51Export('env')
52
53build_env = [(opt, env[opt]) for opt in export_vars]
54
55from m5.util import code_formatter, compareVersions
56
57########################################################################
58# Code for adding source files of various types
59#
60# When specifying a source file of some type, a set of guards can be
61# specified for that file.  When get() is used to find the files, if
62# get specifies a set of filters, only files that match those filters
63# will be accepted (unspecified filters on files are assumed to be
64# false).  Current filters are:
65#     main -- specifies the gem5 main() function
66#     skip_lib -- do not put this file into the gem5 library
67#     skip_no_python -- do not put this file into a no_python library
68#       as it embeds compiled Python
69#     <unittest> -- unit tests use filters based on the unit test name
70#
71# A parent can now be specified for a source file and default filter
72# values will be retrieved recursively from parents (children override
73# parents).
74#
75def guarded_source_iterator(sources, **guards):
76    '''Iterate over a set of sources, gated by a set of guards.'''
77    for src in sources:
78        for flag,value in guards.iteritems():
79            # if the flag is found and has a different value, skip
80            # this file
81            if src.all_guards.get(flag, False) != value:
82                break
83        else:
84            yield src
85
86class SourceMeta(type):
87    '''Meta class for source files that keeps track of all files of a
88    particular type and has a get function for finding all functions
89    of a certain type that match a set of guards'''
90    def __init__(cls, name, bases, dict):
91        super(SourceMeta, cls).__init__(name, bases, dict)
92        cls.all = []
93
94    def get(cls, **guards):
95        '''Find all files that match the specified guards.  If a source
96        file does not specify a flag, the default is False'''
97        for s in guarded_source_iterator(cls.all, **guards):
98            yield s
99
100class SourceFile(object):
101    '''Base object that encapsulates the notion of a source file.
102    This includes, the source node, target node, various manipulations
103    of those.  A source file also specifies a set of guards which
104    describing which builds the source file applies to.  A parent can
105    also be specified to get default guards from'''
106    __metaclass__ = SourceMeta
107    def __init__(self, source, parent=None, **guards):
108        self.guards = guards
109        self.parent = parent
110
111        tnode = source
112        if not isinstance(source, SCons.Node.FS.File):
113            tnode = File(source)
114
115        self.tnode = tnode
116        self.snode = tnode.srcnode()
117
118        for base in type(self).__mro__:
119            if issubclass(base, SourceFile):
120                base.all.append(self)
121
122    @property
123    def filename(self):
124        return str(self.tnode)
125
126    @property
127    def dirname(self):
128        return dirname(self.filename)
129
130    @property
131    def basename(self):
132        return basename(self.filename)
133
134    @property
135    def extname(self):
136        index = self.basename.rfind('.')
137        if index <= 0:
138            # dot files aren't extensions
139            return self.basename, None
140
141        return self.basename[:index], self.basename[index+1:]
142
143    @property
144    def all_guards(self):
145        '''find all guards for this object getting default values
146        recursively from its parents'''
147        guards = {}
148        if self.parent:
149            guards.update(self.parent.guards)
150        guards.update(self.guards)
151        return guards
152
153    def __lt__(self, other): return self.filename < other.filename
154    def __le__(self, other): return self.filename <= other.filename
155    def __gt__(self, other): return self.filename > other.filename
156    def __ge__(self, other): return self.filename >= other.filename
157    def __eq__(self, other): return self.filename == other.filename
158    def __ne__(self, other): return self.filename != other.filename
159
160    @staticmethod
161    def done():
162        def disabled(cls, name, *ignored):
163            raise RuntimeError("Additional SourceFile '%s'" % name,\
164                  "declared, but targets deps are already fixed.")
165        SourceFile.__init__ = disabled
166
167
168class Source(SourceFile):
169    current_group = None
170    source_groups = { None : [] }
171
172    @classmethod
173    def set_group(cls, group):
174        if not group in Source.source_groups:
175            Source.source_groups[group] = []
176        Source.current_group = group
177
178    '''Add a c/c++ source file to the build'''
179    def __init__(self, source, Werror=True, swig=False, **guards):
180        '''specify the source file, and any guards'''
181        super(Source, self).__init__(source, **guards)
182
183        self.Werror = Werror
184        self.swig = swig
185
186        Source.source_groups[Source.current_group].append(self)
187
188class PySource(SourceFile):
189    '''Add a python source file to the named package'''
190    invalid_sym_char = re.compile('[^A-z0-9_]')
191    modules = {}
192    tnodes = {}
193    symnames = {}
194
195    def __init__(self, package, source, **guards):
196        '''specify the python package, the source file, and any guards'''
197        super(PySource, self).__init__(source, **guards)
198
199        modname,ext = self.extname
200        assert ext == 'py'
201
202        if package:
203            path = package.split('.')
204        else:
205            path = []
206
207        modpath = path[:]
208        if modname != '__init__':
209            modpath += [ modname ]
210        modpath = '.'.join(modpath)
211
212        arcpath = path + [ self.basename ]
213        abspath = self.snode.abspath
214        if not exists(abspath):
215            abspath = self.tnode.abspath
216
217        self.package = package
218        self.modname = modname
219        self.modpath = modpath
220        self.arcname = joinpath(*arcpath)
221        self.abspath = abspath
222        self.compiled = File(self.filename + 'c')
223        self.cpp = File(self.filename + '.cc')
224        self.symname = PySource.invalid_sym_char.sub('_', modpath)
225
226        PySource.modules[modpath] = self
227        PySource.tnodes[self.tnode] = self
228        PySource.symnames[self.symname] = self
229
230class SimObject(PySource):
231    '''Add a SimObject python file as a python source object and add
232    it to a list of sim object modules'''
233
234    fixed = False
235    modnames = []
236
237    def __init__(self, source, **guards):
238        '''Specify the source file and any guards (automatically in
239        the m5.objects package)'''
240        super(SimObject, self).__init__('m5.objects', source, **guards)
241        if self.fixed:
242            raise AttributeError, "Too late to call SimObject now."
243
244        bisect.insort_right(SimObject.modnames, self.modname)
245
246class SwigSource(SourceFile):
247    '''Add a swig file to build'''
248
249    def __init__(self, package, source, **guards):
250        '''Specify the python package, the source file, and any guards'''
251        super(SwigSource, self).__init__(source, skip_no_python=True, **guards)
252
253        modname,ext = self.extname
254        assert ext == 'i'
255
256        self.package = package
257        self.module = modname
258        cc_file = joinpath(self.dirname, modname + '_wrap.cc')
259        py_file = joinpath(self.dirname, modname + '.py')
260
261        self.cc_source = Source(cc_file, swig=True, parent=self, **guards)
262        self.py_source = PySource(package, py_file, parent=self, **guards)
263
264class ProtoBuf(SourceFile):
265    '''Add a Protocol Buffer to build'''
266
267    def __init__(self, source, **guards):
268        '''Specify the source file, and any guards'''
269        super(ProtoBuf, self).__init__(source, **guards)
270
271        # Get the file name and the extension
272        modname,ext = self.extname
273        assert ext == 'proto'
274
275        # Currently, we stick to generating the C++ headers, so we
276        # only need to track the source and header.
277        self.cc_file = File(modname + '.pb.cc')
278        self.hh_file = File(modname + '.pb.h')
279
280class UnitTest(object):
281    '''Create a UnitTest'''
282
283    all = []
284    def __init__(self, target, *sources, **kwargs):
285        '''Specify the target name and any sources.  Sources that are
286        not SourceFiles are evalued with Source().  All files are
287        guarded with a guard of the same name as the UnitTest
288        target.'''
289
290        srcs = []
291        for src in sources:
292            if not isinstance(src, SourceFile):
293                src = Source(src, skip_lib=True)
294            src.guards[target] = True
295            srcs.append(src)
296
297        self.sources = srcs
298        self.target = target
299        self.main = kwargs.get('main', False)
300        UnitTest.all.append(self)
301
302# Children should have access
303Export('Source')
304Export('PySource')
305Export('SimObject')
306Export('SwigSource')
307Export('ProtoBuf')
308Export('UnitTest')
309
310########################################################################
311#
312# Debug Flags
313#
314debug_flags = {}
315def DebugFlag(name, desc=None):
316    if name in debug_flags:
317        raise AttributeError, "Flag %s already specified" % name
318    debug_flags[name] = (name, (), desc)
319
320def CompoundFlag(name, flags, desc=None):
321    if name in debug_flags:
322        raise AttributeError, "Flag %s already specified" % name
323
324    compound = tuple(flags)
325    debug_flags[name] = (name, compound, desc)
326
327Export('DebugFlag')
328Export('CompoundFlag')
329
330########################################################################
331#
332# Set some compiler variables
333#
334
335# Include file paths are rooted in this directory.  SCons will
336# automatically expand '.' to refer to both the source directory and
337# the corresponding build directory to pick up generated include
338# files.
339env.Append(CPPPATH=Dir('.'))
340
341for extra_dir in extras_dir_list:
342    env.Append(CPPPATH=Dir(extra_dir))
343
344# Workaround for bug in SCons version > 0.97d20071212
345# Scons bug id: 2006 gem5 Bug id: 308
346for root, dirs, files in os.walk(base_dir, topdown=True):
347    Dir(root[len(base_dir) + 1:])
348
349########################################################################
350#
351# Walk the tree and execute all SConscripts in subdirectories
352#
353
354here = Dir('.').srcnode().abspath
355for root, dirs, files in os.walk(base_dir, topdown=True):
356    if root == here:
357        # we don't want to recurse back into this SConscript
358        continue
359
360    if 'SConscript' in files:
361        build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
362        Source.set_group(build_dir)
363        SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
364
365for extra_dir in extras_dir_list:
366    prefix_len = len(dirname(extra_dir)) + 1
367
368    # Also add the corresponding build directory to pick up generated
369    # include files.
370    env.Append(CPPPATH=Dir(joinpath(env['BUILDDIR'], extra_dir[prefix_len:])))
371
372    for root, dirs, files in os.walk(extra_dir, topdown=True):
373        # if build lives in the extras directory, don't walk down it
374        if 'build' in dirs:
375            dirs.remove('build')
376
377        if 'SConscript' in files:
378            build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
379            SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
380
381for opt in export_vars:
382    env.ConfigFile(opt)
383
384def makeTheISA(source, target, env):
385    isas = [ src.get_contents() for src in source ]
386    target_isa = env['TARGET_ISA']
387    def define(isa):
388        return isa.upper() + '_ISA'
389
390    def namespace(isa):
391        return isa[0].upper() + isa[1:].lower() + 'ISA'
392
393
394    code = code_formatter()
395    code('''\
396#ifndef __CONFIG_THE_ISA_HH__
397#define __CONFIG_THE_ISA_HH__
398
399''')
400
401    # create defines for the preprocessing and compile-time determination
402    for i,isa in enumerate(isas):
403        code('#define $0 $1', define(isa), i + 1)
404    code()
405
406    # create an enum for any run-time determination of the ISA, we
407    # reuse the same name as the namespaces
408    code('enum class Arch {')
409    for i,isa in enumerate(isas):
410        if i + 1 == len(isas):
411            code('  $0 = $1', namespace(isa), define(isa))
412        else:
413            code('  $0 = $1,', namespace(isa), define(isa))
414    code('};')
415
416    code('''
417
418#define THE_ISA ${{define(target_isa)}}
419#define TheISA ${{namespace(target_isa)}}
420#define THE_ISA_STR "${{target_isa}}"
421
422#endif // __CONFIG_THE_ISA_HH__''')
423
424    code.write(str(target[0]))
425
426env.Command('config/the_isa.hh', map(Value, all_isa_list),
427            MakeAction(makeTheISA, Transform("CFG ISA", 0)))
428
429def makeTheGPUISA(source, target, env):
430    isas = [ src.get_contents() for src in source ]
431    target_gpu_isa = env['TARGET_GPU_ISA']
432    def define(isa):
433        return isa.upper() + '_ISA'
434
435    def namespace(isa):
436        return isa[0].upper() + isa[1:].lower() + 'ISA'
437
438
439    code = code_formatter()
440    code('''\
441#ifndef __CONFIG_THE_GPU_ISA_HH__
442#define __CONFIG_THE_GPU_ISA_HH__
443
444''')
445
446    # create defines for the preprocessing and compile-time determination
447    for i,isa in enumerate(isas):
448        code('#define $0 $1', define(isa), i + 1)
449    code()
450
451    # create an enum for any run-time determination of the ISA, we
452    # reuse the same name as the namespaces
453    code('enum class GPUArch {')
454    for i,isa in enumerate(isas):
455        if i + 1 == len(isas):
456            code('  $0 = $1', namespace(isa), define(isa))
457        else:
458            code('  $0 = $1,', namespace(isa), define(isa))
459    code('};')
460
461    code('''
462
463#define THE_GPU_ISA ${{define(target_gpu_isa)}}
464#define TheGpuISA ${{namespace(target_gpu_isa)}}
465#define THE_GPU_ISA_STR "${{target_gpu_isa}}"
466
467#endif // __CONFIG_THE_GPU_ISA_HH__''')
468
469    code.write(str(target[0]))
470
471env.Command('config/the_gpu_isa.hh', map(Value, all_gpu_isa_list),
472            MakeAction(makeTheGPUISA, Transform("CFG ISA", 0)))
473
474########################################################################
475#
476# Prevent any SimObjects from being added after this point, they
477# should all have been added in the SConscripts above
478#
479SimObject.fixed = True
480
481class DictImporter(object):
482    '''This importer takes a dictionary of arbitrary module names that
483    map to arbitrary filenames.'''
484    def __init__(self, modules):
485        self.modules = modules
486        self.installed = set()
487
488    def __del__(self):
489        self.unload()
490
491    def unload(self):
492        import sys
493        for module in self.installed:
494            del sys.modules[module]
495        self.installed = set()
496
497    def find_module(self, fullname, path):
498        if fullname == 'm5.defines':
499            return self
500
501        if fullname == 'm5.objects':
502            return self
503
504        if fullname.startswith('_m5'):
505            return None
506
507        source = self.modules.get(fullname, None)
508        if source is not None and fullname.startswith('m5.objects'):
509            return self
510
511        return None
512
513    def load_module(self, fullname):
514        mod = imp.new_module(fullname)
515        sys.modules[fullname] = mod
516        self.installed.add(fullname)
517
518        mod.__loader__ = self
519        if fullname == 'm5.objects':
520            mod.__path__ = fullname.split('.')
521            return mod
522
523        if fullname == 'm5.defines':
524            mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
525            return mod
526
527        source = self.modules[fullname]
528        if source.modname == '__init__':
529            mod.__path__ = source.modpath
530        mod.__file__ = source.abspath
531
532        exec file(source.abspath, 'r') in mod.__dict__
533
534        return mod
535
536import m5.SimObject
537import m5.params
538from m5.util import code_formatter
539
540m5.SimObject.clear()
541m5.params.clear()
542
543# install the python importer so we can grab stuff from the source
544# tree itself.  We can't have SimObjects added after this point or
545# else we won't know about them for the rest of the stuff.
546importer = DictImporter(PySource.modules)
547sys.meta_path[0:0] = [ importer ]
548
549# import all sim objects so we can populate the all_objects list
550# make sure that we're working with a list, then let's sort it
551for modname in SimObject.modnames:
552    exec('from m5.objects import %s' % modname)
553
554# we need to unload all of the currently imported modules so that they
555# will be re-imported the next time the sconscript is run
556importer.unload()
557sys.meta_path.remove(importer)
558
559sim_objects = m5.SimObject.allClasses
560all_enums = m5.params.allEnums
561
562if m5.SimObject.noCxxHeader:
563    print >> sys.stderr, \
564        "warning: At least one SimObject lacks a header specification. " \
565        "This can cause unexpected results in the generated SWIG " \
566        "wrappers."
567
568# Find param types that need to be explicitly wrapped with swig.
569# These will be recognized because the ParamDesc will have a
570# swig_decl() method.  Most param types are based on types that don't
571# need this, either because they're based on native types (like Int)
572# or because they're SimObjects (which get swigged independently).
573# For now the only things handled here are VectorParam types.
574params_to_swig = {}
575for name,obj in sorted(sim_objects.iteritems()):
576    for param in obj._params.local.values():
577        # load the ptype attribute now because it depends on the
578        # current version of SimObject.allClasses, but when scons
579        # actually uses the value, all versions of
580        # SimObject.allClasses will have been loaded
581        param.ptype
582
583        if not hasattr(param, 'swig_decl'):
584            continue
585        pname = param.ptype_str
586        if pname not in params_to_swig:
587            params_to_swig[pname] = param
588
589########################################################################
590#
591# calculate extra dependencies
592#
593module_depends = ["m5", "m5.SimObject", "m5.params"]
594depends = [ PySource.modules[dep].snode for dep in module_depends ]
595depends.sort(key = lambda x: x.name)
596
597########################################################################
598#
599# Commands for the basic automatically generated python files
600#
601
602# Generate Python file containing a dict specifying the current
603# buildEnv flags.
604def makeDefinesPyFile(target, source, env):
605    build_env = source[0].get_contents()
606
607    code = code_formatter()
608    code("""
609import _m5.core
610import m5.util
611
612buildEnv = m5.util.SmartDict($build_env)
613
614compileDate = _m5.core.compileDate
615_globals = globals()
616for key,val in _m5.core.__dict__.iteritems():
617    if key.startswith('flag_'):
618        flag = key[5:]
619        _globals[flag] = val
620del _globals
621""")
622    code.write(target[0].abspath)
623
624defines_info = Value(build_env)
625# Generate a file with all of the compile options in it
626env.Command('python/m5/defines.py', defines_info,
627            MakeAction(makeDefinesPyFile, Transform("DEFINES", 0)))
628PySource('m5', 'python/m5/defines.py')
629
630# Generate python file containing info about the M5 source code
631def makeInfoPyFile(target, source, env):
632    code = code_formatter()
633    for src in source:
634        data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
635        code('$src = ${{repr(data)}}')
636    code.write(str(target[0]))
637
638# Generate a file that wraps the basic top level files
639env.Command('python/m5/info.py',
640            [ '#/COPYING', '#/LICENSE', '#/README', ],
641            MakeAction(makeInfoPyFile, Transform("INFO")))
642PySource('m5', 'python/m5/info.py')
643
644########################################################################
645#
646# Create all of the SimObject param headers and enum headers
647#
648
649def createSimObjectParamStruct(target, source, env):
650    assert len(target) == 1 and len(source) == 1
651
652    name = str(source[0].get_contents())
653    obj = sim_objects[name]
654
655    code = code_formatter()
656    obj.cxx_param_decl(code)
657    code.write(target[0].abspath)
658
659def createSimObjectCxxConfig(is_header):
660    def body(target, source, env):
661        assert len(target) == 1 and len(source) == 1
662
663        name = str(source[0].get_contents())
664        obj = sim_objects[name]
665
666        code = code_formatter()
667        obj.cxx_config_param_file(code, is_header)
668        code.write(target[0].abspath)
669    return body
670
671def createParamSwigWrapper(target, source, env):
672    assert len(target) == 1 and len(source) == 1
673
674    name = str(source[0].get_contents())
675    param = params_to_swig[name]
676
677    code = code_formatter()
678    param.swig_decl(code)
679    code.write(target[0].abspath)
680
681def createEnumStrings(target, source, env):
682    assert len(target) == 1 and len(source) == 1
683
684    name = str(source[0].get_contents())
685    obj = all_enums[name]
686
687    code = code_formatter()
688    obj.cxx_def(code)
689    code.write(target[0].abspath)
690
691def createEnumDecls(target, source, env):
692    assert len(target) == 1 and len(source) == 1
693
694    name = str(source[0].get_contents())
695    obj = all_enums[name]
696
697    code = code_formatter()
698    obj.cxx_decl(code)
699    code.write(target[0].abspath)
700
701def createEnumSwigWrapper(target, source, env):
702    assert len(target) == 1 and len(source) == 1
703
704    name = str(source[0].get_contents())
705    obj = all_enums[name]
706
707    code = code_formatter()
708    obj.swig_decl(code)
709    code.write(target[0].abspath)
710
711def createSimObjectSwigWrapper(target, source, env):
712    name = source[0].get_contents()
713    obj = sim_objects[name]
714
715    code = code_formatter()
716    obj.swig_decl(code)
717    code.write(target[0].abspath)
718
719# dummy target for generated code
720# we start out with all the Source files so they get copied to build/*/ also.
721SWIG = env.Dummy('swig', [s.tnode for s in Source.get()])
722
723# Generate all of the SimObject param C++ struct header files
724params_hh_files = []
725for name,simobj in sorted(sim_objects.iteritems()):
726    py_source = PySource.modules[simobj.__module__]
727    extra_deps = [ py_source.tnode ]
728
729    hh_file = File('params/%s.hh' % name)
730    params_hh_files.append(hh_file)
731    env.Command(hh_file, Value(name),
732                MakeAction(createSimObjectParamStruct, Transform("SO PARAM")))
733    env.Depends(hh_file, depends + extra_deps)
734    env.Depends(SWIG, hh_file)
735
736# C++ parameter description files
737if GetOption('with_cxx_config'):
738    for name,simobj in sorted(sim_objects.iteritems()):
739        py_source = PySource.modules[simobj.__module__]
740        extra_deps = [ py_source.tnode ]
741
742        cxx_config_hh_file = File('cxx_config/%s.hh' % name)
743        cxx_config_cc_file = File('cxx_config/%s.cc' % name)
744        env.Command(cxx_config_hh_file, Value(name),
745                    MakeAction(createSimObjectCxxConfig(True),
746                    Transform("CXXCPRHH")))
747        env.Command(cxx_config_cc_file, Value(name),
748                    MakeAction(createSimObjectCxxConfig(False),
749                    Transform("CXXCPRCC")))
750        env.Depends(cxx_config_hh_file, depends + extra_deps +
751                    [File('params/%s.hh' % name), File('sim/cxx_config.hh')])
752        env.Depends(cxx_config_cc_file, depends + extra_deps +
753                    [cxx_config_hh_file])
754        Source(cxx_config_cc_file)
755
756    cxx_config_init_cc_file = File('cxx_config/init.cc')
757
758    def createCxxConfigInitCC(target, source, env):
759        assert len(target) == 1 and len(source) == 1
760
761        code = code_formatter()
762
763        for name,simobj in sorted(sim_objects.iteritems()):
764            if not hasattr(simobj, 'abstract') or not simobj.abstract:
765                code('#include "cxx_config/${name}.hh"')
766        code()
767        code('void cxxConfigInit()')
768        code('{')
769        code.indent()
770        for name,simobj in sorted(sim_objects.iteritems()):
771            not_abstract = not hasattr(simobj, 'abstract') or \
772                not simobj.abstract
773            if not_abstract and 'type' in simobj.__dict__:
774                code('cxx_config_directory["${name}"] = '
775                     '${name}CxxConfigParams::makeDirectoryEntry();')
776        code.dedent()
777        code('}')
778        code.write(target[0].abspath)
779
780    py_source = PySource.modules[simobj.__module__]
781    extra_deps = [ py_source.tnode ]
782    env.Command(cxx_config_init_cc_file, Value(name),
783        MakeAction(createCxxConfigInitCC, Transform("CXXCINIT")))
784    cxx_param_hh_files = ["cxx_config/%s.hh" % simobj
785        for name,simobj in sorted(sim_objects.iteritems())
786        if not hasattr(simobj, 'abstract') or not simobj.abstract]
787    Depends(cxx_config_init_cc_file, cxx_param_hh_files +
788            [File('sim/cxx_config.hh')])
789    Source(cxx_config_init_cc_file)
790
791# Generate any needed param SWIG wrapper files
792params_i_files = []
793for name,param in sorted(params_to_swig.iteritems()):
794    i_file = File('python/_m5/%s.i' % (param.swig_module_name()))
795    params_i_files.append(i_file)
796    env.Command(i_file, Value(name),
797                MakeAction(createParamSwigWrapper, Transform("SW PARAM")))
798    env.Depends(i_file, depends)
799    env.Depends(SWIG, i_file)
800    SwigSource('_m5', i_file)
801
802# Generate all enum header files
803for name,enum in sorted(all_enums.iteritems()):
804    py_source = PySource.modules[enum.__module__]
805    extra_deps = [ py_source.tnode ]
806
807    cc_file = File('enums/%s.cc' % name)
808    env.Command(cc_file, Value(name),
809                MakeAction(createEnumStrings, Transform("ENUM STR")))
810    env.Depends(cc_file, depends + extra_deps)
811    env.Depends(SWIG, cc_file)
812    Source(cc_file)
813
814    hh_file = File('enums/%s.hh' % name)
815    env.Command(hh_file, Value(name),
816                MakeAction(createEnumDecls, Transform("ENUMDECL")))
817    env.Depends(hh_file, depends + extra_deps)
818    env.Depends(SWIG, hh_file)
819
820    i_file = File('python/_m5/enum_%s.i' % name)
821    env.Command(i_file, Value(name),
822                MakeAction(createEnumSwigWrapper, Transform("ENUMSWIG")))
823    env.Depends(i_file, depends + extra_deps)
824    env.Depends(SWIG, i_file)
825    SwigSource('_m5', i_file)
826
827# Generate SimObject SWIG wrapper files
828for name,simobj in sorted(sim_objects.iteritems()):
829    py_source = PySource.modules[simobj.__module__]
830    extra_deps = [ py_source.tnode ]
831    i_file = File('python/_m5/param_%s.i' % name)
832    env.Command(i_file, Value(name),
833                MakeAction(createSimObjectSwigWrapper, Transform("SO SWIG")))
834    env.Depends(i_file, depends + extra_deps)
835    SwigSource('_m5', i_file)
836
837# Generate the main swig init file
838def makeEmbeddedSwigInit(package):
839    def body(target, source, env):
840        assert len(target) == 1 and len(source) == 1
841
842        code = code_formatter()
843        module = source[0].get_contents()
844        # Provide the full context so that the swig-generated call to
845        # Py_InitModule ends up placing the embedded module in the
846        # right package.
847        context = str(package) + "._" + str(module)
848        code('''\
849        #include "sim/init.hh"
850
851        extern "C" {
852            void init_${module}();
853        }
854
855        EmbeddedSwig embed_swig_${module}(init_${module}, "${context}");
856        ''')
857        code.write(str(target[0]))
858    return body
859
860# Build all swig modules
861for swig in SwigSource.all:
862    env.Command([swig.cc_source.tnode, swig.py_source.tnode], swig.tnode,
863                MakeAction('$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} '
864                '-o ${TARGETS[0]} $SOURCES', Transform("SWIG")))
865    cc_file = str(swig.tnode)
866    init_file = '%s/%s_init.cc' % (dirname(cc_file), basename(cc_file))
867    env.Command(init_file, Value(swig.module),
868                MakeAction(makeEmbeddedSwigInit(swig.package),
869                           Transform("EMBED SW")))
870    env.Depends(SWIG, init_file)
871    Source(init_file, **swig.guards)
872
873# Build all protocol buffers if we have got protoc and protobuf available
874if env['HAVE_PROTOBUF']:
875    for proto in ProtoBuf.all:
876        # Use both the source and header as the target, and the .proto
877        # file as the source. When executing the protoc compiler, also
878        # specify the proto_path to avoid having the generated files
879        # include the path.
880        env.Command([proto.cc_file, proto.hh_file], proto.tnode,
881                    MakeAction('$PROTOC --cpp_out ${TARGET.dir} '
882                               '--proto_path ${SOURCE.dir} $SOURCE',
883                               Transform("PROTOC")))
884
885        env.Depends(SWIG, [proto.cc_file, proto.hh_file])
886        # Add the C++ source file
887        Source(proto.cc_file, **proto.guards)
888elif ProtoBuf.all:
889    print 'Got protobuf to build, but lacks support!'
890    Exit(1)
891
892#
893# Handle debug flags
894#
895def makeDebugFlagCC(target, source, env):
896    assert(len(target) == 1 and len(source) == 1)
897
898    code = code_formatter()
899
900    # delay definition of CompoundFlags until after all the definition
901    # of all constituent SimpleFlags
902    comp_code = code_formatter()
903
904    # file header
905    code('''
906/*
907 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
908 */
909
910#include "base/debug.hh"
911
912namespace Debug {
913
914''')
915
916    for name, flag in sorted(source[0].read().iteritems()):
917        n, compound, desc = flag
918        assert n == name
919
920        if not compound:
921            code('SimpleFlag $name("$name", "$desc");')
922        else:
923            comp_code('CompoundFlag $name("$name", "$desc",')
924            comp_code.indent()
925            last = len(compound) - 1
926            for i,flag in enumerate(compound):
927                if i != last:
928                    comp_code('&$flag,')
929                else:
930                    comp_code('&$flag);')
931            comp_code.dedent()
932
933    code.append(comp_code)
934    code()
935    code('} // namespace Debug')
936
937    code.write(str(target[0]))
938
939def makeDebugFlagHH(target, source, env):
940    assert(len(target) == 1 and len(source) == 1)
941
942    val = eval(source[0].get_contents())
943    name, compound, desc = val
944
945    code = code_formatter()
946
947    # file header boilerplate
948    code('''\
949/*
950 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
951 */
952
953#ifndef __DEBUG_${name}_HH__
954#define __DEBUG_${name}_HH__
955
956namespace Debug {
957''')
958
959    if compound:
960        code('class CompoundFlag;')
961    code('class SimpleFlag;')
962
963    if compound:
964        code('extern CompoundFlag $name;')
965        for flag in compound:
966            code('extern SimpleFlag $flag;')
967    else:
968        code('extern SimpleFlag $name;')
969
970    code('''
971}
972
973#endif // __DEBUG_${name}_HH__
974''')
975
976    code.write(str(target[0]))
977
978for name,flag in sorted(debug_flags.iteritems()):
979    n, compound, desc = flag
980    assert n == name
981
982    hh_file = 'debug/%s.hh' % name
983    env.Command(hh_file, Value(flag),
984                MakeAction(makeDebugFlagHH, Transform("TRACING", 0)))
985    env.Depends(SWIG, hh_file)
986
987env.Command('debug/flags.cc', Value(debug_flags),
988            MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
989env.Depends(SWIG, 'debug/flags.cc')
990Source('debug/flags.cc')
991
992# version tags
993tags = \
994env.Command('sim/tags.cc', None,
995            MakeAction('util/cpt_upgrader.py --get-cc-file > $TARGET',
996                       Transform("VER TAGS")))
997env.AlwaysBuild(tags)
998
999# Embed python files.  All .py files that have been indicated by a
1000# PySource() call in a SConscript need to be embedded into the M5
1001# library.  To do that, we compile the file to byte code, marshal the
1002# byte code, compress it, and then generate a c++ file that
1003# inserts the result into an array.
1004def embedPyFile(target, source, env):
1005    def c_str(string):
1006        if string is None:
1007            return "0"
1008        return '"%s"' % string
1009
1010    '''Action function to compile a .py into a code object, marshal
1011    it, compress it, and stick it into an asm file so the code appears
1012    as just bytes with a label in the data section'''
1013
1014    src = file(str(source[0]), 'r').read()
1015
1016    pysource = PySource.tnodes[source[0]]
1017    compiled = compile(src, pysource.abspath, 'exec')
1018    marshalled = marshal.dumps(compiled)
1019    compressed = zlib.compress(marshalled)
1020    data = compressed
1021    sym = pysource.symname
1022
1023    code = code_formatter()
1024    code('''\
1025#include "sim/init.hh"
1026
1027namespace {
1028
1029const uint8_t data_${sym}[] = {
1030''')
1031    code.indent()
1032    step = 16
1033    for i in xrange(0, len(data), step):
1034        x = array.array('B', data[i:i+step])
1035        code(''.join('%d,' % d for d in x))
1036    code.dedent()
1037
1038    code('''};
1039
1040EmbeddedPython embedded_${sym}(
1041    ${{c_str(pysource.arcname)}},
1042    ${{c_str(pysource.abspath)}},
1043    ${{c_str(pysource.modpath)}},
1044    data_${sym},
1045    ${{len(data)}},
1046    ${{len(marshalled)}});
1047
1048} // anonymous namespace
1049''')
1050    code.write(str(target[0]))
1051
1052for source in PySource.all:
1053    env.Command(source.cpp, source.tnode,
1054                MakeAction(embedPyFile, Transform("EMBED PY")))
1055    env.Depends(SWIG, source.cpp)
1056    Source(source.cpp, skip_no_python=True)
1057
1058########################################################################
1059#
1060# Define binaries.  Each different build type (debug, opt, etc.) gets
1061# a slightly different build environment.
1062#
1063
1064# List of constructed environments to pass back to SConstruct
1065date_source = Source('base/date.cc', skip_lib=True)
1066
1067# Capture this directory for the closure makeEnv, otherwise when it is
1068# called, it won't know what directory it should use.
1069variant_dir = Dir('.').path
1070def variant(*path):
1071    return os.path.join(variant_dir, *path)
1072def variantd(*path):
1073    return variant(*path)+'/'
1074
1075# Function to create a new build environment as clone of current
1076# environment 'env' with modified object suffix and optional stripped
1077# binary.  Additional keyword arguments are appended to corresponding
1078# build environment vars.
1079def makeEnv(env, label, objsfx, strip = False, **kwargs):
1080    # SCons doesn't know to append a library suffix when there is a '.' in the
1081    # name.  Use '_' instead.
1082    libname = variant('gem5_' + label)
1083    exename = variant('gem5.' + label)
1084    secondary_exename = variant('m5.' + label)
1085
1086    new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
1087    new_env.Label = label
1088    new_env.Append(**kwargs)
1089
1090    swig_env = new_env.Clone()
1091
1092    # Both gcc and clang have issues with unused labels and values in
1093    # the SWIG generated code
1094    swig_env.Append(CCFLAGS=['-Wno-unused-label', '-Wno-unused-value'])
1095
1096    if env['GCC']:
1097        # Depending on the SWIG version, we also need to supress
1098        # warnings about uninitialized variables and missing field
1099        # initializers.
1100        swig_env.Append(CCFLAGS=['-Wno-uninitialized',
1101                                 '-Wno-missing-field-initializers',
1102                                 '-Wno-unused-but-set-variable',
1103                                 '-Wno-maybe-uninitialized',
1104                                 '-Wno-type-limits'])
1105
1106
1107        # The address sanitizer is available for gcc >= 4.8
1108        if GetOption('with_asan'):
1109            if GetOption('with_ubsan') and \
1110                    compareVersions(env['GCC_VERSION'], '4.9') >= 0:
1111                new_env.Append(CCFLAGS=['-fsanitize=address,undefined',
1112                                        '-fno-omit-frame-pointer'])
1113                new_env.Append(LINKFLAGS='-fsanitize=address,undefined')
1114            else:
1115                new_env.Append(CCFLAGS=['-fsanitize=address',
1116                                        '-fno-omit-frame-pointer'])
1117                new_env.Append(LINKFLAGS='-fsanitize=address')
1118        # Only gcc >= 4.9 supports UBSan, so check both the version
1119        # and the command-line option before adding the compiler and
1120        # linker flags.
1121        elif GetOption('with_ubsan') and \
1122                compareVersions(env['GCC_VERSION'], '4.9') >= 0:
1123            new_env.Append(CCFLAGS='-fsanitize=undefined')
1124            new_env.Append(LINKFLAGS='-fsanitize=undefined')
1125
1126
1127    if env['CLANG']:
1128        swig_env.Append(CCFLAGS=['-Wno-sometimes-uninitialized',
1129                                 '-Wno-deprecated-register',
1130                                 '-Wno-tautological-compare'])
1131
1132        # We require clang >= 3.1, so there is no need to check any
1133        # versions here.
1134        if GetOption('with_ubsan'):
1135            if GetOption('with_asan'):
1136                new_env.Append(CCFLAGS=['-fsanitize=address,undefined',
1137                                        '-fno-omit-frame-pointer'])
1138                new_env.Append(LINKFLAGS='-fsanitize=address,undefined')
1139            else:
1140                new_env.Append(CCFLAGS='-fsanitize=undefined')
1141                new_env.Append(LINKFLAGS='-fsanitize=undefined')
1142
1143        elif GetOption('with_asan'):
1144            new_env.Append(CCFLAGS=['-fsanitize=address',
1145                                    '-fno-omit-frame-pointer'])
1146            new_env.Append(LINKFLAGS='-fsanitize=address')
1147
1148    werror_env = new_env.Clone()
1149    # Treat warnings as errors but white list some warnings that we
1150    # want to allow (e.g., deprecation warnings).
1151    werror_env.Append(CCFLAGS=['-Werror',
1152                               '-Wno-error=deprecated-declarations',
1153                               '-Wno-error=deprecated',
1154                               ])
1155
1156    def make_obj(source, static, extra_deps = None):
1157        '''This function adds the specified source to the correct
1158        build environment, and returns the corresponding SCons Object
1159        nodes'''
1160
1161        if source.swig:
1162            env = swig_env
1163        elif source.Werror:
1164            env = werror_env
1165        else:
1166            env = new_env
1167
1168        if static:
1169            obj = env.StaticObject(source.tnode)
1170        else:
1171            obj = env.SharedObject(source.tnode)
1172
1173        if extra_deps:
1174            env.Depends(obj, extra_deps)
1175
1176        return obj
1177
1178    lib_guards = {'main': False, 'skip_lib': False}
1179
1180    # Without Python, leave out all SWIG and Python content from the
1181    # library builds.  The option doesn't affect gem5 built as a program
1182    if GetOption('without_python'):
1183        lib_guards['skip_no_python'] = False
1184
1185    static_objs = []
1186    shared_objs = []
1187    for s in guarded_source_iterator(Source.source_groups[None], **lib_guards):
1188        static_objs.append(make_obj(s, True))
1189        shared_objs.append(make_obj(s, False))
1190
1191    partial_objs = []
1192    for group, all_srcs in Source.source_groups.iteritems():
1193        # If these are the ungrouped source files, skip them.
1194        if not group:
1195            continue
1196
1197        # Get a list of the source files compatible with the current guards.
1198        srcs = [ s for s in guarded_source_iterator(all_srcs, **lib_guards) ]
1199        # If there aren't any left, skip this group.
1200        if not srcs:
1201            continue
1202
1203        # Set up the static partially linked objects.
1204        source_objs = [ make_obj(s, True) for s in srcs ]
1205        file_name = new_env.subst("${OBJPREFIX}lib${OBJSUFFIX}.partial")
1206        target = File(joinpath(group, file_name))
1207        partial = env.PartialStatic(target=target, source=source_objs)
1208        static_objs.append(partial)
1209
1210        # Set up the shared partially linked objects.
1211        source_objs = [ make_obj(s, False) for s in srcs ]
1212        file_name = new_env.subst("${SHOBJPREFIX}lib${SHOBJSUFFIX}.partial")
1213        target = File(joinpath(group, file_name))
1214        partial = env.PartialShared(target=target, source=source_objs)
1215        shared_objs.append(partial)
1216
1217    static_date = make_obj(date_source, static=True, extra_deps=static_objs)
1218    static_objs.append(static_date)
1219
1220    shared_date = make_obj(date_source, static=False, extra_deps=shared_objs)
1221    shared_objs.append(shared_date)
1222
1223    # First make a library of everything but main() so other programs can
1224    # link against m5.
1225    static_lib = new_env.StaticLibrary(libname, static_objs)
1226    shared_lib = new_env.SharedLibrary(libname, shared_objs)
1227
1228    # Now link a stub with main() and the static library.
1229    main_objs = [ make_obj(s, True) for s in Source.get(main=True) ]
1230
1231    for test in UnitTest.all:
1232        flags = { test.target : True }
1233        test_sources = Source.get(**flags)
1234        test_objs = [ make_obj(s, static=True) for s in test_sources ]
1235        if test.main:
1236            test_objs += main_objs
1237        path = variant('unittest/%s.%s' % (test.target, label))
1238        new_env.Program(path, test_objs + static_objs)
1239
1240    progname = exename
1241    if strip:
1242        progname += '.unstripped'
1243
1244    targets = new_env.Program(progname, main_objs + static_objs)
1245
1246    if strip:
1247        if sys.platform == 'sunos5':
1248            cmd = 'cp $SOURCE $TARGET; strip $TARGET'
1249        else:
1250            cmd = 'strip $SOURCE -o $TARGET'
1251        targets = new_env.Command(exename, progname,
1252                    MakeAction(cmd, Transform("STRIP")))
1253
1254    new_env.Command(secondary_exename, exename,
1255            MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK")))
1256
1257    new_env.M5Binary = targets[0]
1258    return new_env
1259
1260# Start out with the compiler flags common to all compilers,
1261# i.e. they all use -g for opt and -g -pg for prof
1262ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'],
1263           'perf' : ['-g']}
1264
1265# Start out with the linker flags common to all linkers, i.e. -pg for
1266# prof, and -lprofiler for perf. The -lprofile flag is surrounded by
1267# no-as-needed and as-needed as the binutils linker is too clever and
1268# simply doesn't link to the library otherwise.
1269ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'],
1270           'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']}
1271
1272# For Link Time Optimization, the optimisation flags used to compile
1273# individual files are decoupled from those used at link time
1274# (i.e. you can compile with -O3 and perform LTO with -O0), so we need
1275# to also update the linker flags based on the target.
1276if env['GCC']:
1277    if sys.platform == 'sunos5':
1278        ccflags['debug'] += ['-gstabs+']
1279    else:
1280        ccflags['debug'] += ['-ggdb3']
1281    ldflags['debug'] += ['-O0']
1282    # opt, fast, prof and perf all share the same cc flags, also add
1283    # the optimization to the ldflags as LTO defers the optimization
1284    # to link time
1285    for target in ['opt', 'fast', 'prof', 'perf']:
1286        ccflags[target] += ['-O3']
1287        ldflags[target] += ['-O3']
1288
1289    ccflags['fast'] += env['LTO_CCFLAGS']
1290    ldflags['fast'] += env['LTO_LDFLAGS']
1291elif env['CLANG']:
1292    ccflags['debug'] += ['-g', '-O0']
1293    # opt, fast, prof and perf all share the same cc flags
1294    for target in ['opt', 'fast', 'prof', 'perf']:
1295        ccflags[target] += ['-O3']
1296else:
1297    print 'Unknown compiler, please fix compiler options'
1298    Exit(1)
1299
1300
1301# To speed things up, we only instantiate the build environments we
1302# need.  We try to identify the needed environment for each target; if
1303# we can't, we fall back on instantiating all the environments just to
1304# be safe.
1305target_types = ['debug', 'opt', 'fast', 'prof', 'perf']
1306obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof',
1307              'gpo' : 'perf'}
1308
1309def identifyTarget(t):
1310    ext = t.split('.')[-1]
1311    if ext in target_types:
1312        return ext
1313    if obj2target.has_key(ext):
1314        return obj2target[ext]
1315    match = re.search(r'/tests/([^/]+)/', t)
1316    if match and match.group(1) in target_types:
1317        return match.group(1)
1318    return 'all'
1319
1320needed_envs = [identifyTarget(target) for target in BUILD_TARGETS]
1321if 'all' in needed_envs:
1322    needed_envs += target_types
1323
1324def makeEnvirons(target, source, env):
1325    # cause any later Source() calls to be fatal, as a diagnostic.
1326    Source.done()
1327
1328    envList = []
1329
1330    # Debug binary
1331    if 'debug' in needed_envs:
1332        envList.append(
1333            makeEnv(env, 'debug', '.do',
1334                    CCFLAGS = Split(ccflags['debug']),
1335                    CPPDEFINES = ['DEBUG', 'TRACING_ON=1'],
1336                    LINKFLAGS = Split(ldflags['debug'])))
1337
1338    # Optimized binary
1339    if 'opt' in needed_envs:
1340        envList.append(
1341            makeEnv(env, 'opt', '.o',
1342                    CCFLAGS = Split(ccflags['opt']),
1343                    CPPDEFINES = ['TRACING_ON=1'],
1344                    LINKFLAGS = Split(ldflags['opt'])))
1345
1346    # "Fast" binary
1347    if 'fast' in needed_envs:
1348        envList.append(
1349            makeEnv(env, 'fast', '.fo', strip = True,
1350                    CCFLAGS = Split(ccflags['fast']),
1351                    CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1352                    LINKFLAGS = Split(ldflags['fast'])))
1353
1354    # Profiled binary using gprof
1355    if 'prof' in needed_envs:
1356        envList.append(
1357            makeEnv(env, 'prof', '.po',
1358                    CCFLAGS = Split(ccflags['prof']),
1359                    CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1360                    LINKFLAGS = Split(ldflags['prof'])))
1361
1362    # Profiled binary using google-pprof
1363    if 'perf' in needed_envs:
1364        envList.append(
1365            makeEnv(env, 'perf', '.gpo',
1366                    CCFLAGS = Split(ccflags['perf']),
1367                    CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1368                    LINKFLAGS = Split(ldflags['perf'])))
1369
1370    # Set up the regression tests for each build.
1371    for e in envList:
1372        SConscript(os.path.join(env.root.abspath, 'tests', 'SConscript'),
1373                   variant_dir = variantd('tests', e.Label),
1374                   exports = { 'env' : e }, duplicate = False)
1375
1376# The MakeEnvirons Builder defers the full dependency collection until
1377# after processing the ISA definition (due to dynamically generated
1378# source files).  Add this dependency to all targets so they will wait
1379# until the environments are completely set up.  Otherwise, a second
1380# process (e.g. -j2 or higher) will try to compile the requested target,
1381# not know how, and fail.
1382env.Append(BUILDERS = {'MakeEnvirons' :
1383                        Builder(action=MakeAction(makeEnvirons,
1384                                                  Transform("ENVIRONS", 1)))})
1385
1386isa_target = env['PHONY_BASE'] + '-deps'
1387environs   = env['PHONY_BASE'] + '-environs'
1388env.Depends('#all-deps',     isa_target)
1389env.Depends('#all-environs', environs)
1390env.ScanISA(isa_target, File('arch/%s/generated/inc.d' % env['TARGET_ISA']))
1391envSetup = env.MakeEnvirons(environs, isa_target)
1392
1393# make sure no -deps targets occur before all ISAs are complete
1394env.Depends(isa_target, '#all-isas')
1395# likewise for -environs targets and all the -deps targets
1396env.Depends(environs, '#all-deps')
1397