#Copyright (c) 2009-2012 XORP, Inc and Others
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, Version 2, June
# 1991 as published by the Free Software Foundation. Redistribution
# and/or modification of this program under the terms of any other
# version of the GNU General Public License is not permitted.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For more details,
# see the GNU General Public License, Version 2, a copy of which can be
# found in the XORP LICENSE.gpl file.
#
# XORP Inc, 2953 Bunker Hill Lane, Suite 204, Santa Clara, CA 95054, USA;
# http://xorp.net

# $ID$

# TODO cross compiles.
# TODO Fix default include/lib paths, pass in from environment.
# TODO Merge scons-unfamiliar syntactic sugar from YHC's sconsfiles.

import relpath

gnutoolwarning = """
WARNING: The GNU %s was not detected on your system.
Some combinations of linker or compiler flags, specific to building XORP,
may not function correctly.
"""

# The XRL tgt-gen and clnt-gen scripts use Python 2.3+'s optparse
# class. However, os.path.relpath() requires Python 2.6.
EnsurePythonVersion(2, 3)

Help("""
    builddir=<some path> to specify a different build directory.
        Default is "obj/<arch>-<os>-<rel>".
""")

import sys
import os
import shutil
import string
import subprocess
import fnmatch

import SCons
from SCons.Script.SConscript import SConsEnvironment
import SCons.Action
import SCons.Builder

try:
    # SCons 0.98.4 is the earliest release that we have tested.  Earlier
    # ones may work.  If so, please submit a Trac issue so the check can
    # be relaxed.
    EnsureSConsVersion(0, 98, 4)
except SystemExit:
    print "WARNING:  Actually, SCONS version 0.98.4 or later is _preferred_."
    print "Attempting to continue with version: " + SCons.__version__ + " but it may not work properly.\n"


vars = Variables()

vars.AddVariables(
    BoolVariable('shared', 'Build with shared libraries', True),
    BoolVariable('strip',  'Strip all installed binaries', False),
    BoolVariable('rtld_origin', 'Use ORIGIN in dynamically linked programs', True),
    BoolVariable('ignore_check_errors', 'Ignore errors when building tests', False),
    BoolVariable('debug_stl',  'Build with checked STL operations', False),
    BoolVariable('debug_msg',  'Build with debug messages', False),
    BoolVariable('debug_fn',  'Build with function names in debug_msg output', False),
    BoolVariable('debug_cb',  'Build with callback debugging', False),
    BoolVariable('disable_ipv6',  'Force disable of IPv6', False),
    BoolVariable('disable_libtecla',  'Use some externally compiled libtecla instead.', False),
    BoolVariable('disable_fw',  'Disable Firewall feature', False),
    BoolVariable('disable_warninglogs',  'Force disable warning logs', False),
    BoolVariable('disable_tracelogs',  'Force disable trace logs', False),
    BoolVariable('disable_fatallogs',  'Force disable fatal logs', False),
    BoolVariable('disable_infologs',  'Force disable info logs', False),
    BoolVariable('disable_assertlogs',  'Force disable assert logs', False),
    BoolVariable('disable_errorlogs',  'Force disable error logs', False),
    BoolVariable('disable_otherlogs',  'Force disable other logs', False),
    BoolVariable('disable_profile',  'Disable Xorp Profiler feature', False),
    BoolVariable('disable_werror',  'Disable -Werror compiler flag', False),
    BoolVariable('disable_assert',  'Force disabling assertions - use with caution', False),
    BoolVariable('enable_lex_hack',  'Works around lex/yacc issues on FreeBSD', False),
    BoolVariable('enable_builddirrun',  'Enable Xorp to run from BUILD_DIR', False),
    BoolVariable('enable_boost',  'Use BOOST', False),
    BoolVariable('enable_ustl',  'Use uSTL', False),
    BoolVariable('enable_bgp',  'Build BGP', True),
    BoolVariable('enable_buildinfo',  'Build Info, see libxorp/create_buildinfo.sh', True),
    BoolVariable('enable_olsr',  'Build OLSR', True),
    BoolVariable('enable_wrapper',  'Build Wrapper', True),
    BoolVariable('enable_ospf',  'Build OSPF', True),
    BoolVariable('enable_rip',  'Build RIP', True),
    BoolVariable('enable_vrrp',  'Build VRRP', True),
    BoolVariable('enable_xorpsh',  'Build xorpsh', True),
    BoolVariable('enable_tests',  'Build Test Programs', False),
    BoolVariable('enable_click',  'Build CLICK support', False),
    BoolVariable('enable_fea_dummy',  'Build fea-dummy target', True),
    BoolVariable('enable_viff_use_ifindex',  'Use VIFF_USE_IFINDEX feature in Linux kernel 2.6.31+', False),
    BoolVariable('enable_async_server',  'Permit asynchronous method implementations', False),
    BoolVariable('debug_xrldb',  'Build with runtime XRL syntax validation in Router Manager', False),
    EnumVariable('debug', 'Build with debug symbols', 'full',
                 allowed_values=('no', 'yes', 'full', 'override'),
                 map={}, ignorecase=2),
    EnumVariable('optimize', 'Build with optimization', 'full',
                 allowed_values=('no', 'minimal', 'yes', 'full', 'highest',
                                 'size', 'override'),
                 map={}, ignorecase=2),
    EnumVariable('profile', 'Build with profiling', 'no',
                 allowed_values=('no', 'gprof', 'pprof', 'override'),
                 map={}, ignorecase=2),
    EnumVariable('transport', 'Set default XRL transport protocol', 'local',
                  allowed_values=('tcp', 'local'),
                  map={}, ignorecase=2),
    PathVariable('prefix', 'Install prefix',
                 '/usr/local/xorp', PathVariable.PathAccept),
    )

