# -*- mode: python -*-
# OpenPGM build script

import os
import time
import sys

EnsureSConsVersion( 1, 0 )
SConsignFile('scons.signatures');

vars = Variables()
vars.AddVariables (
	EnumVariable ('BUILD', 'build environment', 'debug',
			allowed_values=('release', 'debug', 'profile')),
	EnumVariable ('BRANCH', 'branch prediction', 'none',
			allowed_values=('none', 'profile', 'seed')),
	EnumVariable ('COVERAGE', 'test coverage', 'none',
			allowed_values=('none', 'full')),
	EnumVariable ('WITH_HISTOGRAMS', 'Runtime statistical information', 'true',
			allowed_values=('true', 'false')),
	EnumVariable ('WITH_HTTP', 'HTTP administration', 'true',
			allowed_values=('true', 'false')),
	EnumVariable ('WITH_SNMP', 'SNMP administration', 'true',
			allowed_values=('true', 'false')),
	EnumVariable ('WITH_CHECK', 'Check test system', 'false',
			allowed_values=('true', 'false')),
	EnumVariable ('WITH_TEST', 'Network test system', 'false',
			allowed_values=('true', 'false')),
	EnumVariable ('WITH_EXAMPLES', 'Examples', 'true',
			allowed_values=('true', 'false')),
	EnumVariable ('WITH_NCURSES', 'NCURSES examples', 'false',
			allowed_values=('true', 'false')),
	EnumVariable ('WITH_PROTOBUF', 'Google Protocol Buffer examples', 'false',
			allowed_values=('true', 'false')),
	EnumVariable ('WITH_PLUS', 'libpgmplus GPL library', 'false',
			allowed_values=('true', 'false')),
)

#-----------------------------------------------------------------------------
# Dependencies

env = Environment();

def CheckPKGConfig(context, version):
	context.Message( 'Checking for pkg-config... ' )
	ret = context.TryAction('pkg-config --atleast-pkgconfig-version=%s' % version)[0]
	context.Result( ret )
	return ret

def CheckPKG(context, name):
	context.Message( 'Checking for %s... ' % name )
	ret = context.TryAction('pkg-config --exists \'%s\'' % name)[0]
	context.Result( ret )
	return ret

conf = Configure(env, custom_tests = { 'CheckPKGConfig' : CheckPKGConfig,
				       'CheckPKG' : CheckPKG })

if not conf.CheckPKGConfig('0.15.0'):
	print 'pkg-config >= 0.15.0 not found.'
	Exit(1)

if not conf.CheckPKG('glib-2.0 >= 2.10'):
	print 'glib-2.0 >= 2.10 not found.'
	Exit(1)

if not conf.CheckPKG('gthread-2.0'):
	print 'gthread-2.0 not found.'
	Exit(1)

if not conf.CheckPKG('libsoup-2.4'):
	print 'libsoup-2.4 not found.'
	Exit(1)

env = conf.Finish();

#-----------------------------------------------------------------------------
# Platform specifics

