SConstruct revision 6120:4dcea6c903fa
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
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(env.root.abspath, 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(py_getvar('LIBPL'))
561
562py_libs = []
563for lib in py_getvar('LIBS').split() + py_getvar('SYSLIBS').split():
564    assert lib.startswith('-l')
565    lib = lib[2:]   
566    if lib not in py_libs:
567        py_libs.append(lib)
568py_libs.append(py_version)
569
570env.Append(CPPPATH=py_includes)
571env.Append(LIBPATH=py_lib_path)
572
573# verify that this stuff works
574if not conf.CheckHeader('Python.h', '<>'):
575    print "Error: can't find Python.h header in", py_includes
576    Exit(1)
577
578for lib in py_libs:
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# Sticky variables that should be exported
658export_vars = []
659Export('export_vars')
660
661# Non-sticky variables only apply to the current build.
662nonsticky_vars = Variables(args=ARGUMENTS)
663Export('nonsticky_vars')
664
665# Walk the tree and execute all SConsopts scripts that wil add to the
666# above variables
667for bdir in [ base_dir ] + extras_dir_list:
668    for root, dirs, files in os.walk(bdir):
669        if 'SConsopts' in files:
670            print "Reading", joinpath(root, 'SConsopts')
671            SConscript(joinpath(root, 'SConsopts'))
672
673all_isa_list.sort()
674all_cpu_list.sort()
675default_cpus.sort()
676
677sticky_vars.AddVariables(
678    EnumVariable('TARGET_ISA', 'Target ISA', 'alpha', all_isa_list),
679    BoolVariable('FULL_SYSTEM', 'Full-system support', False),
680    ListVariable('CPU_MODELS', 'CPU models', default_cpus, all_cpu_list),
681    BoolVariable('NO_FAST_ALLOC', 'Disable fast object allocator', False),
682    BoolVariable('FAST_ALLOC_DEBUG', 'Enable fast object allocator debugging',
683                 False),
684    BoolVariable('FAST_ALLOC_STATS', 'Enable fast object allocator statistics',
685                 False),
686    BoolVariable('EFENCE', 'Link with Electric Fence malloc debugger',
687                 False),
688    BoolVariable('SS_COMPATIBLE_FP',
689                 'Make floating-point results compatible with SimpleScalar',
690                 False),
691    BoolVariable('USE_SSE2',
692                 'Compile for SSE2 (-msse2) to get IEEE FP on x86 hosts',
693                 False),
694    BoolVariable('USE_MYSQL', 'Use MySQL for stats output', have_mysql),
695    BoolVariable('USE_FENV', 'Use <fenv.h> IEEE mode control', have_fenv),
696    BoolVariable('USE_CHECKER', 'Use checker for detailed CPU models', False),
697    BoolVariable('CP_ANNOTATE', 'Enable critical path annotation capability', False),
698    )
699
700nonsticky_vars.AddVariables(
701    BoolVariable('update_ref', 'Update test reference outputs', False)
702    )
703
704# These variables get exported to #defines in config/*.hh (see src/SConscript).
705export_vars += ['FULL_SYSTEM', 'USE_FENV', 'USE_MYSQL',
706                'NO_FAST_ALLOC', 'FAST_ALLOC_DEBUG', 'FAST_ALLOC_STATS',
707                'SS_COMPATIBLE_FP', 'USE_CHECKER', 'TARGET_ISA', 'CP_ANNOTATE']
708
709###################################################
710#
711# Define a SCons builder for configuration flag headers.
712#
713###################################################
714
715# This function generates a config header file that #defines the
716# variable symbol to the current variable setting (0 or 1).  The source
717# operands are the name of the variable and a Value node containing the
718# value of the variable.
719def build_config_file(target, source, env):
720    (variable, value) = [s.get_contents() for s in source]
721    f = file(str(target[0]), 'w')
722    print >> f, '#define', variable, value
723    f.close()
724    return None
725
726# Generate the message to be printed when building the config file.
727def build_config_file_string(target, source, env):
728    (variable, value) = [s.get_contents() for s in source]
729    return "Defining %s as %s in %s." % (variable, value, target[0])
730
731# Combine the two functions into a scons Action object.
732config_action = Action(build_config_file, build_config_file_string)
733
734# The emitter munges the source & target node lists to reflect what
735# we're really doing.
736def config_emitter(target, source, env):
737    # extract variable name from Builder arg
738    variable = str(target[0])
739    # True target is config header file
740    target = joinpath('config', variable.lower() + '.hh')
741    val = env[variable]
742    if isinstance(val, bool):
743        # Force value to 0/1
744        val = int(val)
745    elif isinstance(val, str):
746        val = '"' + val + '"'
747
748    # Sources are variable name & value (packaged in SCons Value nodes)
749    return ([target], [Value(variable), Value(val)])
750
751config_builder = Builder(emitter = config_emitter, action = config_action)
752
753env.Append(BUILDERS = { 'ConfigFile' : config_builder })
754
755# libelf build is shared across all configs in the build root.
756env.SConscript('ext/libelf/SConscript',
757               variant_dir = joinpath(build_root, 'libelf'))
758
759# gzstream build is shared across all configs in the build root.
760env.SConscript('ext/gzstream/SConscript',
761               variant_dir = joinpath(build_root, 'gzstream'))
762
763###################################################
764#
765# This function is used to set up a directory with switching headers
766#
767###################################################
768
769env['ALL_ISA_LIST'] = all_isa_list
770def make_switching_dir(dname, switch_headers, env):
771    # Generate the header.  target[0] is the full path of the output
772    # header to generate.  'source' is a dummy variable, since we get the
773    # list of ISAs from env['ALL_ISA_LIST'].
774    def gen_switch_hdr(target, source, env):
775        fname = str(target[0])
776        bname = basename(fname)
777        f = open(fname, 'w')
778        f.write('#include "arch/isa_specific.hh"\n')
779        cond = '#if'
780        for isa in all_isa_list:
781            f.write('%s THE_ISA == %s_ISA\n#include "%s/%s/%s"\n'
782                    % (cond, isa.upper(), dname, isa, bname))
783            cond = '#elif'
784        f.write('#else\n#error "THE_ISA not set"\n#endif\n')
785        f.close()
786        return 0
787
788    # String to print when generating header
789    def gen_switch_hdr_string(target, source, env):
790        return "Generating switch header " + str(target[0])
791
792    # Build SCons Action object. 'varlist' specifies env vars that this
793    # action depends on; when env['ALL_ISA_LIST'] changes these actions
794    # should get re-executed.
795    switch_hdr_action = Action(gen_switch_hdr, gen_switch_hdr_string,
796                               varlist=['ALL_ISA_LIST'])
797
798    # Instantiate actions for each header
799    for hdr in switch_headers:
800        env.Command(hdr, [], switch_hdr_action)
801Export('make_switching_dir')
802
803###################################################
804#
805# Define build environments for selected configurations.
806#
807###################################################
808
809# rename base env
810base_env = env
811
812for variant_path in variant_paths:
813    print "Building in", variant_path
814
815    # Make a copy of the build-root environment to use for this config.
816    env = base_env.Clone()
817    env['BUILDDIR'] = variant_path
818
819    # variant_dir is the tail component of build path, and is used to
820    # determine the build parameters (e.g., 'ALPHA_SE')
821    (build_root, variant_dir) = splitpath(variant_path)
822
823    # Set env variables according to the build directory config.
824    sticky_vars.files = []
825    # Variables for $BUILD_ROOT/$VARIANT_DIR are stored in
826    # $BUILD_ROOT/variables/$VARIANT_DIR so you can nuke
827    # $BUILD_ROOT/$VARIANT_DIR without losing your variables settings.
828    current_vars_file = joinpath(build_root, 'variables', variant_dir)
829    if isfile(current_vars_file):
830        sticky_vars.files.append(current_vars_file)
831        print "Using saved variables file %s" % current_vars_file
832    else:
833        # Build dir-specific variables file doesn't exist.
834
835        # Make sure the directory is there so we can create it later
836        opt_dir = dirname(current_vars_file)
837        if not isdir(opt_dir):
838            mkdir(opt_dir)
839
840        # Get default build variables from source tree.  Variables are
841        # normally determined by name of $VARIANT_DIR, but can be
842        # overriden by 'default=' arg on command line.
843        default_vars_file = joinpath('build_opts',
844                                     ARGUMENTS.get('default', variant_dir))
845        if isfile(default_vars_file):
846            sticky_vars.files.append(default_vars_file)
847            print "Variables file %s not found,\n  using defaults in %s" \
848                  % (current_vars_file, default_vars_file)
849        else:
850            print "Error: cannot find variables file %s or %s" \
851                  % (current_vars_file, default_vars_file)
852            Exit(1)
853
854    # Apply current variable settings to env
855    sticky_vars.Update(env)
856    nonsticky_vars.Update(env)
857
858    help_text += "\nSticky variables for %s:\n" % variant_dir \
859                 + sticky_vars.GenerateHelpText(env) \
860                 + "\nNon-sticky variables for %s:\n" % variant_dir \
861                 + nonsticky_vars.GenerateHelpText(env)
862
863    # Process variable settings.
864
865    if not have_fenv and env['USE_FENV']:
866        print "Warning: <fenv.h> not available; " \
867              "forcing USE_FENV to False in", variant_dir + "."
868        env['USE_FENV'] = False
869
870    if not env['USE_FENV']:
871        print "Warning: No IEEE FP rounding mode control in", variant_dir + "."
872        print "         FP results may deviate slightly from other platforms."
873
874    if env['EFENCE']:
875        env.Append(LIBS=['efence'])
876
877    if env['USE_MYSQL']:
878        if not have_mysql:
879            print "Warning: MySQL not available; " \
880                  "forcing USE_MYSQL to False in", variant_dir + "."
881            env['USE_MYSQL'] = False
882        else:
883            print "Compiling in", variant_dir, "with MySQL support."
884            env.ParseConfig(mysql_config_libs)
885            env.ParseConfig(mysql_config_include)
886
887    # Save sticky variable settings back to current variables file
888    sticky_vars.Save(current_vars_file, env)
889
890    if env['USE_SSE2']:
891        env.Append(CCFLAGS='-msse2')
892
893    # The src/SConscript file sets up the build rules in 'env' according
894    # to the configured variables.  It returns a list of environments,
895    # one for each variant build (debug, opt, etc.)
896    envList = SConscript('src/SConscript', variant_dir = variant_path,
897                         exports = 'env')
898
899    # Set up the regression tests for each build.
900    for e in envList:
901        SConscript('tests/SConscript',
902                   variant_dir = joinpath(variant_path, 'tests', e.Label),
903                   exports = { 'env' : e }, duplicate = False)
904
905Help(help_text)
906