#! /usr/bin/env python
# encoding: utf-8

from __future__ import with_statement # needed for python 2.5

import tarfile, os.path, os, re, patch, sys, functools, shutil
from waflib import Utils, Errors, Logs

def unpack_archive (src_dir_node, name, filename = None):
	src_dir = src_dir_node.abspath()
	path = os.path.join (src_dir, name)

	if not os.path.isdir (path):
		# extract the sources
		os.makedirs (path)

		if not filename:
			filename = "%s.tar.gz" % name

		Logs.pprint ("NORMAL", "Unpacking %s" % filename)
		t = tarfile.open (os.path.join (src_dir, filename))
		t.extractall (src_dir)

	node = src_dir_node.find_dir (name)
	assert node
	return node

def convert_path_win2msys (path):
	path = os.path.abspath (path)
	drv, path = os.path.splitdrive (path)

	return "/%s%s" % (drv[0], path.replace("\\", "/"))


def configure (conf):
	conf.env.BUILD_GDTOA	= False
	conf.env.BUILD_MATHLIB	= False
	
	conf.env.BUILD_GAOL	= False
	conf.env.BUILD_FILIB = False
	conf.env.BUILD_CLP = False
	
	conf.env.BUILD_AMPL = False

	extract = functools.partial (unpack_archive, conf.path)

	ibex_env = conf.env
	def ibex_append_path (var, p):
		if isinstance (p, str):	
			p = [p]

		ibex_env.append_unique (var, [
			d if os.path.isabs (d) else os.path.join ("..", "3rd", d)
			for d in p
		])
	
	enable_shlib = conf.env.ENABLE_SHARED
	with_bias = conf.options.BIAS_PATH
	with_gaol = conf.options.GAOL_PATH
	with_filib = conf.options.FILIB_PATH
	
	with_soplex = conf.options.SOPLEX_PATH
	with_cplex = conf.options.CPLEX_PATH
	with_clp = conf.options.CLP_PATH
	
	
	def join (path, *k):
		return os.path.abspath (os.path.join (os.path.expanduser (path), *k)) if path else None
	def use_shlib():
		if enable_shlib:
			# configure the environment to produce PIC objects
			conf.env.CFLAGS.extend   (conf.env.CFLAGS_cshlib)
			conf.env.CXXFLAGS.extend (conf.env.CXXFLAGS_cxxshlib)

		# return true if we build as a shared library
		return enable_shlib
	
	def selected (with_value):
		return with_value is not None

	#####################################################################################################
	# allow only one interval lib
	
	if conf.env.WITHOUT_ROUNDING:
		with_any = False
		for w in with_bias, with_gaol, with_filib:
			if w is not None:
					conf.fatal ("cannot use --with-gaol/--with-bias/--with-filib with the option --without-rounding")
		conf.env.INTERVAL_LIB = "DIRECT"
		conf.env.append_unique ("CXXFLAGS_IBEX_DEPS", ["-frounding-math","-ffloat-store"])
		Logs.pprint ("BLUE","The rounding mode of the Interval arithmetic is disable.")
		
	else :
		with_any = False
		for w in with_bias, with_gaol, with_filib:
			if w is not None:
				if with_any:
					conf.fatal ("cannot use --with-gaol/--with-bias/--with-filib together")
				with_any = True
	
		if not with_any:
			# try to find FILIB
			has_h = conf.check_cxx (header_name	= "interval/interval.hpp",mandatory=False)
			has_l = conf.check_cxx (lib = ["prim"],	uselib_store = "IBEX_DEPS",mandatory=False)
			
			if (has_h and has_l):
				with_any = True
				conf.env.INTERVAL_LIB = "FILIB"
				if conf.env.COMPILER_CXX == "g++":
					conf.env.append_unique ("CXXFLAGS_IBEX_DEPS", ["-frounding-math","-ffloat-store"])
					
				if conf.env.DEST_CPU == "x86" and not conf.options.DISABLE_SSE2:
					conf.env.append_unique ("CXXFLAGS_IBEX_DEPS", ["-msse2", "-mfpmath=sse"])
					
				Logs.pprint ("BLUE","The Interval arithmetic is the installed version of FILIP")
	
		if not with_any:
			# try to find GAOL
			has_h = conf.check_cxx (header_name = ["gaol/gaol.h", "gaol/gaol_interval.h"],mandatory=False)
			has_l = conf.check_cxx (lib=["gaol", "ultim", "gdtoa"], uselib_store="IBEX_DEPS",mandatory=False)
			
			if (has_h and has_l):
				with_any = True
				conf.switch_to_32bits()
				conf.env.INTERVAL_LIB = "GAOL"
				Logs.pprint ("BLUE","The Interval arithmetic is the installed version of GAOL")
				
	
		if not with_any:
			# try to find Profil/BIAS
			has_h = conf.check_cxx (header_name	= "BIAS/BiasF.h",mandatory=False)
			has_l = conf.check_cxx (lib = ["Profil", "Bias", "lr"],	uselib_store = "IBEX_DEPS",mandatory=False)
			
			if (has_h and has_l):
				with_any = True
				conf.env.INTERVAL_LIB = "BIAS"
				Logs.pprint ("BLUE","The Interval arithmetic is the installed version of Profil/BIAS")
	
		if not with_any:
			# no lib selected
			# -> will build one if needed
			if (conf.env.DEST_CPU == "x86_64") or (conf.env.DEST_OS=="win32"):
				# gaol not working on 64 bit cpu -> use filib instead
				Logs.pprint ("BLUE","By Default, the Interval arithmetic is FILIP")
				with_filib = ''
			else:
				Logs.pprint ("BLUE","By Default, the Interval arithmetic is GOAL")
				with_gaol = ''
		##########################
	
	if with_bias is not None:
		# build with bias

		conf.env.INTERVAL_LIB = "BIAS"

		if with_bias:
			conf.env.append_unique ("INCLUDES", join (with_bias, "include"))

		conf.check_cxx (header_name	= "BIAS/BiasF.h")

		kw = {"libpath": join (with_bias, "lib")} if with_bias else {}
		conf.check_cxx (lib = ["Profil", "Bias", "lr"],
				uselib_store = "IBEX_DEPS", **kw)

	elif with_filib is not None:
		# build with filib

		conf.env.INTERVAL_LIB = "FILIB"

		mandatory = bool (with_filib)

		if with_filib:
			conf.env.append_unique ("INCLUDES", join (with_filib, "include"))

		has_h = conf.check_cxx (
				header_name	= "interval/interval.hpp",
				mandatory	= mandatory)

		kw = {"libpath": join (with_filib, "lib")} if with_filib else {}
		has_l = conf.check_cxx (
				lib = ["prim"],
				uselib_store = "IBEX_DEPS",
				mandatory = mandatory,
				**kw)

		if not (has_h and has_l):
			conf.env.BUILD_FILIB = True

		if conf.env.COMPILER_CXX == "g++":
			conf.env.append_unique ("CXXFLAGS_IBEX_DEPS", ["-frounding-math","-ffloat-store"])


			if conf.env.DEST_CPU == "x86" and not conf.options.DISABLE_SSE2:
				conf.env.append_unique ("CXXFLAGS_IBEX_DEPS", ["-msse2", "-mfpmath=sse"])
				
	elif with_gaol is not None:
		# build with gaol
		conf.env.INTERVAL_LIB = "GAOL"

		mandatory = bool (with_gaol)
		conf.switch_to_32bits()
		if with_gaol:
			conf.env.append_unique ("INCLUDES", join (with_gaol, "include"))
			
		mandatory = bool (with_gaol)

		has_h = conf.check_cxx (
				header_name = ["gaol/gaol.h", "gaol/gaol_interval.h"],
				mandatory = mandatory)

		kw = {"libpath": join (with_gaol, "lib")} if with_gaol else {}
		has_l = conf.check_cxx (lib=["gaol", "ultim", "gdtoa"], uselib_store="IBEX_DEPS",
					mandatory = mandatory, **kw)

		if not (has_h and has_l):
			conf.env.BUILD_GAOL = True
			
	#####################################################################################################
	# allow only one linear solver
	with_any_solver = False
	for w in with_soplex, with_cplex, with_clp:
		if w is not None:
			if with_any_solver:
				conf.fatal ("cannot use --with-cplex/--with-soplex/--with-clp together")
			with_any_solver = True
			
	if (with_any_solver and  conf.env.WITHOUT_LP):
		conf.fatal ("cannot use --without-lp and  (--with-cplex/--with-soplex/--with-clp) option")
	
	if 	conf.env.WITHOUT_LP:
		conf.env.LP_LIB = "NOLP"
	else:	
		# check if SOPLEX is installed		
		if not with_any_solver: 
			has_Soplex_h = conf.check_cxx (header_name	= "soplex.h",mandatory = False)
			has_Soplexlib =  conf.check_cxx (lib = "soplex", uselib_store = "IBEX_DEPS",
						libpath = [],
						mandatory = False,
						fragment = """
							#include <soplex.h>
							int main (int argc, char* argv[]) {
								soplex::SPxLP lp;
								lp.read(std::cin);
								return 0;
							}
						""")
			if not has_Soplexlib:
				has_Soplexlib = conf.check_cxx (lib = ["soplex", "z"], uselib_store = "IBEX_DEPS",
						libpath = [],
						mandatory = False,
						fragment = """
							#include <soplex.h>
							int main (int argc, char* argv[]) {
								soplex::SPxLP lp;
								lp.read(std::cin);
								return 0;
							}
						""")
				
			if (has_Soplex_h and has_Soplexlib):
				conf.env.LP_LIB = "SOPLEX"
				with_any_solver = True
				Logs.pprint ("BLUE","The Linear Solver is the installed version of SOPLEX")
			
			
		# check if CPLEX is installed		
		if not with_any_solver: 			
			has_Cplex_h = conf.check_cxx (header_name	= "ilcplex/cplex.h",mandatory = False)
			has_Cplexlib =  conf.check_cxx (lib = ["cplex", "pthread"], uselib_store = "IBEX_DEPS",
							libpath = [],  
							mandatory = False,
							fragment = """
								#include "ilcplex/cplex.h"
								int main (int argc, char* argv[]) {
									CPXENVptr  envcplex;
									CPXLPptr lpcplex;
									return 0;
								}
							""")
				
			if (has_Cplex_h and has_Cplexlib):
				conf.env.LP_LIB = "CPLEX"
				with_any_solver = True
				Logs.pprint ("BLUE","The Linear Solver is the installed version of CPLEX")
				
		################################		
		# check if CLP is installed	
		if not with_any_solver: 
			has_Clp_h = conf.check_cxx (header_name	= "coin/ClpSimplex.hpp",mandatory = False)
			has_Clplib = conf.check_cxx (lib = ["Clp","CoinUtils","z","m"], uselib_store = "IBEX_DEPS",
										libpath = [],
										fragment = """
							#include "coin/ClpSimplex.hpp"
							int main (int argc, char* argv[]) {
								ClpSimplex 	myclp;
								myclp.resize(2,2);
								return 0;
							} """, mandatory = False)	
			if not has_Clplib:
				has_Clplib = conf.check_cxx (lib = ["Clp","CoinUtils","m"], uselib_store = "IBEX_DEPS",
											libpath = [],
											fragment = """
								#include "coin/ClpSimplex.hpp"
								int main (int argc, char* argv[]) {
									ClpSimplex 	myclp;
									myclp.resize(2,2);
									return 0;
								} """, mandatory = False)	
				
			if (has_Clp_h and has_Clplib):
				conf.env.LP_LIB = "CLP"
				with_any_solver = True
				Logs.pprint ("BLUE","The Linear Solver is the installed version of CLP")
			
		# no Linear Solver is install
		if not with_any_solver: 
			Logs.pprint ("BLUE","By Default, we install the Linear Solver Clp-1.15.6 from COIN-OR project")
			conf.env.LP_LIB = "CLP"
			conf.env.BUILD_CLP = True
			
		################################
		# build with cplex
		if with_cplex is not None:
			
			conf.env.LP_LIB = "CPLEX"
			conf.msg ("Candidate directory for lib Cplex", with_cplex)
	
			conf.env.append_unique ("INCLUDES",  os.path.join (with_cplex, "cplex/include/"))
			conf.check_cxx (header_name	= "ilcplex/cplex.h")
			
			dirtmp1 = os.path.join(with_cplex, "cplex/lib/")
			for pp in os.listdir(dirtmp1) :
				dirtmp2= os.path.join (dirtmp1,pp)
				if (os.path.isdir(dirtmp2)):
					if (not(conf.check_cxx (lib = ["cplex", "pthread"], uselib_store = "IBEX_DEPS",
							libpath = [os.path.join(dirtmp2, "static_pic/")],  
							mandatory = False,
							fragment = """
								#include "ilcplex/cplex.h"
								int main (int argc, char* argv[]) {
									CPXENVptr  envcplex;
									CPXLPptr lpcplex;
									return 0;
								}
							"""))):
						conf.fatal ("cannot link with the Cplex library")			
			
			
			
		elif with_soplex is not None :
			# build with soplex
			conf.env.LP_LIB = "SOPLEX"
			conf.msg ("Candidate directory for lib Soplex", with_soplex)
	
			conf.env.append_unique ("INCLUDES",  os.path.join (with_soplex, "src"))
			conf.check_cxx (header_name	= "soplex.h")
	
			# Try without and with -lz (soplex may be built without zlib)
			for l in ("soplex", ["soplex", "z"]):
				if (conf.check_cxx (lib = l, uselib_store = "IBEX_DEPS",
						libpath = [os.path.join (with_soplex, "lib")],
						mandatory = False,
						fragment = """
							#include <soplex.h>
							int main (int argc, char* argv[]) {
								soplex::SPxLP lp;
								lp.read(std::cin);
								return 0;
							}
						""")):
					break
			else:
				conf.fatal ("cannot link with the Soplex library")
			
			
		elif with_clp is not None :
			
			if ((conf.env["INTERVAL_LIB"] is "GAOL") and (conf.env.DEST_CPU == "x86_64")):
				conf.fatal ("cannot use the Clp linear solver with Gaol on x86_64 processor, please install Soplex or Cplex, or use another Interval library (Filib or Profil/Bias).")
				
			# build with Clp
			conf.env.LP_LIB = "CLP"
			conf.msg ("Candidate directory for lib Clp", with_clp)
			mandatory = bool (with_clp)
			
			if with_clp:
				conf.env.append_unique ("INCLUDES", os.path.join (with_clp, "include"))
				
			
			has_Clp = conf.check_cxx (
					header_name	= "coin/ClpSimplex.hpp",
					mandatory	= mandatory)
	
			has_Clplib_z = conf.check_cxx (lib = ["Clp","CoinUtils","z","m"], uselib_store = "IBEX_DEPS",
										libpath = [os.path.join (with_clp, "lib")],
										fragment = """
							#include "coin/ClpSimplex.hpp"
							int main (int argc, char* argv[]) {
								ClpSimplex 	myclp;
								myclp.resize(2,2);
								return 0;
							} """, mandatory = False)	
			if not has_Clplib_z:
				has_Clplib = conf.check_cxx (lib = ["Clp","CoinUtils","m"], uselib_store = "IBEX_DEPS",
											libpath = [os.path.join (with_clp, "lib")],
											fragment = """
								#include "coin/ClpSimplex.hpp"
								int main (int argc, char* argv[]) {
									ClpSimplex 	myclp;
									myclp.resize(2,2);
									return 0;
								} """, mandatory = False)	
			else:
				has_Clplib = True
				
			if mandatory and (not (has_Clplib or has_Clplib_z)):
				conf.fatal ("cannot link with the Clp library")
				
			if not (has_Clp and (has_Clplib or has_Clplib_z)):
				conf.env.BUILD_CLP = True


	#####################################################################################################
	# AMPL configure
	if conf.env.WITH_AMPL:
	
		has_asl = conf.check_cxx (
				header_name	= "asl.h",
				mandatory	= False)
		has_arith = conf.check_cxx (
				header_name	= "arith.h",
				mandatory	= False)
		has_ampl = conf.check_cxx (
				lib = ["amplsolver"],
				uselib_store = "IBEX_DEPS",
				mandatory = False)
	
		if not (has_asl and has_arith and has_ampl):
			if (conf.env.WITH_AMPL):
				conf.env.BUILD_AMPL = True
		else:
			if (not conf.check_cxx (
				lib = ["dl"],
				uselib_store = "IBEX_DEPS",
				mandatory = False
				) ):
				conf.fatal ("cannot link with the dl library")
			
	
	#####################################################################################################
	# common vars for building gaol/gdtoa/mathlib
	gdtoa_name = "gdtoa-1.0"

	def log_start (name):
		Logs.pprint ("BLUE", "Starting configuration for %s" % name)
	
	if conf.env.BUILD_GAOL:
		# This will compile the bundled version of gaol
		#
		# default config is:    --disable-preserve-rounding
		#


		log_start ("gaol")
		conf.setenv ("gaol")
		#TODO: support config options

		conf.env.GAOL = "gaol-3.1.1"

		conf.load ("compiler_cxx compiler_cc")
		conf.switch_to_32bits()

		# check if we need to build gdtoa and/or mathlib
		if use_shlib():
			ibex_env.BUILD_GDTOA = True
			ibex_env.BUILD_MATHLIB = True
		else:
			if (	conf.check_cxx (header_name="gdtoa.h", mandatory=False) and
				conf.check_cxx (lib="gdtoa", mandatory=False)):

				ibex_env.append_unique ("LIB_IBEX_DEPS", "gdtoa")
			else:
				ibex_env.BUILD_GDTOA = True

			if conf.check_cxx (lib="ultim", mandatory=False):

				ibex_env.append_unique ("LIB_IBEX_DEPS", "ultim")
			else:
				ibex_env.BUILD_MATHLIB = True

		conf.env.append_unique ("INCLUDES", [conf.env.GAOL, gdtoa_name])
		ibex_append_path ("INCLUDES", conf.env.GAOL)

		conf.define ("GAOL_USING_APMATHLIB", 1)
		conf.define ("GAOL_VERBOSE_MODE", 1)

		if conf.env.CC_NAME != "msvc":
			conf.env.append_unique ("CXXFLAGS", "-O9 -funroll-loops -fomit-frame-pointer -fexpensive-optimizations".split())
			vis_flags = "-fvisibility-inlines-hidden -fvisibility=hidden".split()
			if conf.check_cxx (cxxflags=vis_flags, mandatory=False):
				conf.env.append_unique ("CXXFLAGS", vis_flags)
				conf.define ("HAVE_VISIBILITY_OPTIONS", 1)

		conf.env.FAST_MATH = "fast"

		conf.define ("GAOL_PRESERVE_ROUNDING", 0)   # --disable-preserve-rounding
		conf.define ("GAOL_CERTAINLY_RELATIONS", 1)
		conf.define ("GAOL_EXCEPTIONS_ENABLED", 1)
		conf.define ("GAOL_USING_ASM", 1)

		if sys.platform in ("cygwin", "win32"):
			# TODO: some better detection
			host = "i386-cygwin"
		else:
			host = "%s-%s" % (
				os.uname()[4],
				Utils.unversioned_sys_platform(),
			)

		for known, macro in (
			(".*-linux$", 		"IX86_LINUX"),
			(".*-cygwin$",		"IX86_CYGWIN"),
			(".*-darwin$", 		"IX86_LINUX"),
			("sparc.*-sunos$",	"SPARC_SOLARIS"),
			("powerpc-",		"POWERPC_UNIX"),
		):
			if re.match (known, host):
				conf.define (macro, 1)
				break
		else:
			conf.fatal ("Sorry, this system (%s) is not yet supported by gaol" % host)

		conf.env.IS_CYGWIN_COND = host.endswith("-cygwin")

		conf.check_cxx (lib = "dl", mandatory = False)
		if conf.check_cxx (lib = "m", mandatory = False):
			conf.define ("HAVE_NEXTAFTER", 1)

		conf.define ("STDC_HEADERS", 1)
		
		for h in "fenv.h float.h stddef.h stdlib.h string.h sys/time.h unistd.h stdbool.h".split():
			conf.check_cc (header_name = h, mandatory = False)

		for h in "limits cassert".split():
			conf.check_cxx (header_name = h, mandatory = False)

		if not conf.check ( 	mandatory = False, features = "c", msg = "Checking for const",
					fragment = "const int a = 1;"):
			conf.define ("const", "", False)

		conf.check_inline()

		if not conf.check ( 	mandatory = False, features = "c", msg = "Checking for size_t",
					fragment = "#include <sys/types.h>\nsize_t a();\n"):
			conf.define ("size_t", "unsigned int", False)

		if conf.check_endianness() != "little":
			conf.define ("WORDS_BIGENDIAN", 1)

		def check_sizeof (typ):
			# not cross-compilation compliant
			size = conf.check_cc (
				msg = "Checking size of %s" % typ,
				fragment =	"#include <stdio.h>\n"
						'int main() { printf ("%%d", sizeof(%s));\n'
						'return 0; }\n' % typ,
				execute = True,
				define_ret = True,
			)
			conf.define ("SIZEOF_%s" % typ.replace(" ", "_").upper(), size, False)

		
		check_sizeof ("int")
		check_sizeof ("long")
		check_sizeof ("long long int")

		if conf.env.CC_NAME != "msvc":
			rou_flags = "-frounding-math"
			if conf.check_cxx (cxxflags=rou_flags, mandatory=False):
				conf.env.append_unique ("CXXFLAGS", rou_flags)
				conf.define ("HAVE_ROUNDING_MATH_OPTION", 1)

		conf.env.append_unique ("CXXFLAGS", "-ffloat-store")

		for func, header, macro in (
			("fesetround",	"fenv.h",		None),
			("floor",	"math.h",		None),
			("localeconv",	"locale.h",		None),
			("memset",	"string.h",		None),
			("pow",		"math.h",		None),
			("sqrt",	"math.h",		None),
			("getrusage",	"sys/resource.h",	"GETRUSAGE_IN_HEADER"),
			("clock",	"time.h",		"CLOCK_IN_HEADER"),
			("__signbit",	"math.h",		None),
			("round",	"math.h",		None),
			("rint",	"math.h",		None),
			("finite",	"math.h",		None),
		):
			v = conf.check (features = "c", mandatory = False,
					function_name = func, header_name = header)
			if macro:
				conf.define (macro, 1 if v else 0)

		# fix to build for macosx
		if "HAVE_FENV_H=1" in conf.env.DEFINES:
			check_fenv_t_attrib = lambda attr, mandatory: conf.check (
				mandatory = mandatory, features = "c",
				msg = "Checking if fenv_t has .%s" % attr,
				fragment = """#include <fenv.h>\nvoid a(fenv_t* e) {
						unsigned short int c;
						c = e->%s;
					}\n""" % attr)

			if not check_fenv_t_attrib ("__control_word", False):
				check_fenv_t_attrib ("__control", True)
				# FIXME: may produce some side effects
				conf.define ("__control_word", "__control", False)

		conf.write_config_header ("%s/gaol/gaol_configuration.h" % conf.env.GAOL)

		# TODO: flex & bison

		conf.setenv ("")
	##################################
	if conf.env.BUILD_GDTOA:
		log_start ("gdtoa")
		conf.setenv ("gdtoa")

		conf.env.GDTOA = gdtoa_name
		gdtoa_includes = conf.env.GDTOA

		conf.load ("compiler_cxx compiler_cc")
		conf.switch_to_32bits()

		use_shlib()

		conf.define ("INFNAN_CHECK", 1)
		conf.define ("SET_INEXACT", 1)
		conf.define ("Honor_FLT_ROUNDS", 1)
	
		conf.env.append_unique ("INCLUDES", gdtoa_includes)
		ibex_append_path ("INCLUDES", gdtoa_includes)

		conf.setenv ("")

	if conf.env.BUILD_MATHLIB:
		log_start ("mathlib")
		conf.setenv ("mathlib")

		platform = Utils.unversioned_sys_platform()

		conf.env.MATHLIB = "mathlib-2.0.0" if platform != "darwin" else "mathlib-lion"
		
		conf.load ("compiler_cxx compiler_cc")
		conf.switch_to_32bits()

		use_shlib()

		conf.env.append_unique ("INCLUDES", os.path.join (conf.env.MATHLIB, "src"))

		if conf.env.CC_NAME != "msvc":
			conf.env.append_unique ("CFLAGS", ["-g0", "-O2"])

		for h in (	"dlfcn.h",
				"inttypes.h",
				"memory.h",
				"stdint.h",
				"stdlib.h",
				"strings.h",
				"string.h",
				"sys/stat.h",
				"sys/types.h",
				"unistd.h",
				"fenv.h",
		):
			conf.check_cc (header_name = h, mandatory = False)

		conf.define ("STDC_HEADERS", 1)
		conf.define ("VERSION", conf.env.MATHLIB.split("-")[1])

		try:
			conf.define ({
				"aix":		"MATHLIB_AIX",
				"cygwin":	"MATHLIB_CYGWIN",
				"win32":	"MATHLIB_CYGWIN",
				"linux":	"MATHLIB_LINUX",
				"darwin":	"MATHLIB_I86_MACOSX",
				"sunos":	"MATHLIB_SUN",

			}[platform], 1)

			if platform == "darwin":
				# this is defined in mathlib-lion
				# (don't know if it is really useful)
				conf.define ("IX86_CPU", 1)
		except KeyError:
			conf.fatal ("unknown platform: %s" % platform)

		conf.write_config_header ("%s/src/mathlib_configuration.h" % conf.env.MATHLIB)

		conf.setenv ("")
	
	##################################
	if conf.env.BUILD_FILIB:
		log_start ("filib")
		conf.env.FILIB = "filibsrc"
		filib_dir = extract (conf.env.FILIB, "filibsrc-3.0.2.2.tar.gz")

		ibex_append_path ("INCLUDES", conf.env.FILIB)
		ibex_append_path ("LIBPATH", os.path.join (filib_dir.abspath(), "libprim", ".libs"))

		ibex_env.append_unique ("INCLUDES_IBEX_DEPS", os.path.join (conf.env.PREFIX, "include"))
		ibex_env.append_unique ("LIB_IBEX_DEPS", "prim")

		conf.find_program ("make")

		shared = "--enable-shared" if conf.env.ENABLE_SHARED else ""

		if sys.platform == "win32":
			# mingw build
			conf.find_program ("sh")

			cmd = [conf.env.SH, "-c",
				"./configure --quiet --prefix=%r %s"
				% (convert_path_win2msys (conf.env.PREFIX), shared)]
		else:
			cmd =	"./configure --quiet --prefix=%r %s" % (conf.env.PREFIX, shared)

		if conf.exec_command (cmd, cwd=filib_dir.abspath(), stdout=None, stderr=None):
			conf.fatal ("failed to configure filib (%s)" % cmd)

	##################################
	# AMPL interface 
	# BUILD
	if conf.env.BUILD_AMPL:
		log_start ("AMPL")
		conf.env.AMPL="solvers"
		ampl_dir = extract (conf.env.AMPL, "amplsolvers.tar.gz")

		ibex_append_path ("INCLUDES", conf.env.AMPL)
		ibex_append_path ("LIBPATH", ampl_dir.abspath())

		ibex_env.append_unique ("INCLUDES_IBEX_DEPS", os.path.join (conf.env.PREFIX, "include"))
		ibex_env.append_unique ("LIB_IBEX_DEPS", ["amplsolver","dl"])

		conf.find_program ("make")
		cmd = "./configurehere "

		if conf.exec_command (cmd, cwd=ampl_dir.abspath(), stdout=None, stderr=None):
			conf.fatal ("failed to configure AMPL (%s)" % cmd)
	##################################
	# CLP
	# BUILD
	if conf.env.BUILD_CLP:
		log_start ("CLP")
		conf.env.CLP="Clp-1.15.6"
		clp_dir = extract (conf.env.CLP, "Clp-1.15.6.tgz")

		ibex_append_path ("INCLUDES", os.path.join (conf.env.PREFIX, "include"))
		ibex_append_path ("LIBPATH", os.path.join (conf.env.PREFIX, "lib"))

		ibex_env.append_unique ("INCLUDES_IBEX_DEPS", os.path.join (conf.env.PREFIX, "include"))
		ibex_env.append_unique ("LIB_IBEX_DEPS", ["Clp","CoinUtils","m"])
		
		conf.find_program ("make")
		shared = "--enable-shared" if conf.env.ENABLE_SHARED else ""
		
		if sys.platform == "win32":
			# mingw build
			conf.find_program ("sh")

			cmd = [conf.env.SH, "-c",
				"./configure --quiet --disable-zlib  --disable-bzlib --prefix=%r %s"
				% (convert_path_win2msys (conf.env.PREFIX), shared)]
		else:
			cmd =	"./configure --quiet --disable-zlib --disable-bzlib --prefix=%r %s" % (os.path.abspath (conf.env.PREFIX), shared)

		if conf.exec_command (cmd, cwd=clp_dir.abspath(), stdout=None, stderr=None):
			conf.fatal ("failed to configure CLP (%s)" % cmd)
			
	
