SConstruct revision 5863:f73e06bc8765
1# -*- mode:python -*-
2
3# Copyright (c) 2004-2005 The Regents of The University of Michigan
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions are
8# met: redistributions of source code must retain the above copyright
9# notice, this list of conditions and the following disclaimer;
10# redistributions in binary form must reproduce the above copyright
11# notice, this list of conditions and the following disclaimer in the
12# documentation and/or other materials provided with the distribution;
13# neither the name of the copyright holders nor the names of its
14# contributors may be used to endorse or promote products derived from
15# this software without specific prior written permission.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28#
29# Authors: Steve Reinhardt
30#          Nathan Binkert
31
32###################################################
33#
34# SCons top-level build description (SConstruct) file.
35#
36# While in this directory ('m5'), just type 'scons' to build the default
37# configuration (see below), or type 'scons build/<CONFIG>/<binary>'
38# to build some other configuration (e.g., 'build/ALPHA_FS/m5.opt' for
39# the optimized full-system version).
40#
41# You can build M5 in a different directory as long as there is a
42# 'build/<CONFIG>' somewhere along the target path.  The build system
43# expects that all configs under the same build directory are being
44# built for the same host system.
45#
46# Examples:
47#
48#   The following two commands are equivalent.  The '-u' option tells
49#   scons to search up the directory tree for this SConstruct file.
50#   % cd <path-to-src>/m5 ; scons build/ALPHA_FS/m5.debug
51#   % cd <path-to-src>/m5/build/ALPHA_FS; scons -u m5.debug
52#
53#   The following two commands are equivalent and demonstrate building
54#   in a directory outside of the source tree.  The '-C' option tells
55#   scons to chdir to the specified directory to find this SConstruct
56#   file.
57#   % cd <path-to-src>/m5 ; scons /local/foo/build/ALPHA_FS/m5.debug
58#   % cd /local/foo/build/ALPHA_FS; scons -C <path-to-src>/m5 m5.debug
59#
60# You can use 'scons -H' to print scons options.  If you're in this
61# 'm5' directory (or use -u or -C to tell scons where to find this
62# file), you can use 'scons -h' to print all the M5-specific build
63# options as well.
64#
65###################################################
66
67# Check for recent-enough Python and SCons versions.
68try:
69    # Really old versions of scons only take two options for the
70    # function, so check once without the revision and once with the
71    # revision, the first instance will fail for stuff other than
72    # 0.98, and the second will fail for 0.98.0
73    EnsureSConsVersion(0, 98)
74    EnsureSConsVersion(0, 98, 1)
75except SystemExit, e:
76    print """
77For more details, see:
78    http://m5sim.org/wiki/index.php/Compiling_M5
79"""
80    raise
81
82# We ensure the python version early because we have stuff that
83# requires python 2.4
84try:
85    EnsurePythonVersion(2, 4)
86except SystemExit, e:
87    print """
88You can use a non-default installation of the Python interpreter by
89either (1) rearranging your PATH so that scons finds the non-default
90'python' first or (2) explicitly invoking an alternative interpreter
91on the scons script.
92
93For more details, see:
94    http://m5sim.org/wiki/index.php/Using_a_non-default_Python_installation
95"""
96    raise
97
98import os
99import re
100import subprocess
101import sys
102
103from os import mkdir, environ
104from os.path import abspath, basename, dirname, expanduser, normpath
105from os.path import exists,  isdir, isfile
106from os.path import join as joinpath, split as splitpath
107
108import SCons
109
110def read_command(cmd):
111    """run the command cmd, read the results and return them
112    this is sorta like `cmd` in shell"""
113    from subprocess import Popen, PIPE, STDOUT
114    subp = Popen(cmd, shell=True, stdout=PIPE, stderr=STDOUT, close_fds=True)
115    return subp.communicate()[0]
116
117# helper function: compare arrays or strings of version numbers.
118# E.g., compare_version((1,3,25), (1,4,1)')
119# returns -1, 0, 1 if v1 is <, ==, > v2
120def compare_versions(v1, v2):
121    def make_version_list(v):
122        if isinstance(v, (list,tuple)):
123            return v
124        elif isinstance(v, str):
125            return map(lambda x: int(re.match('\d+', x).group()), v.split('.'))
126        else:
127            raise TypeError
128
129    v1 = make_version_list(v1)
130    v2 = make_version_list(v2)
131    # Compare corresponding elements of lists
132    for n1,n2 in zip(v1, v2):
133        if n1 < n2: return -1
134        if n1 > n2: return  1
135    # all corresponding values are equal... see if one has extra values
136    if len(v1) < len(v2): return -1
137    if len(v1) > len(v2): return  1
138    return 0
139
140# The absolute path to the current directory (where this file lives).
141ROOT = Dir('.').abspath
142
143# Path to the M5 source tree.
144SRCDIR = joinpath(ROOT, 'src')
145
146# tell python where to find m5 python code
147sys.path.append(joinpath(ROOT, 'src/python'))
148
149###################################################
150# Mercurial Stuff.
151# 1) Grab repository revision if we know it.
152# 2) Ensure that the style hook is in place.
153###################################################
154
155hg_info = "Unknown"
156try:
157    if not exists(ROOT) or not isdir(ROOT) or \
158           not exists(joinpath(ROOT, ".hg")):
159        raise ValueError(".hg directory not found")
160    hg_info = read_command("cd %s; hg id -n -i -t -b" % ROOT).strip()
161except ImportError, e:
162    print "Mercurial not found"
163except ValueError, e:
164    print e
165except Exception, e:
166    print "Other mercurial exception: %s" % e
167
168def check_style_hook(ui):
169    ui.readconfig(joinpath(ROOT, '.hg', 'hgrc'))
170    style_hook = ui.config('hooks', 'pretxncommit.style', None)
171
172    if not style_hook:
173        print """\
174You're missing the M5 style hook.
175Please install the hook so we can ensure that all code fits a common style.
176
177All you'd need to do is add the following lines to your repository .hg/hgrc
178or your personal .hgrc
179----------------
180
181[extensions]
182style = %s/util/style.py
183
184[hooks]
185pretxncommit.style = python:style.check_whitespace
186""" % (ROOT)
187        sys.exit(1)
188
189if ARGUMENTS.get('IGNORE_STYLE') != 'True' and isdir(joinpath(ROOT, '.hg')):
190    try:
191        from mercurial import ui
192        check_style_hook(ui.ui())
193    except ImportError:
194        pass
195
196
197###################################################
198#
199# Figure out which configurations to set up based on the path(s) of
200# the target(s).
201#
202###################################################
203
204# Find default configuration & binary.
205Default(environ.get('M5_DEFAULT_BINARY', 'build/ALPHA_SE/m5.debug'))
206
207# helper function: find last occurrence of element in list
208def rfind(l, elt, offs = -1):
209    for i in range(len(l)+offs, 0, -1):
210        if l[i] == elt:
211            return i
212    raise ValueError, "element not found"
213
214# Each target must have 'build' in the interior of the path; the
215# directory below this will determine the build parameters.  For
216# example, for target 'foo/bar/build/ALPHA_SE/arch/alpha/blah.do' we
217# recognize that ALPHA_SE specifies the configuration because it
218# follow 'build' in the bulid path.
219
220# Generate absolute paths to targets so we can see where the build dir is
221if COMMAND_LINE_TARGETS:
222    # Ask SCons which directory it was invoked from
223    launch_dir = GetLaunchDir()
224    # Make targets relative to invocation directory
225    abs_targets = [ normpath(joinpath(launch_dir, str(x))) for x in \
226                    COMMAND_LINE_TARGETS]
227else:
228    # Default targets are relative to root of tree
229    abs_targets = [ normpath(joinpath(ROOT, str(x))) for x in \
230                    DEFAULT_TARGETS]
231
232
233# Generate a list of the unique build roots and configs that the
234# collected targets reference.
235variant_paths = []
236build_root = None
237for t in abs_targets:
238    path_dirs = t.split('/')
239    try:
240        build_top = rfind(path_dirs, 'build', -2)
241    except:
242        print "Error: no non-leaf 'build' dir found on target path", t
243        Exit(1)
244    this_build_root = joinpath('/',*path_dirs[:build_top+1])
245    if not build_root:
246        build_root = this_build_root
247    else:
248        if this_build_root != build_root:
249            print "Error: build targets not under same build root\n"\
250                  "  %s\n  %s" % (build_root, this_build_root)
251            Exit(1)
252    variant_path = joinpath('/',*path_dirs[:build_top+2])
253    if variant_path not in variant_paths:
254        variant_paths.append(variant_path)
255
256# Make sure build_root exists (might not if this is the first build there)
257if not isdir(build_root):
258    mkdir(build_root)
259
260###################################################
261#
262# Set up the default build environment.  This environment is copied
263# and modified according to each selected configuration.
264#
265###################################################
266
267env = Environment(ENV = environ,  # inherit user's environment vars
268                  ROOT = ROOT,
269                  SRCDIR = SRCDIR,
270                  HG_INFO = hg_info)
271
272Export('env')
273
274env.SConsignFile(joinpath(build_root, "sconsign"))
275
276# Default duplicate option is to use hard links, but this messes up
277# when you use emacs to edit a file in the target dir, as emacs moves
278# file to file~ then copies to file, breaking the link.  Symbolic
279# (soft) links work better.
280env.SetOption('duplicate', 'soft-copy')
281
282#
283# Set up global sticky variables... these are common to an entire build
284# tree (not specific to a particular build like ALPHA_SE)
285#
286
287# Variable validators & converters for global sticky variables
288def PathListMakeAbsolute(val):
289    if not val:
290        return val
291    f = lambda p: abspath(expanduser(p))
292    return ':'.join(map(f, val.split(':')))
293
294def PathListAllExist(key, val, env):
295    if not val:
296        return
297    paths = val.split(':')
298    for path in paths:
299        if not isdir(path):
300            raise SCons.Errors.UserError("Path does not exist: '%s'" % path)
301
302global_sticky_vars_file = joinpath(build_root, 'variables.global')
303
304global_sticky_vars = Variables(global_sticky_vars_file, args=ARGUMENTS)
305
306global_sticky_vars.AddVariables(
307    ('CC', 'C compiler', environ.get('CC', env['CC'])),
308    ('CXX', 'C++ compiler', environ.get('CXX', env['CXX'])),
309    ('BATCH', 'Use batch pool for build and tests', False),
310    ('BATCH_CMD', 'Batch pool submission command name', 'qdo'),
311    ('EXTRAS', 'Add Extra directories to the compilation', '',
312     PathListAllExist, PathListMakeAbsolute)
313    )    
314
315# base help text
316help_text = '''
317Usage: scons [scons options] [build options] [target(s)]
318
319Global sticky options:
320'''
321
322help_text += global_sticky_vars.GenerateHelpText(env)
323
324# Update env with values from ARGUMENTS & file global_sticky_vars_file
325global_sticky_vars.Update(env)
326
327# Save sticky variable settings back to current variables file
328global_sticky_vars.Save(global_sticky_vars_file, env)
329
330# Parse EXTRAS variable to build list of all directories where we're
331# look for sources etc.  This list is exported as base_dir_list.
332base_dir = joinpath(ROOT, 'src')
333if env['EXTRAS']:
334    extras_dir_list = env['EXTRAS'].split(':')
335else:
336    extras_dir_list = []
337
338Export('base_dir')
339Export('extras_dir_list')
340
341# M5_PLY is used by isa_parser.py to find the PLY package.
342env.Append(ENV = { 'M5_PLY' : str(Dir('ext/ply')) })
343env['GCC'] = read_command(env['CXX'] + ' --version').find('g++') >= 0
344env['SUNCC'] = read_command(env['CXX'] + ' -V').find('Sun C++') >= 0
345env['ICC'] = read_command(env['CXX'] + ' -V').find('Intel') >= 0
346if env['GCC'] + env['SUNCC'] + env['ICC'] > 1:
347    print 'Error: How can we have two at the same time?'
348    Exit(1)
349
350# Set up default C++ compiler flags
351if env['GCC']:
352    env.Append(CCFLAGS='-pipe')
353    env.Append(CCFLAGS='-fno-strict-aliasing')
354    env.Append(CCFLAGS=Split('-Wall -Wno-sign-compare -Werror -Wundef'))
355    env.Append(CXXFLAGS='-Wno-deprecated')
356elif env['ICC']:
357    pass #Fix me... add warning flags once we clean up icc warnings
358elif env['SUNCC']:
359    env.Append(CCFLAGS='-Qoption ccfe')
360    env.Append(CCFLAGS='-features=gcc')
361    env.Append(CCFLAGS='-features=extensions')
362    env.Append(CCFLAGS='-library=stlport4')
363    env.Append(CCFLAGS='-xar')
364#    env.Append(CCFLAGS='-instances=semiexplicit')
365else:
366    print 'Error: Don\'t know what compiler options to use for your compiler.'
367    print '       Please fix SConstruct and src/SConscript and try again.'
368    Exit(1)
369
370# Do this after we save setting back, or else we'll tack on an
371# extra 'qdo' every time we run scons.
372if env['BATCH']:
373    env['CC']     = env['BATCH_CMD'] + ' ' + env['CC']
374    env['CXX']    = env['BATCH_CMD'] + ' ' + env['CXX']
375    env['AS']     = env['BATCH_CMD'] + ' ' + env['AS']
376    env['AR']     = env['BATCH_CMD'] + ' ' + env['AR']
377    env['RANLIB'] = env['BATCH_CMD'] + ' ' + env['RANLIB']
378
379if sys.platform == 'cygwin':
380    # cygwin has some header file issues...
381    env.Append(CCFLAGS=Split("-Wno-uninitialized"))
382env.Append(CPPPATH=[Dir('ext/dnet')])
383
384# Check for SWIG
385if not env.has_key('SWIG'):
386    print 'Error: SWIG utility not found.'
387    print '       Please install (see http://www.swig.org) and retry.'
388    Exit(1)
389
390# Check for appropriate SWIG version
391swig_version = read_command('swig -version').split()
392# First 3 words should be "SWIG Version x.y.z"
393if len(swig_version) < 3 or \
394        swig_version[0] != 'SWIG' or swig_version[1] != 'Version':
395    print 'Error determining SWIG version.'
396    Exit(1)
397
398min_swig_version = '1.3.28'
399if compare_versions(swig_version[2], min_swig_version) < 0:
400    print 'Error: SWIG version', min_swig_version, 'or newer required.'
401    print '       Installed version:', swig_version[2]
402    Exit(1)
403
404# Set up SWIG flags & scanner
405swig_flags=Split('-c++ -python -modern -templatereduce $_CPPINCFLAGS')
406env.Append(SWIGFLAGS=swig_flags)
407
408# filter out all existing swig scanners, they mess up the dependency
409# stuff for some reason
410scanners = []
411for scanner in env['SCANNERS']:
412    skeys = scanner.skeys
413    if skeys == '.i':
414        continue
415
416    if isinstance(skeys, (list, tuple)) and '.i' in skeys:
417        continue
418
419    scanners.append(scanner)
420
421# add the new swig scanner that we like better
422from SCons.Scanner import ClassicCPP as CPPScanner
423swig_inc_re = '^[ \t]*[%,#][ \t]*(?:include|import)[ \t]*(<|")([^>"]+)(>|")'
424scanners.append(CPPScanner("SwigScan", [ ".i" ], "CPPPATH", swig_inc_re))
425
426# replace the scanners list that has what we want
427env['SCANNERS'] = scanners
428
429# Add a custom Check function to the Configure context so that we can
430# figure out if the compiler adds leading underscores to global
431# variables.  This is needed for the autogenerated asm files that we
432# use for embedding the python code.
433def CheckLeading(context):
434    context.Message("Checking for leading underscore in global variables...")
435    # 1) Define a global variable called x from asm so the C compiler
436    #    won't change the symbol at all.
437    # 2) Declare that variable.
438    # 3) Use the variable
439    #
440    # If the compiler prepends an underscore, this will successfully
441    # link because the external symbol 'x' will be called '_x' which
442    # was defined by the asm statement.  If the compiler does not
443    # prepend an underscore, this will not successfully link because
444    # '_x' will have been defined by assembly, while the C portion of
445    # the code will be trying to use 'x'
446    ret = context.TryLink('''
447        asm(".globl _x; _x: .byte 0");
448        extern int x;
449        int main() { return x; }
450        ''', extension=".c")
451    context.env.Append(LEADING_UNDERSCORE=ret)
452    context.Result(ret)
453    return ret
454
455# Platform-specific configuration.  Note again that we assume that all
456# builds under a given build root run on the same host platform.
457conf = Configure(env,
458                 conf_dir = joinpath(build_root, '.scons_config'),
459                 log_file = joinpath(build_root, 'scons_config.log'),
460                 custom_tests = { 'CheckLeading' : CheckLeading })
461
462# Check for leading underscores.  Don't really need to worry either
463# way so don't need to check the return code.
464conf.CheckLeading()
465
466# Check if we should compile a 64 bit binary on Mac OS X/Darwin
467try:
468    import platform
469    uname = platform.uname()
470    if uname[0] == 'Darwin' and compare_versions(uname[2], '9.0.0') >= 0:
471        if int(read_command('sysctl -n hw.cpu64bit_capable')[0]):
472            env.Append(CCFLAGS='-arch x86_64')
473            env.Append(CFLAGS='-arch x86_64')
474            env.Append(LINKFLAGS='-arch x86_64')
475            env.Append(ASFLAGS='-arch x86_64')
476except:
477    pass
478
479# Recent versions of scons substitute a "Null" object for Configure()
480# when configuration isn't necessary, e.g., if the "--help" option is
481# present.  Unfortuantely this Null object always returns false,
482# breaking all our configuration checks.  We replace it with our own
483# more optimistic null object that returns True instead.
484if not conf:
485    def NullCheck(*args, **kwargs):
486        return True
487
488    class NullConf:
489        def __init__(self, env):
490            self.env = env
491        def Finish(self):
492            return self.env
493        def __getattr__(self, mname):
494            return NullCheck
495
496    conf = NullConf(env)
497
498# Find Python include and library directories for embedding the
499# interpreter.  For consistency, we will use the same Python
500# installation used to run scons (and thus this script).  If you want
501# to link in an alternate version, see above for instructions on how
502# to invoke scons with a different copy of the Python interpreter.
503from distutils import sysconfig
504
505py_getvar = sysconfig.get_config_var
506
507py_version = 'python' + py_getvar('VERSION')
508
509py_general_include = sysconfig.get_python_inc()
510py_platform_include = sysconfig.get_python_inc(plat_specific=True)
511py_includes = [ py_general_include ]
512if py_platform_include != py_general_include:
513    py_includes.append(py_platform_include)
514
515py_lib_path = []
516# add the prefix/lib/pythonX.Y/config dir, but only if there is no
517# shared library in prefix/lib/.
518if not py_getvar('Py_ENABLE_SHARED'):
519    py_lib_path.append('-L' + py_getvar('LIBPL'))
520
521py_libs = []
522for lib in py_getvar('LIBS').split() + py_getvar('SYSLIBS').split():
523    if lib not in py_libs:
524        py_libs.append(lib)
525py_libs.append('-l' + py_version)
526
527env.Append(CPPPATH=py_includes)
528env.Append(LIBPATH=py_lib_path)
529#env.Append(LIBS=py_libs)
530
531# verify that this stuff works
532if not conf.CheckHeader('Python.h', '<>'):
533    print "Error: can't find Python.h header in", py_includes
534    Exit(1)
535
536for lib in py_libs:
537    assert lib.startswith('-l')
538    lib = lib[2:]
539    if not conf.CheckLib(lib):
540        print "Error: can't find library %s required by python" % lib
541        Exit(1)
542
543# On Solaris you need to use libsocket for socket ops
544if not conf.CheckLibWithHeader(None, 'sys/socket.h', 'C++', 'accept(0,0,0);'):
545   if not conf.CheckLibWithHeader('socket', 'sys/socket.h', 'C++', 'accept(0,0,0);'):
546       print "Can't find library with socket calls (e.g. accept())"
547       Exit(1)
548
549# Check for zlib.  If the check passes, libz will be automatically
550# added to the LIBS environment variable.
551if not conf.CheckLibWithHeader('z', 'zlib.h', 'C++','zlibVersion();'):
552    print 'Error: did not find needed zlib compression library '\
553          'and/or zlib.h header file.'
554    print '       Please install zlib and try again.'
555    Exit(1)
556
557# Check for <fenv.h> (C99 FP environment control)
558have_fenv = conf.CheckHeader('fenv.h', '<>')
559if not have_fenv:
560    print "Warning: Header file <fenv.h> not found."
561    print "         This host has no IEEE FP rounding mode control."
562
563######################################################################
564#
565# Check for mysql.
566#
567mysql_config = WhereIs('mysql_config')
568have_mysql = bool(mysql_config)
569
570# Check MySQL version.
571if have_mysql:
572    mysql_version = read_command(mysql_config + ' --version')
573    min_mysql_version = '4.1'
574    if compare_versions(mysql_version, min_mysql_version) < 0:
575        print 'Warning: MySQL', min_mysql_version, 'or newer required.'
576        print '         Version', mysql_version, 'detected.'
577        have_mysql = False
578
579# Set up mysql_config commands.
580if have_mysql:
581    mysql_config_include = mysql_config + ' --include'
582    if os.system(mysql_config_include + ' > /dev/null') != 0:
583        # older mysql_config versions don't support --include, use
584        # --cflags instead
585        mysql_config_include = mysql_config + ' --cflags | sed s/\\\'//g'
586    # This seems to work in all versions
587    mysql_config_libs = mysql_config + ' --libs'
588
589######################################################################
590#
591# Finish the configuration
592#
593env = conf.Finish()
594
595######################################################################
596#
597# Collect all non-global variables
598#
599
600# Define the universe of supported ISAs
601all_isa_list = [ ]
602Export('all_isa_list')
603
604# Define the universe of supported CPU models
605all_cpu_list = [ ]
606default_cpus = [ ]
607Export('all_cpu_list', 'default_cpus')
608
609# Sticky variables get saved in the variables file so they persist from
610# one invocation to the next (unless overridden, in which case the new
611# value becomes sticky).
612sticky_vars = Variables(args=ARGUMENTS)
613Export('sticky_vars')
614
615# Non-sticky variables only apply to the current build.
616nonsticky_vars = Variables(args=ARGUMENTS)
617Export('nonsticky_vars')
618
619# Walk the tree and execute all SConsopts scripts that wil add to the
620# above variables
621for bdir in [ base_dir ] + extras_dir_list:
622    for root, dirs, files in os.walk(bdir):
623        if 'SConsopts' in files:
624            print "Reading", joinpath(root, 'SConsopts')
625            SConscript(joinpath(root, 'SConsopts'))
626
627all_isa_list.sort()
628all_cpu_list.sort()
629default_cpus.sort()
630
631sticky_vars.AddVariables(
632    EnumVariable('TARGET_ISA', 'Target ISA', 'alpha', all_isa_list),
633    BoolVariable('FULL_SYSTEM', 'Full-system support', False),
634    ListVariable('CPU_MODELS', 'CPU models', default_cpus, all_cpu_list),
635    BoolVariable('NO_FAST_ALLOC', 'Disable fast object allocator', False),
636    BoolVariable('FAST_ALLOC_DEBUG', 'Enable fast object allocator debugging',
637                 False),
638    BoolVariable('FAST_ALLOC_STATS', 'Enable fast object allocator statistics',
639                 False),
640    BoolVariable('EFENCE', 'Link with Electric Fence malloc debugger',
641                 False),
642    BoolVariable('SS_COMPATIBLE_FP',
643                 'Make floating-point results compatible with SimpleScalar',
644                 False),
645    BoolVariable('USE_SSE2',
646                 'Compile for SSE2 (-msse2) to get IEEE FP on x86 hosts',
647                 False),
648    BoolVariable('USE_MYSQL', 'Use MySQL for stats output', have_mysql),
649    BoolVariable('USE_FENV', 'Use <fenv.h> IEEE mode control', have_fenv),
650    BoolVariable('USE_CHECKER', 'Use checker for detailed CPU models', False),
651    )
652
653nonsticky_vars.AddVariables(
654    BoolVariable('update_ref', 'Update test reference outputs', False)
655    )
656
657# These variables get exported to #defines in config/*.hh (see src/SConscript).
658env.ExportVariables = ['FULL_SYSTEM', 'ALPHA_TLASER', 'USE_FENV', \
659                       'USE_MYSQL', 'NO_FAST_ALLOC', 'FAST_ALLOC_DEBUG', \
660                       'FAST_ALLOC_STATS', 'SS_COMPATIBLE_FP', \
661                       'USE_CHECKER', 'TARGET_ISA']
662
663###################################################
664#
665# Define a SCons builder for configuration flag headers.
666#
667###################################################
668
669# This function generates a config header file that #defines the
670# variable symbol to the current variable setting (0 or 1).  The source
671# operands are the name of the variable and a Value node containing the
672# value of the variable.
673def build_config_file(target, source, env):
674    (variable, value) = [s.get_contents() for s in source]
675    f = file(str(target[0]), 'w')
676    print >> f, '#define', variable, value
677    f.close()
678    return None
679
680# Generate the message to be printed when building the config file.
681def build_config_file_string(target, source, env):
682    (variable, value) = [s.get_contents() for s in source]
683    return "Defining %s as %s in %s." % (variable, value, target[0])
684
685# Combine the two functions into a scons Action object.
686config_action = Action(build_config_file, build_config_file_string)
687
688# The emitter munges the source & target node lists to reflect what
689# we're really doing.
690def config_emitter(target, source, env):
691    # extract variable name from Builder arg
692    variable = str(target[0])
693    # True target is config header file
694    target = joinpath('config', variable.lower() + '.hh')
695    val = env[variable]
696    if isinstance(val, bool):
697        # Force value to 0/1
698        val = int(val)
699    elif isinstance(val, str):
700        val = '"' + val + '"'
701
702    # Sources are variable name & value (packaged in SCons Value nodes)
703    return ([target], [Value(variable), Value(val)])
704
705config_builder = Builder(emitter = config_emitter, action = config_action)
706
707env.Append(BUILDERS = { 'ConfigFile' : config_builder })
708
709# libelf build is shared across all configs in the build root.
710env.SConscript('ext/libelf/SConscript',
711               variant_dir = joinpath(build_root, 'libelf'),
712               exports = 'env')
713
714# gzstream build is shared across all configs in the build root.
715env.SConscript('ext/gzstream/SConscript',
716               variant_dir = joinpath(build_root, 'gzstream'),
717               exports = 'env')
718
719###################################################
720#
721# This function is used to set up a directory with switching headers
722#
723###################################################
724
725env['ALL_ISA_LIST'] = all_isa_list
726def make_switching_dir(dname, switch_headers, env):
727    # Generate the header.  target[0] is the full path of the output
728    # header to generate.  'source' is a dummy variable, since we get the
729    # list of ISAs from env['ALL_ISA_LIST'].
730    def gen_switch_hdr(target, source, env):
731        fname = str(target[0])
732        bname = basename(fname)
733        f = open(fname, 'w')
734        f.write('#include "arch/isa_specific.hh"\n')
735        cond = '#if'
736        for isa in all_isa_list:
737            f.write('%s THE_ISA == %s_ISA\n#include "%s/%s/%s"\n'
738                    % (cond, isa.upper(), dname, isa, bname))
739            cond = '#elif'
740        f.write('#else\n#error "THE_ISA not set"\n#endif\n')
741        f.close()
742        return 0
743
744    # String to print when generating header
745    def gen_switch_hdr_string(target, source, env):
746        return "Generating switch header " + str(target[0])
747
748    # Build SCons Action object. 'varlist' specifies env vars that this
749    # action depends on; when env['ALL_ISA_LIST'] changes these actions
750    # should get re-executed.
751    switch_hdr_action = Action(gen_switch_hdr, gen_switch_hdr_string,
752                               varlist=['ALL_ISA_LIST'])
753
754    # Instantiate actions for each header
755    for hdr in switch_headers:
756        env.Command(hdr, [], switch_hdr_action)
757Export('make_switching_dir')
758
759###################################################
760#
761# Define build environments for selected configurations.
762#
763###################################################
764
765# rename base env
766base_env = env
767
768for variant_path in variant_paths:
769    print "Building in", variant_path
770
771    # Make a copy of the build-root environment to use for this config.
772    env = base_env.Clone()
773    env['BUILDDIR'] = variant_path
774
775    # variant_dir is the tail component of build path, and is used to
776    # determine the build parameters (e.g., 'ALPHA_SE')
777    (build_root, variant_dir) = splitpath(variant_path)
778
779    # Set env variables according to the build directory config.
780    sticky_vars.files = []
781    # Variables for $BUILD_ROOT/$VARIANT_DIR are stored in
782    # $BUILD_ROOT/variables/$VARIANT_DIR so you can nuke
783    # $BUILD_ROOT/$VARIANT_DIR without losing your variables settings.
784    current_vars_file = joinpath(build_root, 'variables', variant_dir)
785    if isfile(current_vars_file):
786        sticky_vars.files.append(current_vars_file)
787        print "Using saved variables file %s" % current_vars_file
788    else:
789        # Build dir-specific variables file doesn't exist.
790
791        # Make sure the directory is there so we can create it later
792        opt_dir = dirname(current_vars_file)
793        if not isdir(opt_dir):
794            mkdir(opt_dir)
795
796        # Get default build variables from source tree.  Variables are
797        # normally determined by name of $VARIANT_DIR, but can be
798        # overriden by 'default=' arg on command line.
799        default_vars_file = joinpath('build_opts',
800                                     ARGUMENTS.get('default', variant_dir))
801        if isfile(default_vars_file):
802            sticky_vars.files.append(default_vars_file)
803            print "Variables file %s not found,\n  using defaults in %s" \
804                  % (current_vars_file, default_vars_file)
805        else:
806            print "Error: cannot find variables file %s or %s" \
807                  % (current_vars_file, default_vars_file)
808            Exit(1)
809
810    # Apply current variable settings to env
811    sticky_vars.Update(env)
812    nonsticky_vars.Update(env)
813
814    help_text += "\nSticky variables for %s:\n" % variant_dir \
815                 + sticky_vars.GenerateHelpText(env) \
816                 + "\nNon-sticky variables for %s:\n" % variant_dir \
817                 + nonsticky_vars.GenerateHelpText(env)
818
819    # Process variable settings.
820
821    if not have_fenv and env['USE_FENV']:
822        print "Warning: <fenv.h> not available; " \
823              "forcing USE_FENV to False in", variant_dir + "."
824        env['USE_FENV'] = False
825
826    if not env['USE_FENV']:
827        print "Warning: No IEEE FP rounding mode control in", variant_dir + "."
828        print "         FP results may deviate slightly from other platforms."
829
830    if env['EFENCE']:
831        env.Append(LIBS=['efence'])
832
833    if env['USE_MYSQL']:
834        if not have_mysql:
835            print "Warning: MySQL not available; " \
836                  "forcing USE_MYSQL to False in", variant_dir + "."
837            env['USE_MYSQL'] = False
838        else:
839            print "Compiling in", variant_dir, "with MySQL support."
840            env.ParseConfig(mysql_config_libs)
841            env.ParseConfig(mysql_config_include)
842
843    # Save sticky variable settings back to current variables file
844    sticky_vars.Save(current_vars_file, env)
845
846    if env['USE_SSE2']:
847        env.Append(CCFLAGS='-msse2')
848
849    # The src/SConscript file sets up the build rules in 'env' according
850    # to the configured variables.  It returns a list of environments,
851    # one for each variant build (debug, opt, etc.)
852    envList = SConscript('src/SConscript', variant_dir = variant_path,
853                         exports = 'env')
854
855    # Set up the regression tests for each build.
856    for e in envList:
857        SConscript('tests/SConscript',
858                   variant_dir = joinpath(variant_path, 'tests', e.Label),
859                   exports = { 'env' : e }, duplicate = False)
860
861Help(help_text)
862
863
864###################################################
865#
866# Let SCons do its thing.  At this point SCons will use the defined
867# build environments to build the requested targets.
868#
869###################################################
870
871