env = Environment(
	variables = vars,
	ENV = os.environ,
	CCFLAGS = [	'-pipe',
			'-Wall',
				'-Wextra',
				'-Wfloat-equal',
				'-Wshadow',
				'-Wunsafe-loop-optimizations',
				'-Wpointer-arith',
				'-Wbad-function-cast',
				'-Wcast-qual',
				'-Wcast-align',
				'-Wwrite-strings',
				'-Waggregate-return',
				'-Wstrict-prototypes',
				'-Wold-style-definition',
				'-Wmissing-prototypes',
				'-Wmissing-declarations',
				'-Wmissing-noreturn',
				'-Wmissing-format-attribute',
				'-Wredundant-decls',
				'-Wnested-externs',
				'-Winline',
			'-pedantic',
			'-std=gnu99',
			'--param', 'max-inline-insns-single=600',
			'-D_REENTRANT',
			'-D_GNU_SOURCE',
			'-D__need_IOV_MAX',
			'-DCONFIG_HAVE_PSELECT',
			'-DCONFIG_HAVE_POLL',
			'-DCONFIG_HAVE_PPOLL',
			'-DCONFIG_HAVE_EPOLL',
			'-DCONFIG_HAVE_CLOCK_GETTIME',
			'-DCONFIG_HAVE_CLOCK_NANOSLEEP',
			'-DCONFIG_HAVE_NANOSLEEP',
			'-DCONFIG_HAVE_USLEEP',
			'-DCONFIG_HAVE_RTC',
			'-DCONFIG_HAVE_TSC',
			'-DCONFIG_HAVE_IFR_NETMASK',
			'-DCONFIG_HAVE_GETIFADDRS',
			'-DCONFIG_HAVE_GETHOSTBYNAME2',
			'-DCONFIG_HAVE_GETPROTOBYNAME_R',
			'-DCONFIG_HAVE_MCAST_JOIN',
			'-DCONFIG_HAVE_IP_MREQN',
			'-DCONFIG_HAVE_SPRINTF_GROUPING',
			'-DCONFIG_HAVE_HPET',
			'-DCONFIG_HAVE_DSO_VISIBILITY',
			'-DCONFIG_BIND_INADDR_ANY',
			'-DCONFIG_GALOIS_MUL_LUT',
		 ],
	LINKFLAGS = [	'-pipe',
			'-lm'
		    ]
)

# Branch prediction
if env['BRANCH'] == 'profile':
	env.Append(CCFLAGS = '-fprofile-arcs')
	env.Append(LINKFLAGS = '-fprofile-arcs')
elif env['BRANCH'] == 'seed':
	env.Append(CCFLAGS = '-fbranch-probabilities')

# Coverage analysis
if env['COVERAGE'] == 'full':
	env.Append(CCFLAGS = '-fprofile-arcs')
	env.Append(CCFLAGS = '-ftest-coverage')
	env.Append(LINKFLAGS = '-fprofile-arcs')
	env.Append(LINKFLAGS = '-lgcov')

# Define separate build environments
release = env.Clone(BUILD = 'release')
release.Append(CCFLAGS = '-O2')

debug = env.Clone(BUILD = 'debug')
debug.Append(CCFLAGS = '-ggdb', LINKFLAGS = '-gdb')

profile = env.Clone(BUILD = 'profile')
profile.Append(CCFLAGS = ['-O2','-pg'], LINKFLAGS = '-pg')

thirtytwo = release.Clone(BUILD = 'thirtytwo')
thirtytwo.Append(CCFLAGS = '-m32', LINKFLAGS = '-m32')

# choose and environment to build
if env['BUILD'] == 'release':
	Export({'env':release})
elif env['BUILD'] == 'profile':
	Export({'env':profile})
elif env['BUILD'] == 'thirtytwo':
	Export({'env':thirtytwo})
else:
	Export({'env':debug})

#-----------------------------------------------------------------------------
# Re-analyse dependencies

Import('env')

# vanilla environment
env.ParseConfig('pkg-config --cflags --libs glib-2.0 gthread-2.0');

# instrumentation
if env['WITH_HTTP'] == 'true' and env['WITH_HISTOGRAMS'] == 'true':
	env.Append(CCFLAGS = '-DCONFIG_HISTOGRAMS');

# managed environment for libpgmsnmp, libpgmhttp
env['SNMP_FLAGS'] = { 
        'CCFLAGS'        : [],
        'LIBS'          : [ 'netsnmpagent', 'netsnmpmibs', 'netsnmphelpers', 'netsnmp' ],
};
env['LIBSOUP_FLAGS'] = env.ParseFlags('!pkg-config --cflags --libs libsoup-2.4');

def CheckSNMP(context):
	context.Message('Checking Net-SNMP...');
	lastLIBS = context.env['LIBS'];
	lastCCFLAGS= context.env['CCFLAGS'];
	context.env.MergeFlags(env['SNMP_FLAGS']);
	result = context.TryLink("""
int main(int argc, char**argv)
{
	init_agent("PGM");
	return 0;
}
""", '.c');
	context.env.Replace(LIBS = lastLIBS, CCFLAGS=lastCCFLAGS);
	context.Result(not result);
	return result;

