SConstruct revision 10456
1# -*- mode:python -*-
2
3# Copyright (c) 2013 ARM Limited
4# All rights reserved.
5#
6# The license below extends only to copyright in the software and shall
7# not be construed as granting a license to any other intellectual
8# property including but not limited to intellectual property relating
9# to a hardware implementation of the functionality of the software
10# licensed hereunder.  You may use the software subject to the license
11# terms below provided that you ensure that this notice is replicated
12# unmodified and in its entirety in all distributions of the software,
13# modified or unmodified, in source code or in binary form.
14#
15# Copyright (c) 2011 Advanced Micro Devices, Inc.
16# Copyright (c) 2009 The Hewlett-Packard Development Company
17# Copyright (c) 2004-2005 The Regents of The University of Michigan
18# All rights reserved.
19#
20# Redistribution and use in source and binary forms, with or without
21# modification, are permitted provided that the following conditions are
22# met: redistributions of source code must retain the above copyright
23# notice, this list of conditions and the following disclaimer;
24# redistributions in binary form must reproduce the above copyright
25# notice, this list of conditions and the following disclaimer in the
26# documentation and/or other materials provided with the distribution;
27# neither the name of the copyright holders nor the names of its
28# contributors may be used to endorse or promote products derived from
29# this software without specific prior written permission.
30#
31# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
32# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
34# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
36# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
37# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
38# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
39# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
40# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
41# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42#
43# Authors: Steve Reinhardt
44#          Nathan Binkert
45
46###################################################
47#
48# SCons top-level build description (SConstruct) file.
49#
50# While in this directory ('gem5'), just type 'scons' to build the default
51# configuration (see below), or type 'scons build/<CONFIG>/<binary>'
52# to build some other configuration (e.g., 'build/ALPHA/gem5.opt' for
53# the optimized full-system version).
54#
55# You can build gem5 in a different directory as long as there is a
56# 'build/<CONFIG>' somewhere along the target path.  The build system
57# expects that all configs under the same build directory are being
58# built for the same host system.
59#
60# Examples:
61#
62#   The following two commands are equivalent.  The '-u' option tells
63#   scons to search up the directory tree for this SConstruct file.
64#   % cd <path-to-src>/gem5 ; scons build/ALPHA/gem5.debug
65#   % cd <path-to-src>/gem5/build/ALPHA; scons -u gem5.debug
66#
67#   The following two commands are equivalent and demonstrate building
68#   in a directory outside of the source tree.  The '-C' option tells
69#   scons to chdir to the specified directory to find this SConstruct
70#   file.
71#   % cd <path-to-src>/gem5 ; scons /local/foo/build/ALPHA/gem5.debug
72#   % cd /local/foo/build/ALPHA; scons -C <path-to-src>/gem5 gem5.debug
73#
74# You can use 'scons -H' to print scons options.  If you're in this
75# 'gem5' directory (or use -u or -C to tell scons where to find this
76# file), you can use 'scons -h' to print all the gem5-specific build
77# options as well.
78#
79###################################################
80
81# Check for recent-enough Python and SCons versions.
82try:
83    # Really old versions of scons only take two options for the
84    # function, so check once without the revision and once with the
85    # revision, the first instance will fail for stuff other than
86    # 0.98, and the second will fail for 0.98.0
87    EnsureSConsVersion(0, 98)
88    EnsureSConsVersion(0, 98, 1)
89except SystemExit, e:
90    print """
91For more details, see:
92    http://gem5.org/Dependencies
93"""
94    raise
95
96# We ensure the python version early because because python-config
97# requires python 2.5
98try:
99    EnsurePythonVersion(2, 5)
100except SystemExit, e:
101    print """
102You can use a non-default installation of the Python interpreter by
103rearranging your PATH so that scons finds the non-default 'python' and
104'python-config' first.
105
106For more details, see:
107    http://gem5.org/wiki/index.php/Using_a_non-default_Python_installation
108"""
109    raise
110
111# Global Python includes
112import itertools
113import os
114import re
115import subprocess
116import sys
117
118from os import mkdir, environ
119from os.path import abspath, basename, dirname, expanduser, normpath
120from os.path import exists,  isdir, isfile
121from os.path import join as joinpath, split as splitpath
122
123# SCons includes
124import SCons
125import SCons.Node
126
127extra_python_paths = [
128    Dir('src/python').srcnode().abspath, # gem5 includes
129    Dir('ext/ply').srcnode().abspath, # ply is used by several files
130    ]
131
132sys.path[1:1] = extra_python_paths
133
134from m5.util import compareVersions, readCommand
135from m5.util.terminal import get_termcap
136
137help_texts = {
138    "options" : "",
139    "global_vars" : "",
140    "local_vars" : ""
141}
142
143Export("help_texts")
144
145
146# There's a bug in scons in that (1) by default, the help texts from
147# AddOption() are supposed to be displayed when you type 'scons -h'
148# and (2) you can override the help displayed by 'scons -h' using the
149# Help() function, but these two features are incompatible: once
150# you've overridden the help text using Help(), there's no way to get
151# at the help texts from AddOptions.  See:
152#     http://scons.tigris.org/issues/show_bug.cgi?id=2356
153#     http://scons.tigris.org/issues/show_bug.cgi?id=2611
154# This hack lets us extract the help text from AddOptions and
155# re-inject it via Help().  Ideally someday this bug will be fixed and
156# we can just use AddOption directly.
157def AddLocalOption(*args, **kwargs):
158    col_width = 30
159
160    help = "  " + ", ".join(args)
161    if "help" in kwargs:
162        length = len(help)
163        if length >= col_width:
164            help += "\n" + " " * col_width
165        else:
166            help += " " * (col_width - length)
167        help += kwargs["help"]
168    help_texts["options"] += help + "\n"
169
170    AddOption(*args, **kwargs)
171
172AddLocalOption('--colors', dest='use_colors', action='store_true',
173               help="Add color to abbreviated scons output")
174AddLocalOption('--no-colors', dest='use_colors', action='store_false',
175               help="Don't add color to abbreviated scons output")
176AddLocalOption('--default', dest='default', type='string', action='store',
177               help='Override which build_opts file to use for defaults')
178AddLocalOption('--ignore-style', dest='ignore_style', action='store_true',
179               help='Disable style checking hooks')
180AddLocalOption('--no-lto', dest='no_lto', action='store_true',
181               help='Disable Link-Time Optimization for fast')
182AddLocalOption('--update-ref', dest='update_ref', action='store_true',
183               help='Update test reference outputs')
184AddLocalOption('--verbose', dest='verbose', action='store_true',
185               help='Print full tool command lines')
186AddLocalOption('--without-python', dest='without_python',
187               action='store_true',
188               help='Build without Python configuration support')
189AddLocalOption('--without-tcmalloc', dest='without_tcmalloc',
190               action='store_true',
191               help='Disable linking against tcmalloc')
192
193termcap = get_termcap(GetOption('use_colors'))
194
195########################################################################
196#
197# Set up the main build environment.
198#
199########################################################################
200
201# export TERM so that clang reports errors in color
202use_vars = set([ 'AS', 'AR', 'CC', 'CXX', 'HOME', 'LD_LIBRARY_PATH',
203                 'LIBRARY_PATH', 'PATH', 'PKG_CONFIG_PATH', 'PROTOC',
204                 'PYTHONPATH', 'RANLIB', 'SWIG', 'TERM' ])
205
206use_prefixes = [
207    "M5",           # M5 configuration (e.g., path to kernels)
208    "DISTCC_",      # distcc (distributed compiler wrapper) configuration
209    "CCACHE_",      # ccache (caching compiler wrapper) configuration
210    "CCC_",         # clang static analyzer configuration
211    ]
212
213use_env = {}
214for key,val in os.environ.iteritems():
215    if key in use_vars or \
216            any([key.startswith(prefix) for prefix in use_prefixes]):
217        use_env[key] = val
218
219main = Environment(ENV=use_env)
220main.Decider('MD5-timestamp')
221main.root = Dir(".")         # The current directory (where this file lives).
222main.srcdir = Dir("src")     # The source directory
223
224main_dict_keys = main.Dictionary().keys()
225
226# Check that we have a C/C++ compiler
227if not ('CC' in main_dict_keys and 'CXX' in main_dict_keys):
228    print "No C++ compiler installed (package g++ on Ubuntu and RedHat)"
229    Exit(1)
230
231# Check that swig is present
232if not 'SWIG' in main_dict_keys:
233    print "swig is not installed (package swig on Ubuntu and RedHat)"
234    Exit(1)
235
236# add useful python code PYTHONPATH so it can be used by subprocesses
237# as well
238main.AppendENVPath('PYTHONPATH', extra_python_paths)
239
240########################################################################
241#
242# Mercurial Stuff.
243#
244# If the gem5 directory is a mercurial repository, we should do some
245# extra things.
246#
247########################################################################
248
249hgdir = main.root.Dir(".hg")
250
251mercurial_style_message = """
252You're missing the gem5 style hook, which automatically checks your code
253against the gem5 style rules on hg commit and qrefresh commands.  This
254script will now install the hook in your .hg/hgrc file.
255Press enter to continue, or ctrl-c to abort: """
256
257mercurial_style_hook = """
258# The following lines were automatically added by gem5/SConstruct
259# to provide the gem5 style-checking hooks
260[extensions]
261style = %s/util/style.py
262
263[hooks]
264pretxncommit.style = python:style.check_style
265pre-qrefresh.style = python:style.check_style
266# End of SConstruct additions
267
268""" % (main.root.abspath)
269
270mercurial_lib_not_found = """
271Mercurial libraries cannot be found, ignoring style hook.  If
272you are a gem5 developer, please fix this and run the style
273hook. It is important.
274"""
275
276# Check for style hook and prompt for installation if it's not there.
277# Skip this if --ignore-style was specified, there's no .hg dir to
278# install a hook in, or there's no interactive terminal to prompt.
279if not GetOption('ignore_style') and hgdir.exists() and sys.stdin.isatty():
280    style_hook = True
281    try:
282        from mercurial import ui
283        ui = ui.ui()
284        ui.readconfig(hgdir.File('hgrc').abspath)
285        style_hook = ui.config('hooks', 'pretxncommit.style', None) and \
286                     ui.config('hooks', 'pre-qrefresh.style', None)
287    except ImportError:
288        print mercurial_lib_not_found
289
290    if not style_hook:
291        print mercurial_style_message,
292        # continue unless user does ctrl-c/ctrl-d etc.
293        try:
294            raw_input()
295        except:
296            print "Input exception, exiting scons.\n"
297            sys.exit(1)
298        hgrc_path = '%s/.hg/hgrc' % main.root.abspath
299        print "Adding style hook to", hgrc_path, "\n"
300        try:
301            hgrc = open(hgrc_path, 'a')
302            hgrc.write(mercurial_style_hook)
303            hgrc.close()
304        except:
305            print "Error updating", hgrc_path
306            sys.exit(1)
307
308
309###################################################
310#
311# Figure out which configurations to set up based on the path(s) of
312# the target(s).
313#
314###################################################
315
316# Find default configuration & binary.
317Default(environ.get('M5_DEFAULT_BINARY', 'build/ALPHA/gem5.debug'))
318
319# helper function: find last occurrence of element in list
320def rfind(l, elt, offs = -1):
321    for i in range(len(l)+offs, 0, -1):
322        if l[i] == elt:
323            return i
324    raise ValueError, "element not found"
325
326# Take a list of paths (or SCons Nodes) and return a list with all
327# paths made absolute and ~-expanded.  Paths will be interpreted
328# relative to the launch directory unless a different root is provided
329def makePathListAbsolute(path_list, root=GetLaunchDir()):
330    return [abspath(joinpath(root, expanduser(str(p))))
331            for p in path_list]
332
333# Each target must have 'build' in the interior of the path; the
334# directory below this will determine the build parameters.  For
335# example, for target 'foo/bar/build/ALPHA_SE/arch/alpha/blah.do' we
336# recognize that ALPHA_SE specifies the configuration because it
337# follow 'build' in the build path.
338
339# The funky assignment to "[:]" is needed to replace the list contents
340# in place rather than reassign the symbol to a new list, which
341# doesn't work (obviously!).
342BUILD_TARGETS[:] = makePathListAbsolute(BUILD_TARGETS)
343
344# Generate a list of the unique build roots and configs that the
345# collected targets reference.
346variant_paths = []
347build_root = None
348for t in BUILD_TARGETS:
349    path_dirs = t.split('/')
350    try:
351        build_top = rfind(path_dirs, 'build', -2)
352    except:
353        print "Error: no non-leaf 'build' dir found on target path", t
354        Exit(1)
355    this_build_root = joinpath('/',*path_dirs[:build_top+1])
356    if not build_root:
357        build_root = this_build_root
358    else:
359        if this_build_root != build_root:
360            print "Error: build targets not under same build root\n"\
361                  "  %s\n  %s" % (build_root, this_build_root)
362            Exit(1)
363    variant_path = joinpath('/',*path_dirs[:build_top+2])
364    if variant_path not in variant_paths:
365        variant_paths.append(variant_path)
366
367# Make sure build_root exists (might not if this is the first build there)
368if not isdir(build_root):
369    mkdir(build_root)
370main['BUILDROOT'] = build_root
371
372Export('main')
373
374main.SConsignFile(joinpath(build_root, "sconsign"))
375
376# Default duplicate option is to use hard links, but this messes up
377# when you use emacs to edit a file in the target dir, as emacs moves
378# file to file~ then copies to file, breaking the link.  Symbolic
379# (soft) links work better.
380main.SetOption('duplicate', 'soft-copy')
381
382#
383# Set up global sticky variables... these are common to an entire build
384# tree (not specific to a particular build like ALPHA_SE)
385#
386
387global_vars_file = joinpath(build_root, 'variables.global')
388
389global_vars = Variables(global_vars_file, args=ARGUMENTS)
390
391global_vars.AddVariables(
392    ('CC', 'C compiler', environ.get('CC', main['CC'])),
393    ('CXX', 'C++ compiler', environ.get('CXX', main['CXX'])),
394    ('SWIG', 'SWIG tool', environ.get('SWIG', main['SWIG'])),
395    ('PROTOC', 'protoc tool', environ.get('PROTOC', 'protoc')),
396    ('BATCH', 'Use batch pool for build and tests', False),
397    ('BATCH_CMD', 'Batch pool submission command name', 'qdo'),
398    ('M5_BUILD_CACHE', 'Cache built objects in this directory', False),
399    ('EXTRAS', 'Add extra directories to the compilation', '')
400    )
401
402# Update main environment with values from ARGUMENTS & global_vars_file
403global_vars.Update(main)
404help_texts["global_vars"] += global_vars.GenerateHelpText(main)
405
406# Save sticky variable settings back to current variables file
407global_vars.Save(global_vars_file, main)
408
409# Parse EXTRAS variable to build list of all directories where we're
410# look for sources etc.  This list is exported as extras_dir_list.
411base_dir = main.srcdir.abspath
412if main['EXTRAS']:
413    extras_dir_list = makePathListAbsolute(main['EXTRAS'].split(':'))
414else:
415    extras_dir_list = []
416
417Export('base_dir')
418Export('extras_dir_list')
419
420# the ext directory should be on the #includes path
421main.Append(CPPPATH=[Dir('ext')])
422
423def strip_build_path(path, env):
424    path = str(path)
425    variant_base = env['BUILDROOT'] + os.path.sep
426    if path.startswith(variant_base):
427        path = path[len(variant_base):]
428    elif path.startswith('build/'):
429        path = path[6:]
430    return path
431
432# Generate a string of the form:
433#   common/path/prefix/src1, src2 -> tgt1, tgt2
434# to print while building.
435class Transform(object):
436    # all specific color settings should be here and nowhere else
437    tool_color = termcap.Normal
438    pfx_color = termcap.Yellow
439    srcs_color = termcap.Yellow + termcap.Bold
440    arrow_color = termcap.Blue + termcap.Bold
441    tgts_color = termcap.Yellow + termcap.Bold
442
443    def __init__(self, tool, max_sources=99):
444        self.format = self.tool_color + (" [%8s] " % tool) \
445                      + self.pfx_color + "%s" \
446                      + self.srcs_color + "%s" \
447                      + self.arrow_color + " -> " \
448                      + self.tgts_color + "%s" \
449                      + termcap.Normal
450        self.max_sources = max_sources
451
452    def __call__(self, target, source, env, for_signature=None):
453        # truncate source list according to max_sources param
454        source = source[0:self.max_sources]
455        def strip(f):
456            return strip_build_path(str(f), env)
457        if len(source) > 0:
458            srcs = map(strip, source)
459        else:
460            srcs = ['']
461        tgts = map(strip, target)
462        # surprisingly, os.path.commonprefix is a dumb char-by-char string
463        # operation that has nothing to do with paths.
464        com_pfx = os.path.commonprefix(srcs + tgts)
465        com_pfx_len = len(com_pfx)
466        if com_pfx:
467            # do some cleanup and sanity checking on common prefix
468            if com_pfx[-1] == ".":
469                # prefix matches all but file extension: ok
470                # back up one to change 'foo.cc -> o' to 'foo.cc -> .o'
471                com_pfx = com_pfx[0:-1]
472            elif com_pfx[-1] == "/":
473                # common prefix is directory path: OK
474                pass
475            else:
476                src0_len = len(srcs[0])
477                tgt0_len = len(tgts[0])
478                if src0_len == com_pfx_len:
479                    # source is a substring of target, OK
480                    pass
481                elif tgt0_len == com_pfx_len:
482                    # target is a substring of source, need to back up to
483                    # avoid empty string on RHS of arrow
484                    sep_idx = com_pfx.rfind(".")
485                    if sep_idx != -1:
486                        com_pfx = com_pfx[0:sep_idx]
487                    else:
488                        com_pfx = ''
489                elif src0_len > com_pfx_len and srcs[0][com_pfx_len] == ".":
490                    # still splitting at file extension: ok
491                    pass
492                else:
493                    # probably a fluke; ignore it
494                    com_pfx = ''
495        # recalculate length in case com_pfx was modified
496        com_pfx_len = len(com_pfx)
497        def fmt(files):
498            f = map(lambda s: s[com_pfx_len:], files)
499            return ', '.join(f)
500        return self.format % (com_pfx, fmt(srcs), fmt(tgts))
501
502Export('Transform')
503
504# enable the regression script to use the termcap
505main['TERMCAP'] = termcap
506
507if GetOption('verbose'):
508    def MakeAction(action, string, *args, **kwargs):
509        return Action(action, *args, **kwargs)
510else:
511    MakeAction = Action
512    main['CCCOMSTR']        = Transform("CC")
513    main['CXXCOMSTR']       = Transform("CXX")
514    main['ASCOMSTR']        = Transform("AS")
515    main['SWIGCOMSTR']      = Transform("SWIG")
516    main['ARCOMSTR']        = Transform("AR", 0)
517    main['LINKCOMSTR']      = Transform("LINK", 0)
518    main['RANLIBCOMSTR']    = Transform("RANLIB", 0)
519    main['M4COMSTR']        = Transform("M4")
520    main['SHCCCOMSTR']      = Transform("SHCC")
521    main['SHCXXCOMSTR']     = Transform("SHCXX")
522Export('MakeAction')
523
524# Initialize the Link-Time Optimization (LTO) flags
525main['LTO_CCFLAGS'] = []
526main['LTO_LDFLAGS'] = []
527
528# According to the readme, tcmalloc works best if the compiler doesn't
529# assume that we're using the builtin malloc and friends. These flags
530# are compiler-specific, so we need to set them after we detect which
531# compiler we're using.
532main['TCMALLOC_CCFLAGS'] = []
533
534CXX_version = readCommand([main['CXX'],'--version'], exception=False)
535CXX_V = readCommand([main['CXX'],'-V'], exception=False)
536
537main['GCC'] = CXX_version and CXX_version.find('g++') >= 0
538main['CLANG'] = CXX_version and CXX_version.find('clang') >= 0
539if main['GCC'] + main['CLANG'] > 1:
540    print 'Error: How can we have two at the same time?'
541    Exit(1)
542
543# Set up default C++ compiler flags
544if main['GCC'] or main['CLANG']:
545    # As gcc and clang share many flags, do the common parts here
546    main.Append(CCFLAGS=['-pipe'])
547    main.Append(CCFLAGS=['-fno-strict-aliasing'])
548    # Enable -Wall and then disable the few warnings that we
549    # consistently violate
550    main.Append(CCFLAGS=['-Wall', '-Wno-sign-compare', '-Wundef'])
551    # We always compile using C++11, but only gcc >= 4.7 and clang 3.1
552    # actually use that name, so we stick with c++0x
553    main.Append(CXXFLAGS=['-std=c++0x'])
554    # Add selected sanity checks from -Wextra
555    main.Append(CXXFLAGS=['-Wmissing-field-initializers',
556                          '-Woverloaded-virtual'])
557else:
558    print termcap.Yellow + termcap.Bold + 'Error' + termcap.Normal,
559    print "Don't know what compiler options to use for your compiler."
560    print termcap.Yellow + '       compiler:' + termcap.Normal, main['CXX']
561    print termcap.Yellow + '       version:' + termcap.Normal,
562    if not CXX_version:
563        print termcap.Yellow + termcap.Bold + "COMMAND NOT FOUND!" +\
564               termcap.Normal
565    else:
566        print CXX_version.replace('\n', '<nl>')
567    print "       If you're trying to use a compiler other than GCC"
568    print "       or clang, there appears to be something wrong with your"
569    print "       environment."
570    print "       "
571    print "       If you are trying to use a compiler other than those listed"
572    print "       above you will need to ease fix SConstruct and "
573    print "       src/SConscript to support that compiler."
574    Exit(1)
575
576if main['GCC']:
577    # Check for a supported version of gcc. >= 4.6 is chosen for its
578    # level of c++11 support. See
579    # http://gcc.gnu.org/projects/cxx0x.html for details. 4.6 is also
580    # the first version with proper LTO support.
581    gcc_version = readCommand([main['CXX'], '-dumpversion'], exception=False)
582    if compareVersions(gcc_version, "4.6") < 0:
583        print 'Error: gcc version 4.6 or newer required.'
584        print '       Installed version:', gcc_version
585        Exit(1)
586
587    main['GCC_VERSION'] = gcc_version
588
589    # gcc from version 4.8 and above generates "rep; ret" instructions
590    # to avoid performance penalties on certain AMD chips. Older
591    # assemblers detect this as an error, "Error: expecting string
592    # instruction after `rep'"
593    if compareVersions(gcc_version, "4.8") > 0:
594        as_version = readCommand([main['AS'], '-v', '/dev/null'],
595                                 exception=False).split()
596
597        if not as_version or compareVersions(as_version[-1], "2.23") < 0:
598            print termcap.Yellow + termcap.Bold + \
599                'Warning: This combination of gcc and binutils have' + \
600                ' known incompatibilities.\n' + \
601                '         If you encounter build problems, please update ' + \
602                'binutils to 2.23.' + \
603                termcap.Normal
604
605    # Add the appropriate Link-Time Optimization (LTO) flags
606    # unless LTO is explicitly turned off. Note that these flags
607    # are only used by the fast target.
608    if not GetOption('no_lto'):
609        # Pass the LTO flag when compiling to produce GIMPLE
610        # output, we merely create the flags here and only append
611        # them later
612        main['LTO_CCFLAGS'] = ['-flto=%d' % GetOption('num_jobs')]
613
614        # Use the same amount of jobs for LTO as we are running
615        # scons with
616        main['LTO_LDFLAGS'] = ['-flto=%d' % GetOption('num_jobs')]
617
618    main.Append(TCMALLOC_CCFLAGS=['-fno-builtin-malloc', '-fno-builtin-calloc',
619                                  '-fno-builtin-realloc', '-fno-builtin-free'])
620
621elif main['CLANG']:
622    # Check for a supported version of clang, >= 3.0 is needed to
623    # support similar features as gcc 4.6. See
624    # http://clang.llvm.org/cxx_status.html for details
625    clang_version_re = re.compile(".* version (\d+\.\d+)")
626    clang_version_match = clang_version_re.search(CXX_version)
627    if (clang_version_match):
628        clang_version = clang_version_match.groups()[0]
629        if compareVersions(clang_version, "3.0") < 0:
630            print 'Error: clang version 3.0 or newer required.'
631            print '       Installed version:', clang_version
632            Exit(1)
633    else:
634        print 'Error: Unable to determine clang version.'
635        Exit(1)
636
637    # clang has a few additional warnings that we disable,
638    # tautological comparisons are allowed due to unsigned integers
639    # being compared to constants that happen to be 0, and extraneous
640    # parantheses are allowed due to Ruby's printing of the AST,
641    # finally self assignments are allowed as the generated CPU code
642    # is relying on this
643    main.Append(CCFLAGS=['-Wno-tautological-compare',
644                         '-Wno-parentheses',
645                         '-Wno-self-assign',
646                         # Some versions of libstdc++ (4.8?) seem to
647                         # use struct hash and class hash
648                         # interchangeably.
649                         '-Wno-mismatched-tags',
650                         ])
651
652    main.Append(TCMALLOC_CCFLAGS=['-fno-builtin'])
653
654    # On Mac OS X/Darwin we need to also use libc++ (part of XCode) as
655    # opposed to libstdc++, as the later is dated.
656    if sys.platform == "darwin":
657        main.Append(CXXFLAGS=['-stdlib=libc++'])
658        main.Append(LIBS=['c++'])
659
660else:
661    print termcap.Yellow + termcap.Bold + 'Error' + termcap.Normal,
662    print "Don't know what compiler options to use for your compiler."
663    print termcap.Yellow + '       compiler:' + termcap.Normal, main['CXX']
664    print termcap.Yellow + '       version:' + termcap.Normal,
665    if not CXX_version:
666        print termcap.Yellow + termcap.Bold + "COMMAND NOT FOUND!" +\
667               termcap.Normal
668    else:
669        print CXX_version.replace('\n', '<nl>')
670    print "       If you're trying to use a compiler other than GCC"
671    print "       or clang, there appears to be something wrong with your"
672    print "       environment."
673    print "       "
674    print "       If you are trying to use a compiler other than those listed"
675    print "       above you will need to ease fix SConstruct and "
676    print "       src/SConscript to support that compiler."
677    Exit(1)
678
679# Set up common yacc/bison flags (needed for Ruby)
680main['YACCFLAGS'] = '-d'
681main['YACCHXXFILESUFFIX'] = '.hh'
682
683# Do this after we save setting back, or else we'll tack on an
684# extra 'qdo' every time we run scons.
685if main['BATCH']:
686    main['CC']     = main['BATCH_CMD'] + ' ' + main['CC']
687    main['CXX']    = main['BATCH_CMD'] + ' ' + main['CXX']
688    main['AS']     = main['BATCH_CMD'] + ' ' + main['AS']
689    main['AR']     = main['BATCH_CMD'] + ' ' + main['AR']
690    main['RANLIB'] = main['BATCH_CMD'] + ' ' + main['RANLIB']
691
692if sys.platform == 'cygwin':
693    # cygwin has some header file issues...
694    main.Append(CCFLAGS=["-Wno-uninitialized"])
695
696# Check for the protobuf compiler
697protoc_version = readCommand([main['PROTOC'], '--version'],
698                             exception='').split()
699
700# First two words should be "libprotoc x.y.z"
701if len(protoc_version) < 2 or protoc_version[0] != 'libprotoc':
702    print termcap.Yellow + termcap.Bold + \
703        'Warning: Protocol buffer compiler (protoc) not found.\n' + \
704        '         Please install protobuf-compiler for tracing support.' + \
705        termcap.Normal
706    main['PROTOC'] = False
707else:
708    # Based on the availability of the compress stream wrappers,
709    # require 2.1.0
710    min_protoc_version = '2.1.0'
711    if compareVersions(protoc_version[1], min_protoc_version) < 0:
712        print termcap.Yellow + termcap.Bold + \
713            'Warning: protoc version', min_protoc_version, \
714            'or newer required.\n' + \
715            '         Installed version:', protoc_version[1], \
716            termcap.Normal
717        main['PROTOC'] = False
718    else:
719        # Attempt to determine the appropriate include path and
720        # library path using pkg-config, that means we also need to
721        # check for pkg-config. Note that it is possible to use
722        # protobuf without the involvement of pkg-config. Later on we
723        # check go a library config check and at that point the test
724        # will fail if libprotobuf cannot be found.
725        if readCommand(['pkg-config', '--version'], exception=''):
726            try:
727                # Attempt to establish what linking flags to add for protobuf
728                # using pkg-config
729                main.ParseConfig('pkg-config --cflags --libs-only-L protobuf')
730            except:
731                print termcap.Yellow + termcap.Bold + \
732                    'Warning: pkg-config could not get protobuf flags.' + \
733                    termcap.Normal
734
735# Check for SWIG
736if not main.has_key('SWIG'):
737    print 'Error: SWIG utility not found.'
738    print '       Please install (see http://www.swig.org) and retry.'
739    Exit(1)
740
741# Check for appropriate SWIG version
742swig_version = readCommand([main['SWIG'], '-version'], exception='').split()
743# First 3 words should be "SWIG Version x.y.z"
744if len(swig_version) < 3 or \
745        swig_version[0] != 'SWIG' or swig_version[1] != 'Version':
746    print 'Error determining SWIG version.'
747    Exit(1)
748
749min_swig_version = '2.0.4'
750if compareVersions(swig_version[2], min_swig_version) < 0:
751    print 'Error: SWIG version', min_swig_version, 'or newer required.'
752    print '       Installed version:', swig_version[2]
753    Exit(1)
754
755# Check for known incompatibilities. The standard library shipped with
756# gcc >= 4.9 does not play well with swig versions prior to 3.0
757if main['GCC'] and compareVersions(gcc_version, '4.9') >= 0 and \
758        compareVersions(swig_version[2], '3.0') < 0:
759    print termcap.Yellow + termcap.Bold + \
760        'Warning: This combination of gcc and swig have' + \
761        ' known incompatibilities.\n' + \
762        '         If you encounter build problems, please update ' + \
763        'swig to 3.0 or later.' + \
764        termcap.Normal
765
766# Set up SWIG flags & scanner
767swig_flags=Split('-c++ -python -modern -templatereduce $_CPPINCFLAGS')
768main.Append(SWIGFLAGS=swig_flags)
769
770# Check for 'timeout' from GNU coreutils.  If present, regressions
771# will be run with a time limit.
772TIMEOUT_version = readCommand(['timeout', '--version'], exception=False)
773main['TIMEOUT'] = TIMEOUT_version and TIMEOUT_version.find('timeout') == 0
774
775# filter out all existing swig scanners, they mess up the dependency
776# stuff for some reason
777scanners = []
778for scanner in main['SCANNERS']:
779    skeys = scanner.skeys
780    if skeys == '.i':
781        continue
782
783    if isinstance(skeys, (list, tuple)) and '.i' in skeys:
784        continue
785
786    scanners.append(scanner)
787
788# add the new swig scanner that we like better
789from SCons.Scanner import ClassicCPP as CPPScanner
790swig_inc_re = '^[ \t]*[%,#][ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")'
791scanners.append(CPPScanner("SwigScan", [ ".i" ], "CPPPATH", swig_inc_re))
792
793# replace the scanners list that has what we want
794main['SCANNERS'] = scanners
795
796# Add a custom Check function to the Configure context so that we can
797# figure out if the compiler adds leading underscores to global
798# variables.  This is needed for the autogenerated asm files that we
799# use for embedding the python code.
800def CheckLeading(context):
801    context.Message("Checking for leading underscore in global variables...")
802    # 1) Define a global variable called x from asm so the C compiler
803    #    won't change the symbol at all.
804    # 2) Declare that variable.
805    # 3) Use the variable
806    #
807    # If the compiler prepends an underscore, this will successfully
808    # link because the external symbol 'x' will be called '_x' which
809    # was defined by the asm statement.  If the compiler does not
810    # prepend an underscore, this will not successfully link because
811    # '_x' will have been defined by assembly, while the C portion of
812    # the code will be trying to use 'x'
813    ret = context.TryLink('''
814        asm(".globl _x; _x: .byte 0");
815        extern int x;
816        int main() { return x; }
817        ''', extension=".c")
818    context.env.Append(LEADING_UNDERSCORE=ret)
819    context.Result(ret)
820    return ret
821
822# Add a custom Check function to test for structure members.
823def CheckMember(context, include, decl, member, include_quotes="<>"):
824    context.Message("Checking for member %s in %s..." %
825                    (member, decl))
826    text = """
827#include %(header)s
828int main(){
829  %(decl)s test;
830  (void)test.%(member)s;
831  return 0;
832};
833""" % { "header" : include_quotes[0] + include + include_quotes[1],
834        "decl" : decl,
835        "member" : member,
836        }
837
838    ret = context.TryCompile(text, extension=".cc")
839    context.Result(ret)
840    return ret
841
842# Platform-specific configuration.  Note again that we assume that all
843# builds under a given build root run on the same host platform.
844conf = Configure(main,
845                 conf_dir = joinpath(build_root, '.scons_config'),
846                 log_file = joinpath(build_root, 'scons_config.log'),
847                 custom_tests = {
848        'CheckLeading' : CheckLeading,
849        'CheckMember' : CheckMember,
850        })
851
852# Check for leading underscores.  Don't really need to worry either
853# way so don't need to check the return code.
854conf.CheckLeading()
855
856# Check if we should compile a 64 bit binary on Mac OS X/Darwin
857try:
858    import platform
859    uname = platform.uname()
860    if uname[0] == 'Darwin' and compareVersions(uname[2], '9.0.0') >= 0:
861        if int(readCommand('sysctl -n hw.cpu64bit_capable')[0]):
862            main.Append(CCFLAGS=['-arch', 'x86_64'])
863            main.Append(CFLAGS=['-arch', 'x86_64'])
864            main.Append(LINKFLAGS=['-arch', 'x86_64'])
865            main.Append(ASFLAGS=['-arch', 'x86_64'])
866except:
867    pass
868
869# Recent versions of scons substitute a "Null" object for Configure()
870# when configuration isn't necessary, e.g., if the "--help" option is
871# present.  Unfortuantely this Null object always returns false,
872# breaking all our configuration checks.  We replace it with our own
873# more optimistic null object that returns True instead.
874if not conf:
875    def NullCheck(*args, **kwargs):
876        return True
877
878    class NullConf:
879        def __init__(self, env):
880            self.env = env
881        def Finish(self):
882            return self.env
883        def __getattr__(self, mname):
884            return NullCheck
885
886    conf = NullConf(main)
887
888# Cache build files in the supplied directory.
889if main['M5_BUILD_CACHE']:
890    print 'Using build cache located at', main['M5_BUILD_CACHE']
891    CacheDir(main['M5_BUILD_CACHE'])
892
893if not GetOption('without_python'):
894    # Find Python include and library directories for embedding the
895    # interpreter. We rely on python-config to resolve the appropriate
896    # includes and linker flags. ParseConfig does not seem to understand
897    # the more exotic linker flags such as -Xlinker and -export-dynamic so
898    # we add them explicitly below. If you want to link in an alternate
899    # version of python, see above for instructions on how to invoke
900    # scons with the appropriate PATH set.
901    #
902    # First we check if python2-config exists, else we use python-config
903    python_config = readCommand(['which', 'python2-config'],
904                                exception='').strip()
905    if not os.path.exists(python_config):
906        python_config = readCommand(['which', 'python-config'],
907                                    exception='').strip()
908    py_includes = readCommand([python_config, '--includes'],
909                              exception='').split()
910    # Strip the -I from the include folders before adding them to the
911    # CPPPATH
912    main.Append(CPPPATH=map(lambda inc: inc[2:], py_includes))
913
914    # Read the linker flags and split them into libraries and other link
915    # flags. The libraries are added later through the call the CheckLib.
916    py_ld_flags = readCommand([python_config, '--ldflags'],
917        exception='').split()
918    py_libs = []
919    for lib in py_ld_flags:
920         if not lib.startswith('-l'):
921             main.Append(LINKFLAGS=[lib])
922         else:
923             lib = lib[2:]
924             if lib not in py_libs:
925                 py_libs.append(lib)
926
927    # verify that this stuff works
928    if not conf.CheckHeader('Python.h', '<>'):
929        print "Error: can't find Python.h header in", py_includes
930        print "Install Python headers (package python-dev on Ubuntu and RedHat)"
931        Exit(1)
932
933    for lib in py_libs:
934        if not conf.CheckLib(lib):
935            print "Error: can't find library %s required by python" % lib
936            Exit(1)
937
938# On Solaris you need to use libsocket for socket ops
939if not conf.CheckLibWithHeader(None, 'sys/socket.h', 'C++', 'accept(0,0,0);'):
940   if not conf.CheckLibWithHeader('socket', 'sys/socket.h', 'C++', 'accept(0,0,0);'):
941       print "Can't find library with socket calls (e.g. accept())"
942       Exit(1)
943
944# Check for zlib.  If the check passes, libz will be automatically
945# added to the LIBS environment variable.
946if not conf.CheckLibWithHeader('z', 'zlib.h', 'C++','zlibVersion();'):
947    print 'Error: did not find needed zlib compression library '\
948          'and/or zlib.h header file.'
949    print '       Please install zlib and try again.'
950    Exit(1)
951
952# If we have the protobuf compiler, also make sure we have the
953# development libraries. If the check passes, libprotobuf will be
954# automatically added to the LIBS environment variable. After
955# this, we can use the HAVE_PROTOBUF flag to determine if we have
956# got both protoc and libprotobuf available.
957main['HAVE_PROTOBUF'] = main['PROTOC'] and \
958    conf.CheckLibWithHeader('protobuf', 'google/protobuf/message.h',
959                            'C++', 'GOOGLE_PROTOBUF_VERIFY_VERSION;')
960
961# If we have the compiler but not the library, print another warning.
962if main['PROTOC'] and not main['HAVE_PROTOBUF']:
963    print termcap.Yellow + termcap.Bold + \
964        'Warning: did not find protocol buffer library and/or headers.\n' + \
965    '       Please install libprotobuf-dev for tracing support.' + \
966    termcap.Normal
967
968# Check for librt.
969have_posix_clock = \
970    conf.CheckLibWithHeader(None, 'time.h', 'C',
971                            'clock_nanosleep(0,0,NULL,NULL);') or \
972    conf.CheckLibWithHeader('rt', 'time.h', 'C',
973                            'clock_nanosleep(0,0,NULL,NULL);')
974
975have_posix_timers = \
976    conf.CheckLibWithHeader([None, 'rt'], [ 'time.h', 'signal.h' ], 'C',
977                            'timer_create(CLOCK_MONOTONIC, NULL, NULL);')
978
979if not GetOption('without_tcmalloc'):
980    if conf.CheckLib('tcmalloc'):
981        main.Append(CCFLAGS=main['TCMALLOC_CCFLAGS'])
982    elif conf.CheckLib('tcmalloc_minimal'):
983        main.Append(CCFLAGS=main['TCMALLOC_CCFLAGS'])
984    else:
985        print termcap.Yellow + termcap.Bold + \
986              "You can get a 12% performance improvement by "\
987              "installing tcmalloc (libgoogle-perftools-dev package "\
988              "on Ubuntu or RedHat)." + termcap.Normal
989
990if not have_posix_clock:
991    print "Can't find library for POSIX clocks."
992
993# Check for <fenv.h> (C99 FP environment control)
994have_fenv = conf.CheckHeader('fenv.h', '<>')
995if not have_fenv:
996    print "Warning: Header file <fenv.h> not found."
997    print "         This host has no IEEE FP rounding mode control."
998
999# Check if we should enable KVM-based hardware virtualization. The API
1000# we rely on exists since version 2.6.36 of the kernel, but somehow
1001# the KVM_API_VERSION does not reflect the change. We test for one of
1002# the types as a fall back.
1003have_kvm = conf.CheckHeader('linux/kvm.h', '<>') and \
1004    conf.CheckTypeSize('struct kvm_xsave', '#include <linux/kvm.h>') != 0
1005if not have_kvm:
1006    print "Info: Compatible header file <linux/kvm.h> not found, " \
1007        "disabling KVM support."
1008
1009# Check if the requested target ISA is compatible with the host
1010def is_isa_kvm_compatible(isa):
1011    isa_comp_table = {
1012        "arm" : ( "armv7l" ),
1013        "x86" : ( "x86_64" ),
1014        }
1015    try:
1016        import platform
1017        host_isa = platform.machine()
1018    except:
1019        print "Warning: Failed to determine host ISA."
1020        return False
1021
1022    return host_isa in isa_comp_table.get(isa, [])
1023
1024
1025# Check if the exclude_host attribute is available. We want this to
1026# get accurate instruction counts in KVM.
1027main['HAVE_PERF_ATTR_EXCLUDE_HOST'] = conf.CheckMember(
1028    'linux/perf_event.h', 'struct perf_event_attr', 'exclude_host')
1029
1030
1031######################################################################
1032#
1033# Finish the configuration
1034#
1035main = conf.Finish()
1036
1037######################################################################
1038#
1039# Collect all non-global variables
1040#
1041
1042# Define the universe of supported ISAs
1043all_isa_list = [ ]
1044Export('all_isa_list')
1045
1046class CpuModel(object):
1047    '''The CpuModel class encapsulates everything the ISA parser needs to
1048    know about a particular CPU model.'''
1049
1050    # Dict of available CPU model objects.  Accessible as CpuModel.dict.
1051    dict = {}
1052
1053    # Constructor.  Automatically adds models to CpuModel.dict.
1054    def __init__(self, name, default=False):
1055        self.name = name           # name of model
1056
1057        # This cpu is enabled by default
1058        self.default = default
1059
1060        # Add self to dict
1061        if name in CpuModel.dict:
1062            raise AttributeError, "CpuModel '%s' already registered" % name
1063        CpuModel.dict[name] = self
1064
1065Export('CpuModel')
1066
1067# Sticky variables get saved in the variables file so they persist from
1068# one invocation to the next (unless overridden, in which case the new
1069# value becomes sticky).
1070sticky_vars = Variables(args=ARGUMENTS)
1071Export('sticky_vars')
1072
1073# Sticky variables that should be exported
1074export_vars = []
1075Export('export_vars')
1076
1077# For Ruby
1078all_protocols = []
1079Export('all_protocols')
1080protocol_dirs = []
1081Export('protocol_dirs')
1082slicc_includes = []
1083Export('slicc_includes')
1084
1085# Walk the tree and execute all SConsopts scripts that wil add to the
1086# above variables
1087if GetOption('verbose'):
1088    print "Reading SConsopts"
1089for bdir in [ base_dir ] + extras_dir_list:
1090    if not isdir(bdir):
1091        print "Error: directory '%s' does not exist" % bdir
1092        Exit(1)
1093    for root, dirs, files in os.walk(bdir):
1094        if 'SConsopts' in files:
1095            if GetOption('verbose'):
1096                print "Reading", joinpath(root, 'SConsopts')
1097            SConscript(joinpath(root, 'SConsopts'))
1098
1099all_isa_list.sort()
1100
1101sticky_vars.AddVariables(
1102    EnumVariable('TARGET_ISA', 'Target ISA', 'alpha', all_isa_list),
1103    ListVariable('CPU_MODELS', 'CPU models',
1104                 sorted(n for n,m in CpuModel.dict.iteritems() if m.default),
1105                 sorted(CpuModel.dict.keys())),
1106    BoolVariable('EFENCE', 'Link with Electric Fence malloc debugger',
1107                 False),
1108    BoolVariable('SS_COMPATIBLE_FP',
1109                 'Make floating-point results compatible with SimpleScalar',
1110                 False),
1111    BoolVariable('USE_SSE2',
1112                 'Compile for SSE2 (-msse2) to get IEEE FP on x86 hosts',
1113                 False),
1114    BoolVariable('USE_POSIX_CLOCK', 'Use POSIX Clocks', have_posix_clock),
1115    BoolVariable('USE_FENV', 'Use <fenv.h> IEEE mode control', have_fenv),
1116    BoolVariable('CP_ANNOTATE', 'Enable critical path annotation capability', False),
1117    BoolVariable('USE_KVM', 'Enable hardware virtualized (KVM) CPU models', have_kvm),
1118    EnumVariable('PROTOCOL', 'Coherence protocol for Ruby', 'None',
1119                  all_protocols),
1120    )
1121
1122# These variables get exported to #defines in config/*.hh (see src/SConscript).
1123export_vars += ['USE_FENV', 'SS_COMPATIBLE_FP', 'TARGET_ISA', 'CP_ANNOTATE',
1124                'USE_POSIX_CLOCK', 'PROTOCOL', 'HAVE_PROTOBUF',
1125                'HAVE_PERF_ATTR_EXCLUDE_HOST']
1126
1127###################################################
1128#
1129# Define a SCons builder for configuration flag headers.
1130#
1131###################################################
1132
1133# This function generates a config header file that #defines the
1134# variable symbol to the current variable setting (0 or 1).  The source
1135# operands are the name of the variable and a Value node containing the
1136# value of the variable.
1137def build_config_file(target, source, env):
1138    (variable, value) = [s.get_contents() for s in source]
1139    f = file(str(target[0]), 'w')
1140    print >> f, '#define', variable, value
1141    f.close()
1142    return None
1143
1144# Combine the two functions into a scons Action object.
1145config_action = MakeAction(build_config_file, Transform("CONFIG H", 2))
1146
1147# The emitter munges the source & target node lists to reflect what
1148# we're really doing.
1149def config_emitter(target, source, env):
1150    # extract variable name from Builder arg
1151    variable = str(target[0])
1152    # True target is config header file
1153    target = joinpath('config', variable.lower() + '.hh')
1154    val = env[variable]
1155    if isinstance(val, bool):
1156        # Force value to 0/1
1157        val = int(val)
1158    elif isinstance(val, str):
1159        val = '"' + val + '"'
1160
1161    # Sources are variable name & value (packaged in SCons Value nodes)
1162    return ([target], [Value(variable), Value(val)])
1163
1164config_builder = Builder(emitter = config_emitter, action = config_action)
1165
1166main.Append(BUILDERS = { 'ConfigFile' : config_builder })
1167
1168# libelf build is shared across all configs in the build root.
1169main.SConscript('ext/libelf/SConscript',
1170                variant_dir = joinpath(build_root, 'libelf'))
1171
1172# gzstream build is shared across all configs in the build root.
1173main.SConscript('ext/gzstream/SConscript',
1174                variant_dir = joinpath(build_root, 'gzstream'))
1175
1176# libfdt build is shared across all configs in the build root.
1177main.SConscript('ext/libfdt/SConscript',
1178                variant_dir = joinpath(build_root, 'libfdt'))
1179
1180# fputils build is shared across all configs in the build root.
1181main.SConscript('ext/fputils/SConscript',
1182                variant_dir = joinpath(build_root, 'fputils'))
1183
1184# DRAMSim2 build is shared across all configs in the build root.
1185main.SConscript('ext/dramsim2/SConscript',
1186                variant_dir = joinpath(build_root, 'dramsim2'))
1187
1188# DRAMPower build is shared across all configs in the build root.
1189main.SConscript('ext/drampower/SConscript',
1190                variant_dir = joinpath(build_root, 'drampower'))
1191
1192###################################################
1193#
1194# This function is used to set up a directory with switching headers
1195#
1196###################################################
1197
1198main['ALL_ISA_LIST'] = all_isa_list
1199all_isa_deps = {}
1200def make_switching_dir(dname, switch_headers, env):
1201    # Generate the header.  target[0] is the full path of the output
1202    # header to generate.  'source' is a dummy variable, since we get the
1203    # list of ISAs from env['ALL_ISA_LIST'].
1204    def gen_switch_hdr(target, source, env):
1205        fname = str(target[0])
1206        isa = env['TARGET_ISA'].lower()
1207        try:
1208            f = open(fname, 'w')
1209            print >>f, '#include "%s/%s/%s"' % (dname, isa, basename(fname))
1210            f.close()
1211        except IOError:
1212            print "Failed to create %s" % fname
1213            raise
1214
1215    # Build SCons Action object. 'varlist' specifies env vars that this
1216    # action depends on; when env['ALL_ISA_LIST'] changes these actions
1217    # should get re-executed.
1218    switch_hdr_action = MakeAction(gen_switch_hdr,
1219                          Transform("GENERATE"), varlist=['ALL_ISA_LIST'])
1220
1221    # Instantiate actions for each header
1222    for hdr in switch_headers:
1223        env.Command(hdr, [], switch_hdr_action)
1224
1225    isa_target = Dir('.').up().name.lower().replace('_', '-')
1226    env['PHONY_BASE'] = '#'+isa_target
1227    all_isa_deps[isa_target] = None
1228
1229Export('make_switching_dir')
1230
1231# all-isas -> all-deps -> all-environs -> all_targets
1232main.Alias('#all-isas', [])
1233main.Alias('#all-deps', '#all-isas')
1234
1235# Dummy target to ensure all environments are created before telling
1236# SCons what to actually make (the command line arguments).  We attach
1237# them to the dependence graph after the environments are complete.
1238ORIG_BUILD_TARGETS = list(BUILD_TARGETS) # force a copy; gets closure to work.
1239def environsComplete(target, source, env):
1240    for t in ORIG_BUILD_TARGETS:
1241        main.Depends('#all-targets', t)
1242
1243# Each build/* switching_dir attaches its *-environs target to #all-environs.
1244main.Append(BUILDERS = {'CompleteEnvirons' :
1245                        Builder(action=MakeAction(environsComplete, None))})
1246main.CompleteEnvirons('#all-environs', [])
1247
1248def doNothing(**ignored): pass
1249main.Append(BUILDERS = {'Dummy': Builder(action=MakeAction(doNothing, None))})
1250
1251# The final target to which all the original targets ultimately get attached.
1252main.Dummy('#all-targets', '#all-environs')
1253BUILD_TARGETS[:] = ['#all-targets']
1254
1255###################################################
1256#
1257# Define build environments for selected configurations.
1258#
1259###################################################
1260
1261for variant_path in variant_paths:
1262    if not GetOption('silent'):
1263        print "Building in", variant_path
1264
1265    # Make a copy of the build-root environment to use for this config.
1266    env = main.Clone()
1267    env['BUILDDIR'] = variant_path
1268
1269    # variant_dir is the tail component of build path, and is used to
1270    # determine the build parameters (e.g., 'ALPHA_SE')
1271    (build_root, variant_dir) = splitpath(variant_path)
1272
1273    # Set env variables according to the build directory config.
1274    sticky_vars.files = []
1275    # Variables for $BUILD_ROOT/$VARIANT_DIR are stored in
1276    # $BUILD_ROOT/variables/$VARIANT_DIR so you can nuke
1277    # $BUILD_ROOT/$VARIANT_DIR without losing your variables settings.
1278    current_vars_file = joinpath(build_root, 'variables', variant_dir)
1279    if isfile(current_vars_file):
1280        sticky_vars.files.append(current_vars_file)
1281        if not GetOption('silent'):
1282            print "Using saved variables file %s" % current_vars_file
1283    else:
1284        # Build dir-specific variables file doesn't exist.
1285
1286        # Make sure the directory is there so we can create it later
1287        opt_dir = dirname(current_vars_file)
1288        if not isdir(opt_dir):
1289            mkdir(opt_dir)
1290
1291        # Get default build variables from source tree.  Variables are
1292        # normally determined by name of $VARIANT_DIR, but can be
1293        # overridden by '--default=' arg on command line.
1294        default = GetOption('default')
1295        opts_dir = joinpath(main.root.abspath, 'build_opts')
1296        if default:
1297            default_vars_files = [joinpath(build_root, 'variables', default),
1298                                  joinpath(opts_dir, default)]
1299        else:
1300            default_vars_files = [joinpath(opts_dir, variant_dir)]
1301        existing_files = filter(isfile, default_vars_files)
1302        if existing_files:
1303            default_vars_file = existing_files[0]
1304            sticky_vars.files.append(default_vars_file)
1305            print "Variables file %s not found,\n  using defaults in %s" \
1306                  % (current_vars_file, default_vars_file)
1307        else:
1308            print "Error: cannot find variables file %s or " \
1309                  "default file(s) %s" \
1310                  % (current_vars_file, ' or '.join(default_vars_files))
1311            Exit(1)
1312
1313    # Apply current variable settings to env
1314    sticky_vars.Update(env)
1315
1316    help_texts["local_vars"] += \
1317        "Build variables for %s:\n" % variant_dir \
1318                 + sticky_vars.GenerateHelpText(env)
1319
1320    # Process variable settings.
1321
1322    if not have_fenv and env['USE_FENV']:
1323        print "Warning: <fenv.h> not available; " \
1324              "forcing USE_FENV to False in", variant_dir + "."
1325        env['USE_FENV'] = False
1326
1327    if not env['USE_FENV']:
1328        print "Warning: No IEEE FP rounding mode control in", variant_dir + "."
1329        print "         FP results may deviate slightly from other platforms."
1330
1331    if env['EFENCE']:
1332        env.Append(LIBS=['efence'])
1333
1334    if env['USE_KVM']:
1335        if not have_kvm:
1336            print "Warning: Can not enable KVM, host seems to lack KVM support"
1337            env['USE_KVM'] = False
1338        elif not have_posix_timers:
1339            print "Warning: Can not enable KVM, host seems to lack support " \
1340                "for POSIX timers"
1341            env['USE_KVM'] = False
1342        elif not is_isa_kvm_compatible(env['TARGET_ISA']):
1343            print "Info: KVM support disabled due to unsupported host and " \
1344                "target ISA combination"
1345            env['USE_KVM'] = False
1346
1347    # Warn about missing optional functionality
1348    if env['USE_KVM']:
1349        if not main['HAVE_PERF_ATTR_EXCLUDE_HOST']:
1350            print "Warning: perf_event headers lack support for the " \
1351                "exclude_host attribute. KVM instruction counts will " \
1352                "be inaccurate."
1353
1354    # Save sticky variable settings back to current variables file
1355    sticky_vars.Save(current_vars_file, env)
1356
1357    if env['USE_SSE2']:
1358        env.Append(CCFLAGS=['-msse2'])
1359
1360    # The src/SConscript file sets up the build rules in 'env' according
1361    # to the configured variables.  It returns a list of environments,
1362    # one for each variant build (debug, opt, etc.)
1363    SConscript('src/SConscript', variant_dir = variant_path, exports = 'env')
1364
1365def pairwise(iterable):
1366    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
1367    a, b = itertools.tee(iterable)
1368    b.next()
1369    return itertools.izip(a, b)
1370
1371# Create false dependencies so SCons will parse ISAs, establish
1372# dependencies, and setup the build Environments serially. Either
1373# SCons (likely) and/or our SConscripts (possibly) cannot cope with -j
1374# greater than 1. It appears to be standard race condition stuff; it
1375# doesn't always fail, but usually, and the behaviors are different.
1376# Every time I tried to remove this, builds would fail in some
1377# creative new way. So, don't do that. You'll want to, though, because
1378# tests/SConscript takes a long time to make its Environments.
1379for t1, t2 in pairwise(sorted(all_isa_deps.iterkeys())):
1380    main.Depends('#%s-deps'     % t2, '#%s-deps'     % t1)
1381    main.Depends('#%s-environs' % t2, '#%s-environs' % t1)
1382
1383# base help text
1384Help('''
1385Usage: scons [scons options] [build variables] [target(s)]
1386
1387Extra scons options:
1388%(options)s
1389
1390Global build variables:
1391%(global_vars)s
1392
1393%(local_vars)s
1394''' % help_texts)
1395