# -*-python-*-
# This is the main SCons script which defines compilation rules.
# See https://www.scons.org/ for more information.
#
# If you wish to tweak the build system but aren't familiar with SCons,
# then simply edit these variables:

# Extra directories in which the C(++) compiler should look for
# header files.
# Example: EXTRA_INCLUDE_DIRECTORIES = ['/foo', '/bar']
EXTRA_INCLUDE_DIRECTORIES = []

# Extra directories in which the linker should search for libraries.
# Example: EXTRA_LIBRARY_DIRECTORIES = ['/opt/foo/lib', '/tmp/bar/lib']
EXTRA_LIBRARY_DIRECTORIES = []

# Extra arguments to be passed to the compiler during the compilation
# stage (not during the linking stage).
# EXTRA_COMPILER_FLAGS = ['-Wall', '-g', '-O2', '-pipe']

# Optimized Flags. Use only on Release.
EXTRA_COMPILER_FLAGS = ['-Wall', '-O3', '-pipe']

# Brew's readline
DARWIN_INCLUDE_DIRECTORIES = ['/usr/local/opt/readline/include']
DARWIN_LIBRARY_DIRECTORIES = ['/usr/local/opt/readline/lib']

# Fixes for OS X (src/auto/XSTools/darwin/include)
DARWIN_INCLUDE_DIRECTORIES += ['darwin/include']

####################

import os
import sys
import subprocess

### Platform configuration ###

platform = str(ARGUMENTS.get('OS', Platform()))
cygwin = platform == "cygwin"
darwin = platform == "darwin"
win32 = cygwin or platform == "win32"
have_ncurses = False
READLINE_LIB = 'readline'

perlconfig = {}
env = Environment()
if win32:
	env = Environment(
		ENV = {
			'PATH' : os.environ['PATH'], 
			'TEMP' : os.environ['TEMP'],
			'TMP' : os.environ['TMP'],
		}, 
		tools = ['mingw', 'gcc', 'cc', 'g++', 'c++'],
	)

if darwin:
	env['LIBPATH'] = DARWIN_LIBRARY_DIRECTORIES
	env['CPPPATH'] = DARWIN_INCLUDE_DIRECTORIES

def CheckPerl(context):
	global cygwin
	global win32
	global perlconfig

	context.Message('Checking Perl configuration ...')
	source = '''
	use strict;
	use Config;
	use File::Spec;

	sub search {
		my $paths = shift;
		my $file = shift;
		foreach (@{$paths}) {
			if (-f "$_/$file") {
				return "$_/$file";
				last;
			}
		}
		return;
	}

	my $coredir = File::Spec->catfile($Config{archlibexp}, "CORE");

	open(F, ">", ".perlconfig.txt");
	print F "perl=$Config{perlpath}\\n";
	print F "typemap=" . search(\\@INC, "ExtUtils/typemap") . "\\n";
	print F "xsubpp=" . search(\\@INC, "ExtUtils/xsubpp" || search([File::Spec->path()], "xsubpp")) . "\\n";
	print F "coredir=$coredir\\n";
	print F "perlversion=" . $^V . "\\n";
	close F;
	'''

	if sys.version_info >= (3,0,0):
		f = open(".perltest.pl", "w")
	else:
		f = file(".perltest.pl", "w")

	f.write(source)
	f.close()

	if win32:
		if cygwin:
			# Maybe the user just installed ActivePerl and wperl isn't
			# in PATH yet. So add the default Perl installation folder
			# to PATH.
			os.environ['PATH'] += os.path.pathsep + "/cygdrive/c/Perl/bin"
		ret = subprocess.call(["wperl", ".perltest.pl"])
	else:
		ret = subprocess.call(["perl", ".perltest.pl"])
	context.Result(ret == 0)

	os.unlink(".perltest.pl")
	if ret == 0:
		if sys.version_info >= (3,0,0):
			f = open(".perlconfig.txt", "r")
		else:
			f = file(".perlconfig.txt", "r")

		while 1:
			line = f.readline()
			if line == "":
				break
			line = line.rstrip("\n")
			line = line.rstrip("\r")
			[name, value] = line.split("=", 2)
			perlconfig[name] = value
		f.close()
		os.unlink(".perlconfig.txt")

		if cygwin:
			# Convert paths to Cygwin-compatible paths
			def cygpath(path):
				f = os.popen('cygpath --unix "' + path + '"', 'r')
				if f == None:
					return path
				else:
					line = f.readline()
					line = line.rstrip("\n")
					line = line.rstrip("\r")
					f.close()
					return line
			perlconfig['perl'] = cygpath(perlconfig['perl'])
			perlconfig['coredir'] = cygpath(perlconfig['coredir'])
	return ret == 0