def log_args(afname):
    f = open(afname, 'w')
    for a in ARGUMENTS.iteritems():
        print >>f, "%s=%s" % a,
    print >>f
    f.close()

def tgt_guess():
    p = subprocess.Popen(['./config.guess'], stdout=subprocess.PIPE)
    o = p.communicate()[0]
    if p.returncode != 0:
        Exit(1)
    return o.strip()

def tgt_canonicalize(alias):
    p = subprocess.Popen(['./config.sub', alias], stdout=subprocess.PIPE)
    o = p.communicate()[0]
    if p.returncode != 0:
        Exit(1)
    return o.strip()

build_alias = ARGUMENTS.get('build', None)
if build_alias == None:
    build_alias = tgt_guess()
build = tgt_canonicalize(build_alias)
(build_cpu, build_vendor, build_os) = build.split('-', 2)

host_alias = ARGUMENTS.get('host', None)
if host_alias == None:
    host_alias = build_alias
host = tgt_canonicalize(host_alias)
(host_cpu, host_vendor, host_os)  = host.split('-', 2)


sourcedir=Dir(".").abspath
builddir=Dir(ARGUMENTS.get('builddir', '#obj/' + host)).abspath

SConsignFile(builddir + '/.sconsign')

try:
    Execute(Mkdir(builddir))
except:
    pass

# Only save args when doing the default build process.
if (len(COMMAND_LINE_TARGETS) == 0):
    log_args(builddir + '/.scons_build_args')

# XXX TODO: Make initial CPPPATH/LIBPATH derive from
# autodetected host system *or* command line.
#env = Environment(
#		  TOOLS = ['default', 'autotest', 'clntgen', 'tgtgen',
#		           'TOOL_SUBST'],
#		  ENV = os.environ,
#		  BUILDDIR = builddir,
#		  CPPPATH=['/usr/local/include', '$BUILDDIR'],
#		  LIBPATH=['usr/lib', '/usr/local/lib'],
#		  variables = vars)

env = Environment(
		  TOOLS = ['default', 'autotest', 'clntgen', 'tgtgen',
		           'TOOL_SUBST'],
		  ENV = os.environ,
		  BUILDDIR = builddir,
		  CPPPATH=['$BUILDDIR'],
		  LIBPATH=['usr/lib'],
		  variables = vars)

prefix = env['prefix']

env['builddir'] = builddir;

print 'Build System Type: ', build
print 'Host System Type:  ', host
print 'Source path:       ', sourcedir
print 'Build path:        ', builddir
print 'Install prefix:    ', prefix

env['DESTDIR'] = ARGUMENTS.get('DESTDIR', '')

# Provide mechanism to override CC, CXX, etc.
if ARGUMENTS.get('CC', None):
    env['CC'] = ARGUMENTS.get('CC')
print 'CC:               ', env['CC']

if ARGUMENTS.get('CXX', None):
    env['CXX'] = ARGUMENTS.get('CXX')
print 'CXX:              ', env['CXX']

if ARGUMENTS.get('RANLIB', None):
    env['RANLIB'] = ARGUMENTS.get('RANLIB')
print 'RANLIB:           ', env['RANLIB']

if ARGUMENTS.get('AR', None):
    env['AR'] = ARGUMENTS.get('AR')
    print 'AR:               ', env['AR']

if ARGUMENTS.get('LD', None):
    env['LD'] = ARGUMENTS.get('LD')
    print 'LD:               ', env['LD']


env['STRIP'] = ARGUMENTS.get('STRIP', 'strip')
print 'STRIP:            ', env['STRIP']

if env['strip']:
    env['strip'] = True
print 'Strip binaries:   ', env.has_key('strip')

# POSIX strip has no options by the strict letter of the law.
# Assume we have GNU binutils strip until proven otherwise.
gnustrip = True
env['_STRIP_UNNEEDED'] = ""
if gnustrip:
    env['_STRIP_UNNEEDED'] = "--strip-unneeded"

# User can override this. Defaults to gcc's -O1, as this trims
# most of the template fat.
print 'Optimize code:    ', env['optimize']

# Default to disable; wrapper for compiler profiling support.
print 'Profile code:     ', env['profile']

# Default to local (UNIX domain sockets).
print 'Default XRL transport:', env['transport']

# Most of our shared library tweaks are specific to GNU ld.
# Check if the GNU linker is available, and print a warning if not.
if env['shared']:
    if SCons.Tool.FindTool(['gnulink'], env) is None:
        print gnutoolwarning % 'ld linker'
    env['SHAREDLIBS'] = "defined"
print 'Shared libraries: ', env.has_key('SHAREDLIBS')
print 'Use rtld ORIGIN:  ', env['rtld_origin']

# AUTOTEST_SKIP_ERRORS is SetDefault() by site_scons/site_tools/autotest.py,
# so print its value here.
if env['ignore_check_errors']:
    env['AUTOTEST_SKIP_ERRORS'] = True
print 'Ignore check errors: ', env['AUTOTEST_SKIP_ERRORS']

