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