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