# NOTE: Enabling debug messages for the whole tree may not be what you want,
# as it can lead to premature timeouts.
# Enabling callback debugging is currently not advised as it does
# inline printfs.
print 'Debug symbols:    ', env['debug']
print 'Debug STL:        ', env['debug_stl']
print 'Debug messages:   ', env['debug_msg']
print 'Debug function names: ', env['debug_fn']
print 'Debug callbacks:  ', env['debug_cb']
print 'Debug XRL syntax: ', env['debug_xrldb']
print 'Enable OLSR:      ', env['enable_olsr']
print 'Enable Wrapper:   ', env['enable_wrapper']
print 'Enable OSPF:      ', env['enable_ospf']
print 'Enable RIP:       ', env['enable_rip']
print 'Enable VRRP:      ', env['enable_vrrp']
print 'Enable xorpsh     ', env['enable_xorpsh']
print 'Enable Test Programs: ', env['enable_tests']
print 'Enable CLICK:     ', env['enable_click']
print 'Enable FEA Dummy: ', env['enable_fea_dummy']
print 'Enable VIFF_USE_IFINDEX: ', env['enable_viff_use_ifindex']
print 'Enable async method impls: ', env['enable_async_server']
print 'Enable BGP:       ', env['enable_bgp']
print 'Enable BuildInfo: ', env['enable_buildinfo']
print 'Xorp can run from BUILD_DIR: ', env['enable_builddirrun']
print 'Try Enable BOOST: ', env['enable_boost']
print 'Try Enable uSTL : ', env['enable_ustl']
print 'Disable IPv6:     ', env['disable_ipv6']
print 'Disable libtecla: ', env['disable_libtecla']
print 'Disable Firewall: ', env['disable_fw']
print 'Disable Profile : ', env['disable_profile']
print 'Disable -Werror : ', env['disable_werror']
print 'Enable lex hack : ', env['enable_lex_hack']
print 'Disable warning logs : ', env['disable_warninglogs']
print 'Disable error logs : ', env['disable_errorlogs']
print 'Disable trace logs : ', env['disable_tracelogs']
print 'Disable fatal logs : ', env['disable_fatallogs']
print 'Disable info logs : ', env['disable_infologs']
print 'Disable assert logs : ', env['disable_assertlogs']
print 'Disable other logs : ', env['disable_otherlogs']
print 'Disable assert: ', env['disable_assert']


env['CONFIGURELOG'] = str(builddir) + os.sep + 'config.log'
env['CONFIGUREDIR'] = str(builddir) + os.sep + '.sconf_temp'

SConsEnvironment.Chmod = SCons.Action.ActionFactory(os.chmod,
    lambda dest, mode: 'Chmod("%s", 0%o)' % (dest, mode))

def Copy(src, dest):
    try:
        os.unlink(dest)
    except OSError:
        pass
    shutil.copy2(src, dest)
SConsEnvironment.Copy = SCons.Action.ActionFactory(Copy,
    lambda src, link_name: 'Copy("%s", to "%s")' % (src, link_name))

def Symlink(src, link_name):
    try:
        os.unlink(link_name)
    except OSError:
        pass
    os.symlink(src, link_name)
SConsEnvironment.Symlink = SCons.Action.ActionFactory(Symlink,
    lambda src, link_name: 'Symlink("%s", as "%s")' % (src, link_name))

def InstallProgram(env, dest, files, perm = 0755):
    if env.has_key('DESTDIR'):
    	# NB: use simple concatenation
    	dest = env.Dir(env['DESTDIR'] + env.Dir(dest).path)
    obj = env.Install(dest, files)
    for i in obj:
	env.AddPostAction(i, env.Chmod(str(i), perm))
	if env['strip']:
	    env.AddPostAction(i, Action("$STRIP $TARGET"))
    return obj
SConsEnvironment.InstallProgram = InstallProgram

def InstallScript(env, dest, files, perm = 0555):
    if env.has_key('DESTDIR'):
    	# NB: use simple concatenation
    	dest = env.Dir(env['DESTDIR'] + env.Dir(dest).path)
    obj = env.Install(dest, files)
    for i in obj:
	env.AddPostAction(i, env.Chmod(str(i), perm))
    return obj
SConsEnvironment.InstallScript = InstallScript

def InstallLibrary(env, dest, files, perm = 0644):
    if env.has_key('DESTDIR'):
    	# NB: use simple concatenation
    	dest = env.Dir(env['DESTDIR'] + env.Dir(dest).path)
    obj = env.Install(dest, files)
    for i in obj:
	env.AddPostAction(i, env.Chmod(str(i), perm))
	if env['strip']:
	    env.AddPostAction(i, Action("$STRIP $_STRIP_UNNEEDED $TARGET"))
    return obj
SConsEnvironment.InstallLibrary = InstallLibrary

def InstallData(env, dest, files, perm = 0644):
    if env.has_key('DESTDIR'):
    	# NB: use simple concatenation
    	dest = env.Dir(env['DESTDIR'] + env.Dir(dest).path)
    obj = env.Install(dest, files)
    for i in obj:
	env.AddPostAction(i, env.Chmod(str(i), perm))
    return obj
SConsEnvironment.InstallData = InstallData

#
# GNU-style package paths.
#
# These are not parsed using PathVariable() above, to avoid confusion
# on the part of the user, as their default values are derived
# from 'prefix'.
# Not all of them are used at the moment.
#
env['exec_prefix'] = ARGUMENTS.get('exec_prefix', '$prefix')
env['sbindir'] = ARGUMENTS.get('sbindir', '$exec_prefix/sbin')
env['libexecdir'] = ARGUMENTS.get('libexecdir', '$exec_prefix/lib')
env['datarootdir'] = ARGUMENTS.get('datarootdir', '$prefix/share')
env['datadir'] = ARGUMENTS.get('datadir', '$datarootdir/xorp')
env['sysconfdir'] = ARGUMENTS.get('sysconfdir', '$prefix/etc')
env['localstatedir'] = ARGUMENTS.get('localstatedir', '$prefix/var')
env['libdir'] = ARGUMENTS.get('libdir', '$exec_prefix/lib')
env['mandir'] = ARGUMENTS.get('mandir', '$datarootdir/man')