def CheckReadline(context, conf):
	context.Message('Checking for GNU readline 4.3 or higher...')
	result = context.TryCompile("""
		#include <stdio.h>
		#include <readline/readline.h>
		#if !defined(RL_READLINE_VERSION)
			#error "You do not have the GNU readline development headers installed!"
		#elif RL_READLINE_VERSION < 0x0403
			#error "Your version of GNU readline is too old. Please install version 4.3 or higher."
		#endif
""", '.c')
	context.Result(result)
	return result

def CheckLibCurl(context):
	context.Message('Checking for libcurl...')
	p = subprocess.Popen('curl-config --version', shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True, universal_newlines=True)
	(input, output, error) = (p.stdin, p.stdout, p.stderr)
	if input != None:
		input.close()
	if error != None:
		error.close()
	if output != None:
		version = "\n".join(output.readlines())
		output.close()
		version = version.rstrip("\n")
		version = version.rstrip("\r")
		result = version
	else:
		result = False
	context.Result(result)
	return result


conf = Configure(env, custom_tests = {
	'CheckPerl' : CheckPerl,
	'CheckReadline' : CheckReadline,
	'CheckLibCurl'  : CheckLibCurl
})
if not conf.CheckPerl():
	print ("You do not have Perl installed! Read:")
	print ("https://openkore.com/wiki/How_to_run_OpenKore#Perl")
	Exit(1)
if not win32:
	have_ncurses = conf.CheckLib('ncurses')
	if not conf.CheckReadline(conf):
		print ("You don't have GNU readline installed, or your version of GNU readline is not recent enough! Read:")
		print ("https://openkore.com/wiki/How_to_run_OpenKore#GNU_readline")
		Exit(1)

	if darwin:
		has_readline_5 = conf.CheckLib('readline.5')
		sys.stdout.write('Checking whether Readline 5 is available...')
		sys.stdout.flush
		if has_readline_5:
			READLINE_LIB = 'readline.5'
			sys.stdout.write(" yes\n")
		else:
			sys.stdout.write(" no\n")

	if not conf.CheckLibCurl():
		print ("You don't have libcurl installed. Please download it at:");
		print ("https://curl.se/libcurl/");
		Exit(1)
conf.Finish()


### Environment setup ###

# Standard environment for programs
env['CCFLAGS'] = [] + EXTRA_COMPILER_FLAGS
env['LINKFLAGS'] = []
perlversion = int(''.join(perlconfig['perlversion'][1:].split('.')[:2]))

if win32:
	import platform
	# have to use -static-libgcc while compiling with mingw’s g++ to eliminate the dependency on LIBGCC_S_SJLJ-1.DLL
	if(perlversion == 512):
		env['LINKFLAGS'] += ['-static-libgcc']

	if "64" in platform.machine():
		env['CCFLAGS'] += ['-fpermissive', '-DWINx86_64']


env['LIBPATH'] = [] + EXTRA_LIBRARY_DIRECTORIES
env['LIBS'] = []
env['CPPDEFINES'] = []
env['CPPPATH'] = [] + EXTRA_INCLUDE_DIRECTORIES
if cygwin:
	env['CCFLAGS'] += ['-mno-cygwin']
	env['LINKFLAGS'] += ['-mno-cygwin']
env.Replace(CXXFLAGS = env['CCFLAGS'])


# Environment for libraries
libenv = env.Clone()
if win32:
	if cygwin:
		libenv['CCFLAGS'] += ['-mdll']
	libenv['CPPDEFINES'] += ['WIN32']
	if(perlversion > 512):
		env['CPPDEFINES'] += ['PERL_EUPXS_ALWAYS_EXPORT']
elif not darwin:
	libenv['CCFLAGS'] += ['-fPIC']
	libenv['LINKFLAGS'] += ['-fPIC']
else:
	env['LIBPATH'] += DARWIN_LIBRARY_DIRECTORIES
	env['CPPPATH'] += DARWIN_INCLUDE_DIRECTORIES
libenv.Replace(CXXFLAGS = libenv['CCFLAGS'])