def CheckCheck(context):
	context.Message('Checking Check unit test framework...');
	result = context.TryAction('pkg-config --cflags --libs check')[0];
	context.Result(result);
	return result;

def CheckEventFD(context):
	context.Message('Checking eventfd...');
	result = context.TryLink("""
#include <sys/eventfd.h>
int main(int argc, char**argv)
{
	eventfd(0,0);
	return 0;
}
""", '.c')
	context.Result(result);
	return result;

tests = {
	'CheckCheck':	CheckCheck,
	'CheckEventFD':	CheckEventFD
}
if env['WITH_SNMP'] == 'true':
	tests['CheckSNMP'] = CheckSNMP;
conf = Configure(env, custom_tests = tests);

if env['WITH_SNMP'] == 'true' and not conf.CheckSNMP():
	print 'Enabling extra Red Hat dependencies for Net-SNMP.';
	conf.env['SNMP_FLAGS']['LIBS'].append(['librpm', 'libsensors', 'libdl', 'libwrap']);
	lastLIBS = conf.env['LIBS'];
	conf.env.ParseConfig('perl -MExtUtils::Embed -e ldopts');
	conf.env['SNMP_FLAGS']['LIBS'].append(conf.env['LIBS']);
	conf.env.Replace(LIBS = lastLIBS);
	if not conf.CheckSNMP():
		print 'Net-SNMP libraries not compatible.';
		Exit(1);

if env['WITH_CHECK'] == 'true' and conf.CheckCheck():
	print 'Enabling Check unit tests.';
	conf.env['CHECK'] = 'true';
else:
	print 'Disabling Check unit tests.';
	conf.env['CHECK'] = 'false';

if conf.CheckEventFD():
	print 'Enabling kernel eventfd notification mechanism.';
	conf.env.Append(CCFLAGS = '-DCONFIG_EVENTFD');

env = conf.Finish();

# DSO visibility flags
if '-DCONFIG_HAVE_DSO_VISIBILITY' in env['CCFLAGS']:
	env.Append(CCFLAGS = '-DPGM_GNUC_INTERNAL=G_GNUC_INTERNAL')
else:
	env.Append(CCFLAGS = '-DPGM_GNUC_INTERNAL=')

# add builder to create PIC static libraries for including in shared libraries
action_list = [ Action("$ARCOM", "$ARCOMSTR") ];
if env.Detect('ranlib'):
	ranlib_action = Action("$RANLIBCOM", "$RANLIBCOMSTR");
	action_list.append(ranlib_action);
pic_lib = Builder(	action = action_list,
			emitter = '$LIBEMITTER',
			prefix = '$LIBPREFIX',
			suffix = '$LIBSUFFIX',
			src_suffix = '$OBJSUFFIX',
			src_builder = 'SharedObject')
env.Append(BUILDERS = {'StaticSharedLibrary': pic_lib});


#-----------------------------------------------------------------------------

ref_node = 'ref/' + env['BUILD'] + '/';
BuildDir(ref_node, '.', duplicate=0)

env.Append(CPPPATH = os.getcwd() + '/include');
env.Append(LIBPATH = os.getcwd() + '/' + ref_node);

SConscript(ref_node + 'SConscript.libpgm');
SConscript(ref_node + 'SConscript.libpgmex');
if env['WITH_HTTP'] == 'true':
	SConscript(ref_node + 'SConscript.libpgmhttp');
if env['WITH_SNMP'] == 'true':
	SConscript(ref_node + 'SConscript.libpgmsnmp');
if env['WITH_TEST'] == 'true':
	SConscript(ref_node + 'test/SConscript');
if env['WITH_EXAMPLES'] == 'true':
	SConscript(ref_node + 'examples/SConscript');

# end of file