######################################################################################################
def build (bld):
	# installation paths
	INCDIR = "${PREFIX}/include/ibex"
	# NOTE: we do not install the 3rd static libs when ibex is built as a shared library
	#	(they will be linked within the lib instead)
	LIBDIR = "${PREFIX}/lib/ibex" if not bld.env.ENABLE_SHARED else None

	def remove (filename):
		if os.path.isfile (filename):
			os.remove (filename)

	src_dir = bld.path.abspath()
	extract = functools.partial (unpack_archive, bld.path)

	def apply_patch (name):
		patch_file = os.path.join (src_dir, name)
		p = patch.fromfile (patch_file)
		try:
			prev_dir = os.getcwd()
			os.chdir (src_dir)
		except:
			raise
		else:
			if not p.apply():
				raise Errors.WafError ("Cannot apply patch %s" % patch_file)
		finally:
			os.chdir (prev_dir)
			
	##################################
	if bld.env["BUILD_GDTOA"]:
		# build gdtoa

		bld.variant = "gdtoa"

		def prfx (file_name):
			return os.path.join (bld.env.GDTOA, file_name)

		node = extract (bld.env.GDTOA)

		# apply a patch to have strtod() and strtol() match with stdlib.h
		apply_patch ("gdtoa-nothrow.patch")

		# apply a patch initialise Flt_Rounds with a default value
		# (becaues BSD's linker do not include uninitialised symbols by default)
		apply_patch ("gdtoa-initialise-flt-rounds.patch")

		cpat = bld.env.cprogram_PATTERN

		bld.program (
			target	= prfx ("arithchk"),
			source	= prfx ("arithchk.c"),
			install_path = False,
		)

		bld (
			target	= prfx ("arith.h"),
			source	= prfx (cpat % "arithchk"),
			rule	= "${SRC} > ${TGT}",
			install_path = INCDIR,
		)

		bld.program (
			target	= prfx ("qnan"),
			source	= prfx ("qnan.c"),
			install_path = False,
		)

		bld (
			target	= prfx ("gd_qnan.h"),
			source	= prfx (cpat % "qnan"),
			rule	= "${SRC} > ${TGT}",
			install_path = INCDIR,
		)

		bld.objects (
			source	= [prfx(n) for n in """
				dmisc.c dtoa.c g_Qfmt.c g__fmt.c g_ddfmt.c g_dfmt.c g_ffmt.c
				g_xLfmt.c g_xfmt.c gdtoa.c gethex.c gmisc.c hd_init.c hexnan.c
				misc.c smisc.c strtoIQ.c strtoId.c strtoIdd.c strtoIf.c strtoIg.c
				strtoIx.c strtoIxL.c strtod.c strtodI.c strtodg.c strtof.c strtopQ.c
				strtopd.c strtopdd.c strtopf.c strtopx.c strtopxL.c strtorQ.c
				strtord.c strtordd.c strtorf.c strtorx.c strtorxL.c sum.c ulp.c
 				""".split()],
			target	= "gdtoa_objs",
			install_path = LIBDIR,
		)

		bld.install_files (INCDIR, node.ant_glob("*.h"))
		
		bld.variant = ""
		
	##################################
	if bld.env["BUILD_MATHLIB"]:
		# build mathlib

		bld.variant = "mathlib"

		def prfx (file_name):
			return os.path.join (bld.env.MATHLIB, "src", file_name)

		node = extract (bld.env.MATHLIB)
		remove (os.path.join (node.abspath(), "src", "mathlib_configuration.h"))

		bld.objects (
			target = "ultim_objs",
			source = [prfx(n) for n in """
				atnat2.c    halfulp.c  mplog.c     slowexp2.c  ucot.c   upow.c 
				atnat.c     mpa.c      mpsqrt.c    slowexp.c   uexp2.c  urem.c 
				branred.c   mpatan2.c  mptan.c     slowlog2.c  uexp.c   uroot.c
				doasin.c    mpatan.c   sincos32.c  slowpow.c   ulog2.c  usncs.c
				dosincos.c  mpexp.c    slowcot.c   uasncs.c    ulog.c   utan.c 
				DPChange.c
				 """.split()],
			install_path = LIBDIR,
		)
				

		bld.variant = ""
	##################################
	if bld.env["BUILD_GAOL"]:
		# build gaol

		bld.variant = "gaol"

		def prfx (file_name):
			return os.path.join (bld.env.GAOL, "gaol", file_name)

		node = extract (bld.env.GAOL)
		remove (os.path.join (node.abspath(), "gaol", "gaol_double_op.h"))
		remove (os.path.join (node.abspath(), "gaol", "gaol_configuration.h"))
		apply_patch ("gaol-mingw.patch")

		GAOL_INCDIR = "%s/gaol" % INCDIR

		@bld.rule (
			target = prfx ("gaol_double_op.h"),
			source = prfx ("gaol_double_op_%s.h" % bld.env.FAST_MATH),
			install_path = GAOL_INCDIR,
		)
		def _(tsk):
			tsk.outputs[0].write (tsk.inputs[0].read())

		bld.install_files (GAOL_INCDIR, node.ant_glob (["gaol/*.h", "gaol/gaol"]))
		bld.install_files (os.path.join (GAOL_INCDIR, "sysdeps"), node.ant_glob ("gaol/sysdeps/*.h"))
		bld.install_files (GAOL_INCDIR, node.get_bld().find_or_declare ("gaol/gaol_configuration.h"))


		bld.objects (
			target = "gaol_objs",
			source = [prfx(n) for n in """
				gaol_interval.cpp gaol_profile.cpp gaol_common.cpp gaol_parser.cpp
				gaol_expression.cpp gaol_interval_parser.cpp gaol_interval_lexer.cpp
				gaol_port.cpp gaol_flt_output.cpp gaol_exceptions.cpp
				gaol_init_cleanup.cpp s_nextafter.c gaol_exact.c
				 """.split()],
			install_path = LIBDIR,
		)

		bld.variant = ""
	##################################
	if bld.env["BUILD_FILIB"]:
		bld.variant = "filib"
		path = bld.path.find_dir (bld.env.FILIB).abspath()

		with subdir (path):
			if bld.cmd=="install":
				cmd = [["install"]]
			elif bld.cmd in ("uninstall", "clean"): 	
				cmd = [["uninstall"],["distclean"]]
			else :
				cmd = []
			# Try without and with -lz (soplex may be built without zlib)
			for l in cmd:
				if bld.exec_command (
					[bld.env.MAKE, "-j", str(bld.options.jobs)] + l,
					cwd	= path,
					stdout	= None,
					stderr	= None,
				):
					bld.fatal ("failed to build filib")
	
		bld.variant = ""
	##################################
	if bld.env["BUILD_AMPL"]:
		bld.variant = "ampl"
		path = bld.path.find_dir (bld.env.AMPL).abspath()

		with subdir (path):
			if  (bld.env["INTERVAL_LIB"] is "GAOL") :
				cmd = ["clean"] if bld.cmd in ("clean", "uninstall") else ["CFLAGS=-DNo_dtoa -m32"]
			else:
				cmd = ["clean"] if bld.cmd in ("clean", "uninstall") else []
			
			if bld.exec_command (
				[bld.env.MAKE, "-j", str(bld.options.jobs)] + cmd,
				cwd	= path,
				stdout	= None,
				stderr	= None,
			):
				bld.fatal ("failed to build ampl")
	
			if bld.cmd=="install":
				if not os.path.exists(bld.env.LIBDIR):
					os.makedirs(bld.env.LIBDIR)
				shutil.copyfile(os.path.join(path,"libamplsolver.a"),os.path.join(bld.env.LIBDIR,"libamplsolver.a"))
			elif  bld.cmd in ("clean", "uninstall"):
				os.remove(os.path.join(bld.env.LIBDIR,"libamplsolver.a"))

		bld.variant = ""
		
	##################################
	if bld.env["BUILD_CLP"]:
		bld.variant = "Clp"
		path = bld.path.find_dir (bld.env.CLP).abspath()

		with subdir (path):
			if bld.cmd in ("install"):
				cmd = [bld.cmd]
			else :	
				cmd = ["clean"] if bld.cmd in ("uninstall", "clean") else ["install"]
			if bld.exec_command (
				[bld.env.MAKE, "-j", str(bld.options.jobs)] + cmd,
				cwd	= path,
				stdout	= None,
				stderr	= None,
			):
				bld.fatal ("failed to build Clp")
	
		bld.variant = ""


######################################################################################################
class subdir:
	def __init__ (self, name_or_node):
		self.name = name_or_node.abspath() if hasattr (name_or_node, "abspath") else str (name_or_node)

	def __enter__ (self):
		Logs.pprint ("BLUE", "Entering %r" % self.name)

	def __exit__ (self, a, b, c):
		Logs.pprint ("BLUE", "Leaving %r" % self.name)



def distclean (ctx):
	filib_dir = ctx.path.find_dir ("filibsrc")
	if filib_dir and os.path.exists (os.path.join (filib_dir.abspath(), "Makefile")):
		with subdir (filib_dir):
			ctx.exec_command ("make clean && make distclean", cwd = filib_dir.abspath())

	ampl_dir = ctx.path.find_dir ("solvers")
	if ampl_dir and os.path.exists (os.path.join (ampl_dir.abspath(), "Makefile")):
		with subdir (ampl_dir):
			ctx.exec_command ("make clean && make distclean", cwd = ampl_dir.abspath())

