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