if cygwin:
	# We want to build native Win32 DLLs on Cygwin
	def linkDLLAction(target, source, env):
		sources = []
		for f in source:
			sources += [str(f)]

		(temp, dllname) = os.path.split(str(target[0]))
		(targetName, temp) = os.path.splitext(str(target[0]))
		command = ['dlltool', '--dllname', dllname,
			'-z', targetName + '.def',
			'-l', targetName + '.lib',
			'--export-all-symbols',
			'--add-stdcall-alias'] + sources
		print (' '.join(command))
		ret = subprocess.call(command)
		if ret != 0:
			return 0

		command = ['dllwrap', '--driver=g++', '--target=i386-mingw32',
			'--def', targetName + '.def', '-mno-cygwin'] + \
			sources + ['-o', str(target[0])]
		if env.has_key('LIBPATH'):
			for dir in env['LIBPATH']: command += ['-L' + dir]
		if env.has_key('LIBS'):
		 	for flag in env['LIBS']: command += ['-l' + flag]

		command += ['-lstdc++']

		print (' '.join(command))
		return subprocess.call(command)

	NativeDLLBuilder = Builder(action = linkDLLAction,
		emitter = '$LIBEMITTER',
		suffix = 'dll',
		src_suffix = '$OBJSUFFIX',
		src_builder = 'SharedObject')
elif darwin:
	def linkBundleAction(target, source, env):
		sources = []
		for f in source:
			sources += [str(f)]

		command = [env['CXX'], '-flat_namespace', '-bundle',
			'-undefined', 'dynamic_lookup',
			'-o', str(target[0])] + sources
		if env.has_key('LIBPATH'):
			for dir in env['LIBPATH']: command += ['-L' + dir]
		if env.has_key('LIBS'):
		 	for flag in env['LIBS']: command += ['-l' + flag]

		print (' '.join(command))
		return subprocess.call(command)

	NativeDLLBuilder = Builder(action = linkBundleAction,
				   emitter = '$LIBEMITTER',
				   prefix = '',
				   suffix = 'bundle',
				   src_suffix = '$OBJSUFFIX',
				   src_builder = 'SharedObject')
else:
	NativeDLLBuilder = libenv['BUILDERS']['SharedLibrary']
libenv['BUILDERS']['NativeDLL'] = NativeDLLBuilder


# Environment for Perl libraries
perlenv = libenv.Clone()
if win32:
	rawversion = "perl" + ''.join(perlconfig['perlversion'][1:].split('.')[:2])

	# Windows
	perlenv['CCFLAGS'] += Split('-s -Wno-comments -fwrapv -fno-strict-aliasing -mms-bitfields')
	perlenv['CPPDEFINES'] += Split('__USE_MINGW_ANSI_STDIO PERL_TEXTMODE_SCRIPTS PERL_IMPLICIT_CONTEXT PERL_IMPLICIT_SYS USE_PERLIO')
	perlenv['LIBS'] += [rawversion]
	perlenv['LIBPATH'] += [perlconfig['coredir']]
elif not darwin:
	# Unix (except MacOS X)
	perlenv['CCFLAGS'] += Split('-D_REENTRANT -D_GNU_SOURCE' +
		' -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64')
else:
	# MacOS X
	perlenv['CCFLAGS'] += ['-no-cpp-precomp', '-DPERL_DARWIN',
			      '-fno-strict-aliasing']
	perlenv['LIBS'] += ['perl']
	perlenv['LIBPATH'] += [perlconfig['coredir']]
	# Add default Fink header directory to header search path.
	# But give system's default include paths higher priority.
	perlenv['CPPPATH'] += ['/usr/include', '/usr/local/include', '/sw/include']
	perlenv['LIBPATH'] += DARWIN_LIBRARY_DIRECTORIES
	perlenv['CPPPATH'] += DARWIN_INCLUDE_DIRECTORIES

perlenv['CPPPATH'] += [perlconfig['coredir']]
perlenv['CCFLAGS'] += ['-DVERSION=\\"1.0\\"', '-DXS_VERSION=\\"1.0\\"']
perlenv.Replace(CXXFLAGS = perlenv['CCFLAGS'])


def buildXS(target, source, env):
	global perlconfig

	code = '''
	use strict;
	my $out = shift;
	my $file = shift;

	close STDOUT;
	if (!open(STDOUT, ">", $out)) {
		print STDERR "Cannot open $out for writing: $!\n";
		exit 1;
	}
	select STDOUT;
	do $file;
	'''

	print ("Creating", str(target[0]), "...")
	command = [
		perlconfig['perl'],
		'-e',
		code,
		str(target[0]),
		perlconfig['xsubpp'],
		'-C++',
		'-typemap',
		perlconfig['typemap'],
		str(source[0])]
	return subprocess.call(command)

perlenv.Append(BUILDERS = { 'XS' : Builder(action = buildXS) })


### Invoke SConscripts ###

Export('env libenv perlenv platform win32 cygwin darwin have_ncurses READLINE_LIB')
sconscripts = []
if not int(ARGUMENTS.get('TESTS_ONLY', 0)):
	sconscripts += ['src/auto/XSTools/SConscript']
sconscripts += ['src/test/SConscript']
SConscript(sconscripts)