#
# XORP internal package paths.
#
# These are the paths actually used to build the package image
# when installed in its destination directory.
# Most of these paths are derived from the GNU-style variables above.
#
# The image layout is intended to be FHS 2.3 compliant, for the benefit
# of 3rd party packagers and distributors.
#
env['xorp_rootdir']     = env['exec_prefix']	# used to determine RPATH
env['xorp_confdir']     = env['sysconfdir']		# path to xorp.conf
env['xorp_libdir']      = env['libdir']  + '/xorp/lib'
env['xorp_moduledir']   = env['libdir']  + '/xorp/sbin'	# Protocol modules
env['xorp_sbindir']     = env['sbindir']		# End-user binaries
env['xorp_templatedir'] = env['datadir'] + '/templates'
env['xorp_tooldir']     = env['libdir']  + '/xorp/bin'	# tools/*
env['xorp_xrlsdir']     = env['datadir'] + '/xrl/targets'	# *.xrls
env['xorp_sourcedir']   = sourcedir	# rtrmgr/util.cc and xif need this

tst = ARGUMENTS.get('enable_builddirrun', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['enable_builddirrun'] = True
else:
    env['enable_builddirrun'] = False

tst = ARGUMENTS.get('enable_boost', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['enable_boost'] = True
else:
    env['enable_boost'] = False

tst = ARGUMENTS.get('enable_ustl', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['enable_ustl'] = True
else:
    env['enable_ustl'] = False

tst = ARGUMENTS.get('enable_tests', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['enable_tests'] = True
else:
    env['enable_tests'] = False

tst = ARGUMENTS.get('enable_click', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['enable_click'] = True
else:
    env['enable_click'] = False

# Default to enabled
tst = ARGUMENTS.get('enable_fea_dummy', True)
if tst and ((tst == "no") or (tst == "false")):
    env['enable_fea_dummy'] = False
else:
    env['enable_fea_dummy'] = True

# Default to disabled
tst = ARGUMENTS.get('enable_async_server', False)
if tst and ((tst == "no") or (tst == "false")):
    env['enable_async_server'] = False
else:
    env['enable_async_server'] = True

# Default to disabled
tst = ARGUMENTS.get('enable_viff_use_ifindex', False)
if tst and ((tst == "no") or (tst == "false")):
    env['enable_viff_use_ifindex'] = False
else:
    env['enable_viff_use_ifindex'] = True

# Default to enabled
tst = ARGUMENTS.get('enable_bgp', True)
if tst and ((tst == "no") or (tst == "false")):
    env['enable_bgp'] = False
else:
    env['enable_bgp'] = True

# Default to enabled
tst = ARGUMENTS.get('enable_buildinfo', True)
if tst and ((tst == "no") or (tst == "false")):
    env['enable_buildinfo'] = False
else:
    env['enable_buildinfo'] = True
    # Only generate build-info when we're building
    # the normal build.
    if (len(COMMAND_LINE_TARGETS) == 0):
        if not env.GetOption('clean') and \
               not env.GetOption('help'):
            if os.system("./libxorp/create_buildinfo.sh") != 0:
                print "ERROR:  Failed to generate libxorp/build_info.cc."
                print "You can disable this feature with: enable_buildinfo=no"
                exit(1)

if env.GetOption('clean'):
    os.unlink('./libxorp/build_info.cc');


# Default to enabled
tst = ARGUMENTS.get('enable_olsr', True)
if tst and ((tst == "no") or (tst == "false")):
    env['enable_olsr'] = False
else:
    env['enable_olsr'] = True

# Default to enabled
tst = ARGUMENTS.get('enable_wrapper', True)
if tst and (tst == "no"):
    env['enable_wrapper'] = False
else:
    env['enable_wrapper'] = True

# Default to enabled
tst = ARGUMENTS.get('enable_ospf', True)
if tst and ((tst == "no") or (tst == "false")):
    env['enable_ospf'] = False
else:
    env['enable_ospf'] = True

# Default to enabled
tst = ARGUMENTS.get('enable_rip', True)
if tst and ((tst == "no") or (tst == "false")):
    env['enable_rip'] = False
else:
    env['enable_rip'] = True

# Default to enabled
tst = ARGUMENTS.get('enable_vrrp', True)
if tst and ((tst == "no") or (tst == "false")):
    env['enable_vrrp'] = False
else:
    env['enable_vrrp'] = True

# Default to enabled
tst = ARGUMENTS.get('enable_xorpsh', True)
if tst and ((tst == "no") or (tst == "false")):
    env['enable_xorpsh'] = False
else:
    env['enable_xorpsh'] = True

tst = ARGUMENTS.get('disable_ipv6', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['disable_ipv6'] = True
else:
    env['disable_ipv6'] = False

tst = ARGUMENTS.get('disable_fw', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['disable_fw'] = True
else:
    env['disable_fw'] = False

tst = ARGUMENTS.get('disable_libtecla', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['disable_libtecla'] = True
else:
    env['disable_libtecla'] = False

tst = ARGUMENTS.get('disable_profile', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['disable_profile'] = True
else:
    env['disable_profile'] = False

tst = ARGUMENTS.get('disable_werror', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['disable_werror'] = True
else:
    env['disable_werror'] = False

tst = ARGUMENTS.get('enable_lex_hack', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['enable_lex_hack'] = True
else:
    env['enable_lex_hack'] = False

tst = ARGUMENTS.get('disable_warninglogs', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['disable_warninglogs'] = True
else:
    env['disable_warninglogs'] = False

tst = ARGUMENTS.get('disable_errorlogs', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['disable_errorlogs'] = True
else:
    env['disable_errorlogs'] = False

tst = ARGUMENTS.get('disable_tracelogs', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['disable_tracelogs'] = True
else:
    env['disable_tracelogs'] = False

tst = ARGUMENTS.get('disable_fatallogs', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['disable_fatallogs'] = True
else:
    env['disable_fatallogs'] = False

tst = ARGUMENTS.get('disable_infologs', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['disable_infologs'] = True
else:
    env['disable_infologs'] = False

tst = ARGUMENTS.get('disable_assertlogs', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['disable_assertlogs'] = True
else:
    env['disable_assertlogs'] = False

tst = ARGUMENTS.get('disable_otherlogs', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['disable_otherlogs'] = True
else:
    env['disable_otherlogs'] = False

tst = ARGUMENTS.get('disable_assert', False)
if tst and not ((tst == "no") or (tst == "false")):
    env['disable_assert'] = True
    env['disable_werror'] = True
    env['disable_otherlogs'] = True
    env['disable_assertlogs'] = True
    env['disable_infologs'] = True
    env['disable_fatallogs'] = True
    env['disable_tracelogs'] = True
    env['disable_errorlogs'] = True
    env['disable_warninglogs'] = True
else:
    env['disable_assert'] = False

########## start configure magic

if env.has_key('LIBS'):
    pre_LIBS = env['LIBS']
else:
    pre_LIBS = []

if not env.GetOption('clean') and \
   not env.GetOption('help'):

    env.AppendUnique( CFLAGS = Split(ARGUMENTS.get('CFLAGS', '')) )
    env.AppendUnique( CXXFLAGS = Split(ARGUMENTS.get('CXXFLAGS', '')) )
    env.AppendUnique( LINKFLAGS = Split(ARGUMENTS.get('LINKFLAGS', '')) )

    env['MISSING_DEPS'] = []
    env['SKIPPED_DEPS'] = []

    from config.endian import CheckEndianness
    from config.member import CheckTypeMember
    from config.sysctl import CheckSysctl
    from config.boost import CheckBoost, CheckBoostLibrary

    my_custom_tests = {
	'CheckBoost'        : CheckBoost,
	'CheckBoostLibrary' : CheckBoostLibrary,
	'CheckEndianness'   : CheckEndianness,
	'CheckTypeMember'   : CheckTypeMember,
	'CheckSysctl'       : CheckSysctl
    }

    conf = env.Configure(config_h = str(builddir) + '/xorp_config.h',
			 log_file = str(builddir) + '/config.log',
			 custom_tests = my_custom_tests)

    ##########
    # target os

    # friendly name of target
    osname = host_os
    if fnmatch.fnmatch(host_os, 'freebsd*'):
        osname = "FreeBSD"
        conf.Define('HOST_OS_FREEBSD')
        conf.Define('ENABLE_ADVANCED_MULTICAST_API')
    elif fnmatch.fnmatch(host_os, 'mingw32*'):
        osname = "Windows"
        conf.Define('HOST_OS_WINDOWS')
        env['mingw'] = True
        env['PROGSUFFIX'] = '.exe'
    elif fnmatch.fnmatch(host_os, 'linux*'):
        osname = "Linux"
        conf.Define('HOST_OS_LINUX')
        conf.Define('HAVE_PROC_LINUX')
        conf.Define('ENABLE_ADVANCED_MULTICAST_API')
    elif fnmatch.fnmatch(host_os, 'netbsd*'):
	osname = "NetBSD"
	conf.Define('HOST_OS_NETBSD')
        conf.Define('ENABLE_ADVANCED_MULTICAST_API')
    elif fnmatch.fnmatch(host_os, 'sunos*'):
        osname = "Solaris"
        conf.Define('HOST_OS_SOLARIS')
    else:
	osname = sys.platform.upper()
    conf.Define('HOST_OS_NAME', '"' + osname + '"')

    #export osname - needed for lex and yacc
    env['osname'] = osname

    # toolchain
    conf.Define('CPP_SUPPORTS_C99_VA_ARGS')
    conf.Define('CPP_SUPPORTS_GNU_VA_ARGS')

    if env['disable_fw']:
        conf.Define("XORP_DISABLE_FIREWALL")

    if env['disable_profile']:
        conf.Define("XORP_DISABLE_PROFILE")

    if env['enable_ustl']:
        conf.Define("XORP_USE_USTL")

    if env['enable_boost']:
        # Boost
        #env['boost_suffix'] = "-mt"		# Not for FreeBSD
        if  not conf.CheckBoost(require_version='1.34'):
            print "Boost version check for 1.34 failed, disabling boost support.\n"
            env['enable_boost'] = False

        #conf.CheckBoostLibrary('system')
        #conf.CheckBoostLibrary('date_time')
        #conf.CheckBoostLibrary('iostreams')

        # Additional Boost libraries
        #conf.CheckBoostLibrary('filesystem')
        #conf.CheckBoostLibrary('program_options')
        #conf.CheckBoostLibrary('regex')
        #if env['enable_boost'] and not conf.CheckBoostLibrary('regex'):
        #    print "Boost regex library check failed, disabling boost support.\n"
        #    env['enable_boost'] = False
        #conf.CheckBoostLibrary('signals')
        #conf.CheckBoostLibrary('thread')

    if env['enable_boost']:
        conf.Define("USE_BOOST")
        #  I tried checking this with conf.CheckHeader, but it always returns false,
        #  so just over-ride it here and assume it exists.
        conf.Define('HAS_BOOST_NONCOPYABLE_INC')

    # Big ball of mud.
    from config.allconfig import DoAllConfig
    DoAllConfig(env, conf, host_os)

    tst = ARGUMENTS.get('enable_click', False)
    if tst and not ((tst == "no") or (tst == "false")):
        conf.Define('XORP_USE_CLICK')

    tst = ARGUMENTS.get('enable_fea_dummy', True)
    if tst and not ((tst == "no") or (tst == "false")):
        conf.Define('XORP_USE_FEA_DUMMY')

    tst = ARGUMENTS.get('enable_viff_use_ifindex', False)
    if tst and not ((tst == "no") or (tst == "false")):
        conf.Define('XORP_USE_VIFF_USE_IFINDEX')

    tst = ARGUMENTS.get('enable_async_server', False)
    if tst and not ((tst == "no") or (tst == "false")):
        conf.Define('XORP_ENABLE_ASYNC_SERVER')

    if env['enable_xorpsh']:
        conf.Define('XORP_USE_XORPSH')

    # Note: hard-coded paths pushed down to rtrmgr/util.cc environment.

    # Print warnings about missing/skipped dependencies.
    if env['MISSING_DEPS']:
        print '\nRequired dependencies not found, exiting:\n   - %s' % '\n   - '.join(env['MISSING_DEPS'])
        if env['SKIPPED_DEPS']:
            print 'Optional dependencies not found:\n   - %s' % '\n   - '.join(env['SKIPPED_DEPS'])
        Exit(1)

    if env['SKIPPED_DEPS']:
        print 'Optional dependencies not found:\n   - %s' % '\n   - '.join(env['SKIPPED_DEPS'])

    newenv = conf.Finish()
    #
    # Configure will put all libraries in global link line, during checks.
    # This is not what we want, as it means all binaries get these LIBS.
    # We should be able to check, for libraries, at SCons level,
    # by looking in conf.havedict, but that isn't exported.
    #
    # It seems we'd be best off defining a class of our own, containing
    # things we want to see, and passing that in and out of the
    # config tests when we split up allconfig.py.
    # For now, let's assume that libraries are present, and link against
    # them on a piecemeal basis.
    #
    post_LIBS = env['LIBS']
    env['LIBS'] = pre_LIBS

    print 'Detected libraries:',
    for x in post_LIBS:
        print x,
    print

########## end configure magic

if SCons.Tool.FindTool(['gcc'], env) is None or \
   SCons.Tool.FindTool(['g++'], env) is None:
    print gnutoolwarning % 'gcc or g++ compiler'

# If the user didn't override our default optimization, then
# sanitize user's CFLAGS/CXXFLAGS to not contain optimization options,
# and map to an appropriate GCC flag.
if not env['optimize'] == 'override':
    env.Replace( CFLAGS = filter(lambda s: not s.startswith('-O'),
                                 Split(env['CFLAGS'])))
    env.Replace( CXXFLAGS = filter(lambda s: not s.startswith('-O'),
                                   Split(env['CXXFLAGS'])))
    bigodict = { 'no': '-O0',
                 # 'minimal' denotes only those optimizations
                 # necessary to force gcc to perform the tree_sink
                 # pass, to elide most STL template instantiations.
                 'minimal': "-O1 -fno-defer-pop -fno-delayed-branch \
-fno-guess-branch-probability -fno-cprop-registers -fno-if-conversion \
-fno-if-conversion2 -fno-tree-ccp -fno-tree-dce -fno-tree-dominator-opts \
-fno-tree-dse -fno-tree-ter -fno-tree-lrs -fno-tree-sra \
-fno-tree-copyrename -fno-tree-fre -fno-tree-ch -fno-unit-at-a-time \
-fno-merge-constants",
                 'yes': '-O1',
                 'full': '-O2',
                 'highest': '-O3',
                 'size': '-Os' }
    bigoflag = bigodict[env['optimize']]
    env.AppendUnique(CFLAGS = [ bigoflag ])
    env.AppendUnique(CXXFLAGS = [ bigoflag ])

    # At least on Fedora 13, -Os breaks build with strict-aliasing
    # warnings.  Code looks right to me, so going to just disable
    # strict aliasing. --Ben
    if env['optimize'] == 'size':
        env.AppendUnique(CFLAGS = [ '-fno-strict-aliasing' ])
        env.AppendUnique(CXXFLAGS = [ '-fno-strict-aliasing' ])

# Do the same for the flags which control debug symbols.
if not env['debug'] == 'override':
    env.Replace( CFLAGS = filter(lambda s: not s.startswith('-g'),
                                 Split(env['CFLAGS'])))
    env.Replace( CXXFLAGS = filter(lambda s: not s.startswith('-g'),
                                   Split(env['CXXFLAGS'])))
    gdict = { 'no': '', 'yes': '-g', 'full': '-g3' }
    gflag = gdict[env['debug']]
    if not env['debug'] == 'no':
        env.AppendUnique(CFLAGS = [ gflag ])
        env.AppendUnique(CXXFLAGS = [ gflag ])

# Do the same for the flags which control code profiling.
if not env['profile'] == 'override':
    if env['profile'] == 'gprof' and env.has_key('SHAREDLIBS'):
        print """
WARNING: You have requested GNU gprof style profiling
and shared libraries. This is UNSUPPORTED, and probably will not link.
"""
    strip_pg_flags = [ '-pg', '-finstrument-functions', '-fno-omit-frame-pointer', '-fno-optimize-sibling-calls' ]
    try:
        env.Replace( CFLAGS = filter(
            lambda s: not s.startswith(tuple(strip_pg_flags)),
            Split(env['CFLAGS']))
        )
        env.Replace( CXXFLAGS = filter(
            lambda s: not s.startswith(tuple(strip_pg_flags)),
            Split(env['CXXFLAGS']))
        )
    except TypeError:
        print """
        WARNING: Your version of scons and/or python has a syntax issue with this code.
        It cannot strip out the gprof related flags.  If your compile fails, please try
        disabling gprof profiling and/or shared libraries.  If the compile works, then
        you can ignore this warning.
        """

    # Full use of profiling may require more than one flag, so Split() them.
    pgdict = {'no': '',
              'gprof': '-pg',
              'pprof': '',
    }
    pgflag = pgdict[env['profile']]
    if not env['profile'] == 'no':
        pgflags = pgflag + ' -fno-omit-frame-pointer -fno-optimize-sibling-calls'
        env.AppendUnique( CFLAGS = Split(pgflags))
        env.AppendUnique( CXXFLAGS = Split(pgflags) )
        # Using gprof requires we link with the '-pg' option.
        if env['profile'] == 'gprof':
            env.AppendUnique( LINKFLAGS = Split(pgflag) )
        # Using pprof requires we link with the '-lprofiler' library
        # from the Google Performance Tools package. To avoid having
        # to insert calls to the profiler, use the environment
        # variable CPUPROFILE when running the program.
        # If you do insert calls, please bracket them with WITH_PPROF.
        if env['profile'] == 'pprof':
            env.AppendUnique( LIBS = [ 'profiler' ] )
            #env.AppendUnique( CPPDEFINES = [ 'WITH_PPROF' ] )

if env['enable_ustl']:
    env.AppendUnique( LIBS = [ 'ustl' ] )

#
# Apply top-level 'transport' protocol option, which controls the
# default transport protocol chosen by libxipc.
#
# We pass it as an ordinal character constant to avoid shell
# quoting issues, as it is specified by 1 character.
#
# Note: We need to do this at top level to make sure the option
# is propagated correctly, because anything which includes
# <libxipc/xrl_std_router.hh> will be affected by this option.
#
if fnmatch.fnmatch(host_os, 'mingw32*'):
    # Windows only supports tcp.
    env.AppendUnique(CPPDEFINES = [
        ( 'XRL_PF', ord('t')),
        ])
else:
    xrl_pf_dict = { 'tcp': 't', 'local': 'x' }
    env.AppendUnique(CPPDEFINES = [
        ( 'XRL_PF', ord(xrl_pf_dict[env['transport']]) ),
        ])

# Read our VERSION file and use it's contents to pass the
# version into the compile process.
vf = open('VERSION', 'r')
ver = vf.readline()
ver = ver.strip()
env.AppendUnique(CPPDEFINES = [
        ( 'XORP_VERSION', ver),
        ])

# Forcibly disable GCC's SSP support., as it may be incompatible
# with the XORP code base.
#env.AppendUnique(CPPDEFINES = [
#    ( '_FORTIFY_SOURCE', 0 ),
#    ])

if env['enable_boost']:
    # Forcibly disable Boost's thread support; the XORP code base is
    # not yet threaded.
    env.AppendUnique(CPPDEFINES = [
        ( 'BOOST_DISABLE_THREADS' ),
        ])

# Some platforms have alignment warnings that cannot easily be
# fixed, so we can't enable Werror for them.
if ((build != "i386-pc-mingw32") and
    (host_cpu == "i686" or
     host_cpu == "i386" or
     host_cpu == "x86_64")):
    if env['disable_werror']:
	print "WARNING:  Disabling -Werror on user command."
    else:
        env.AppendUnique(CFLAGS = [
	        '-Werror',
		])
	env.AppendUnique(CXXFLAGS = [
		'-Werror',
		])
else:
    print "WARNING:  Detected funky platform, will not enable -Werror compile option: ", host_cpu


# NOTE: gcc specific flags.
env.AppendUnique(CFLAGS = [
    '-W',
    '-Wall',
    '-Wwrite-strings',
    '-Wbad-function-cast',
    '-Wmissing-prototypes',
    '-Wcast-qual',
    '-Wmissing-declarations',
    '-Wpointer-arith',
    '-Wcast-align',
    '-Wstrict-prototypes',
    '-Wnested-externs',
    '-pipe',
    ])

env.AppendUnique(CXXFLAGS = [
    '-W',
    '-Wall',
    '-Wwrite-strings',
    '-Wcast-qual',
    '-Wpointer-arith',
    '-Wcast-align',
    '-Woverloaded-virtual',
    '-pipe',
    ])

# This used to be in the CXXFlags above, but it started breaking
# in Fedora-23.  Looks like a useless flag anyway to me. --Ben
#    '-ftemplate-depth-25',

# Colors screw up display when showing xemacs (my fav editor), so
# disable them here if we are using clang.
if env['CC'] and env['CC'] == 'clang':
    env.AppendUnique(CFLAGS = [
        '-fno-color-diagnostics',
        ])
    env.AppendUnique(CXXFLAGS = [
        '-fno-color-diagnostics',
        ])

if env['enable_buildinfo']:
    env.AppendUnique(CXXFLAGS = [
        '-DXORP_BUILDINFO',
        ]);

if env['enable_lex_hack']:
    env.AppendUnique(CXXFLAGS = [
        '-DNEED_LEX_H_HACK',
        ]);

# NOTE: For GNU STL only at this time.
if env['debug_stl']:
    env.AppendUnique(CXXFLAGS = [
        '-D_GLIBCXX_DEBUG',
        '-D_GLIBCXX_DEBUG_PEDANTIC',
    ])

# NOTE: DEBUG_LOGGING_GLOBAL currently a no-op.
if env['debug_msg']:
    env.AppendUnique(CFLAGS = [
        '-DDEBUG_LOGGING',
    ])
    env.AppendUnique(CXXFLAGS = [
        '-DDEBUG_LOGGING',
    ])

if env['debug_fn']:
    env.AppendUnique(CFLAGS = [
        '-DDEBUG_PRINT_FUNCTION_NAME',
    ])
    env.AppendUnique(CXXFLAGS = [
        '-DDEBUG_PRINT_FUNCTION_NAME',
    ])

if env['debug_cb']:
    env.AppendUnique(CFLAGS = [
        '-DDEBUG_CALLBACKS',
    ])
    env.AppendUnique(CXXFLAGS = [
        '-DDEBUG_CALLBACKS',
    ])

#
# Fixup shared library paths.
# Shared libraries are installed in $xorp_libdir by default.
#
# If rtld_origin is True, the linker will be passed a relative RPATH.
# This allows the package itself to be relocated in the filesystem
# as a whole. This also allows us to run binaries before we ship them.
#

# Go ahead and define some variables here so that SConscript files are not
# so repetitive when dealing with RPATH, etc.
# Since we are building static, they should be ignored anyway.
env['xorp_sbin_rpath'] = env['xorp_libdir']
env['xorp_tool_rpath'] = env['xorp_libdir']
env['xorp_module_rpath'] = env['xorp_libdir']

if env.has_key('SHAREDLIBS'):
    if env['rtld_origin']:
	#
	# Build a subdirectory for holding symlinks to libraries upfront, so
	# that binaries about to be built can be run from inside the BUILDDIR.
	#
	# XXX Assumes that $libdir is below $exec_prefix. If the
	# user changes this, results are undefined.
	#
	# $BUILDIR/lib will contain .so symlinks
	#
	xorp_alias_libdir = os.path.join(builddir, 'lib')
	# XXX workaround Mkdir() failure on EEXIST, SCons < 20090223.
	try:
		Execute(Mkdir(xorp_alias_libdir))
	except:
		pass
	env['xorp_alias_libdir'] = xorp_alias_libdir
	#
	# Build a further alias for the benefit of entities which
	# will be later installed in $xorp_sbindir.
	#
	# $BUILDIR/lib/xorp/lib will point to $BUILDIR/lib
	#
	xorp_alias_subdir = os.path.join(xorp_alias_libdir, 'xorp')
	#
	# XXX workaround Mkdir() failure on EEXIST, SCons < 20090223.
	xorp_module_alias_libdir = os.path.join(xorp_alias_subdir, 'lib')
	try:
		Execute(Mkdir(xorp_alias_subdir))
	except:
		pass
	Execute(env.Symlink(xorp_alias_libdir, xorp_module_alias_libdir))
	env['xorp_module_alias_libdir'] = xorp_module_alias_libdir

        if not fnmatch.fnmatch(host_os, 'mingw32*'):
            # Tell rtld to turn on $ORIGIN processing by default.
            # NOTE: GNU ld specific flag.
            env.PrependUnique( LINKFLAGS = [ '-Wl,-z,origin' ] )

	# Set relative RPATH for each kind of installed XORP component.
	env['xorp_sbin_rpath'] = os.path.join('\\$$ORIGIN',
	   os.path.relpath(env.Dir('$xorp_libdir').abspath,
			   env.Dir('$xorp_sbindir').abspath))
	env['xorp_tool_rpath'] = os.path.join('\\$$ORIGIN',
	   os.path.relpath(env.Dir('$xorp_libdir').abspath,
			   env.Dir('$xorp_tooldir').abspath))
	env['xorp_module_rpath'] = os.path.join('\\$$ORIGIN',
	   os.path.relpath(env.Dir('$xorp_libdir').abspath,
			   env.Dir('$xorp_moduledir').abspath))

if env['enable_builddirrun']:
	#
	# Build a subdirectories for holding symlinks to modules and command binaries,
	# so that xorpsh and rtrmngr, about to be built, can be run from inside the BUILDDIR.
	#
	# $BUILDIR/lib/bin will contain symlinks to command bins
	# $BUILDIR/lib/sbin will contain symlinks to module bins
	#
    xorp_alias_subdir = os.path.join(builddir, 'lib')
    xorp_alias_subdir = os.path.join(xorp_alias_subdir, 'xorp')
    xorp_alias_moduledir = os.path.join(xorp_alias_subdir, 'sbin')
    xorp_alias_tooldir = os.path.join(xorp_alias_subdir, 'bin')
    try:
        Execute(Mkdir(xorp_alias_moduledir))
    except:
        pass
    env['xorp_alias_moduledir'] = xorp_alias_moduledir
    try:
        Execute(Mkdir(xorp_alias_tooldir))
    except:
        pass
    env['xorp_alias_tooldir'] = xorp_alias_tooldir
    #Make subdir  $BUILDDIR/etc/templates for holding template files
    xorp_alias_templatedir = os.path.join(builddir, 'etc')
    xorp_alias_templatedir = os.path.join(xorp_alias_templatedir, 'templates')
    try:
        Execute(Mkdir(xorp_alias_templatedir))
    except:
        pass
    env['xorp_alias_templatedir'] = xorp_alias_templatedir


if not fnmatch.fnmatch(host_os, 'mingw32*'):
    # Export global symbols as dynamic in executables for runtime backtraces
    # w/o GDB or corefiles in production deployments.
    # NOTE: GNU ld specific flag.
    env.AppendUnique(LINKFLAGS = [
        '-rdynamic',
        ])

env.SConscript(['SConscript'], variant_dir='$BUILDDIR',
               exports='env', duplicate=0)

env.Help(vars.GenerateHelpText(env))
