#!/usr/bin/env python
# encoding: utf-8
# Matthias Jahn, 2008, jahn matthias ath freenet punto de
# Thomas Nagy, 2008 (ita)

import sys, re, os, optparse

import TaskGen, Task, Utils, preproc
from Logs import error, debug, warn
from TaskGen import taskgen, after, before, feature

REVISION="0.1.3"

"""
if you want to use the code here, you must use something like this:
obj = obj.create(...)
obj.features.append("libtool")
obj.vnum = "1.2.3" # optional, but versioned libraries are common
"""

# fake libtool files
fakelibtool_vardeps = ['CXX', 'PREFIX']
def fakelibtool_build(task):
	# Writes a .la file, used by libtool
	env = task.env
	dest  = open(task.outputs[0].abspath(env), 'w')
	sname = task.inputs[0].name
	fu = dest.write
	fu("# Generated by ltmain.sh - GNU libtool 1.5.18 - (pwn3d by BKsys II code name WAF)\n")
	if env['vnum']:
		nums = env['vnum'].split('.')
		libname = task.inputs[0].name
		name3 = libname+'.'+env['vnum']
		name2 = libname+'.'+nums[0]
		name1 = libname
		fu("dlname='%s'\n" % name2)
		strn = " ".join([name3, name2, name1])
		fu("library_names='%s'\n" % (strn) )
	else:
		fu("dlname='%s'\n" % sname)
		fu("library_names='%s %s %s'\n" % (sname, sname, sname) )
	fu("old_library=''\n")
	vars = ' '.join(env['libtoolvars']+env['LINKFLAGS'])
	fu("dependency_libs='%s'\n" % vars)
	fu("current=0\n")
	fu("age=0\nrevision=0\ninstalled=yes\nshouldnotlink=no\n")
	fu("dlopen=''\ndlpreopen=''\n")
	fu("libdir='%s/lib'\n" % env['PREFIX'])
	dest.close()
	return 0

def read_la_file(path):
	sp = re.compile(r'^([^=]+)=\'(.*)\'$')
	dc={}
	file = open(path, "r")
	for line in file.readlines():
		try:
			#print sp.split(line.strip())
			_, left, right, _ = sp.split(line.strip())
			dc[left]=right
		except ValueError:
			pass
	file.close()
	return dc

@feature("libtool")
@after('apply_link')
def apply_link_libtool(self):
	if self.type != 'program':
		linktask = self.link_task
		self.latask = self.create_task('fakelibtool', linktask.outputs, linktask.outputs[0].change_ext('.la'))

	if self.bld.is_install:
		self.bld.install_files('${PREFIX}/lib', linktask.outputs[0], self.env)

@feature("libtool")
@before('apply_core')
def apply_libtool(self):
	self.env['vnum']=self.vnum

	paths=[]
	libs=[]
	libtool_files=[]
	libtool_vars=[]

	for l in self.env['LINKFLAGS']:
		if l[:2]=='-L':
			paths.append(l[2:])
		elif l[:2]=='-l':
			libs.append(l[2:])

	for l in libs:
		for p in paths:
			dict = read_la_file(p+'/lib'+l+'.la')
			linkflags2 = dict.get('dependency_libs', '')
			for v in linkflags2.split():
				if v.endswith('.la'):
					libtool_files.append(v)
					libtool_vars.append(v)
					continue
				self.env.append_unique('LINKFLAGS', v)
				break

	self.env['libtoolvars']=libtool_vars

	while libtool_files:
		file = libtool_files.pop()
		dict = read_la_file(file)
		for v in dict['dependency_libs'].split():
			if v[-3:] == '.la':
				libtool_files.append(v)
				continue
			self.env.append_unique('LINKFLAGS', v)

Task.task_type_from_func('fakelibtool', vars=fakelibtool_vardeps, func=fakelibtool_build, color='BLUE', after="cc_link cxx_link static_link")

class libtool_la_file:
	def __init__ (self, la_filename):
		self.__la_filename = la_filename
		#remove path and .la suffix
		self.linkname = str(os.path.split(la_filename)[-1])[:-3]
		if self.linkname.startswith("lib"):
			self.linkname = self.linkname[3:]
		# The name that we can dlopen(3).
		self.dlname = None
		# Names of this library
		self.library_names = None
		# The name of the static archive.
		self.old_library = None
		# Libraries that this one depends upon.
		self.dependency_libs = None
		# Version information for libIlmImf.
		self.current = None
		self.age = None
		self.revision = None
		# Is this an already installed library?
		self.installed = None
		# Should we warn about portability when linking against -modules?
		self.shouldnotlink = None
		# Files to dlopen/dlpreopen
		self.dlopen = None
		self.dlpreopen = None
		# Directory that this library needs to be installed in:
		self.libdir = '/usr/lib'
		if not self.__parse():
			raise ValueError("file %s not found!!" %(la_filename))

	def __parse(self):
		"Retrieve the variables from a file"
		if not os.path.isfile(self.__la_filename): return 0
		la_file=open(self.__la_filename, 'r')
		for line in la_file:
			ln = line.strip()
			if not ln: continue
			if ln[0]=='#': continue
			(key, value) = str(ln).split('=', 1)
			key = key.strip()
			value = value.strip()
			if value == "no": value = False
			elif value == "yes": value = True
			else:
				try: value = int(value)
				except ValueError: value = value.strip("'")
			setattr(self, key, value)
		la_file.close()
		return 1

	def get_libs(self):
		"""return linkflags for this lib"""
		libs = []
		if self.dependency_libs:
			libs = str(self.dependency_libs).strip().split()
		if libs == None:
			libs = []
		# add la lib and libdir
		libs.insert(0, "-l%s" % self.linkname.strip())
		libs.insert(0, "-L%s" % self.libdir.strip())
		return libs

	def __str__(self):
		return '''\
dlname = "%(dlname)s"
library_names = "%(library_names)s"
old_library = "%(old_library)s"
dependency_libs = "%(dependency_libs)s"
version = %(current)s.%(age)s.%(revision)s
installed = "%(installed)s"
shouldnotlink = "%(shouldnotlink)s"
dlopen = "%(dlopen)s"
dlpreopen = "%(dlpreopen)s"
libdir = "%(libdir)s"''' % self.__dict__

class libtool_config:
	def __init__ (self, la_filename):
		self.__libtool_la_file = libtool_la_file(la_filename)
		tmp = self.__libtool_la_file
		self.__version = [int(tmp.current), int(tmp.age), int(tmp.revision)]
		self.__sub_la_files = []
		self.__sub_la_files.append(la_filename)
		self.__libs = None

	def __cmp__(self, other):
		"""make it compareable with X.Y.Z versions (Y and Z are optional)"""
		if not other:
			return 1
		othervers = [int(s) for s in str(other).split(".")]
		selfvers = self.__version
		return cmp(selfvers, othervers)

	def __str__(self):
		return "\n".join([
			str(self.__libtool_la_file),
			' '.join(self.__libtool_la_file.get_libs()),
			'* New getlibs:',
			' '.join(self.get_libs())
		])

	def __get_la_libs(self, la_filename):
		return libtool_la_file(la_filename).get_libs()

	def get_libs(self):
		"""return the complete uniqe linkflags that do not
		contain .la files anymore"""
		libs_list = list(self.__libtool_la_file.get_libs())
		libs_map = {}
		while len(libs_list) > 0:
			entry = libs_list.pop(0)
			if entry:
				if str(entry).endswith(".la"):
					## prevents duplicate .la checks
					if entry not in self.__sub_la_files:
						self.__sub_la_files.append(entry)
						libs_list.extend(self.__get_la_libs(entry))
				else:
					libs_map[entry]=1
		self.__libs = libs_map.keys()
		return self.__libs

	def get_libs_only_L(self):
		if not self.__libs: self.get_libs()
		libs = self.__libs
		libs = [s for s in libs if str(s).startswith('-L')]
		return libs

	def get_libs_only_l(self):
		if not self.__libs: self.get_libs()
		libs = self.__libs
		libs = [s for s in libs if str(s).startswith('-l')]
		return libs

	def get_libs_only_other(self):
		if not self.__libs: self.get_libs()
		libs = self.__libs
		libs = [s for s in libs if not(str(s).startswith('-L')or str(s).startswith('-l'))]
		return libs

def useCmdLine():
	"""parse cmdline args and control build"""
	usage = '''Usage: %prog [options] PathToFile.la
example: %prog --atleast-version=2.0.0 /usr/lib/libIlmImf.la
nor: %prog --libs /usr/lib/libamarok.la'''
	parser = optparse.OptionParser(usage)
	a = parser.add_option
	a("--version", dest = "versionNumber",
		action = "store_true", default = False,
		help = "output version of libtool-config"
		)
	a("--debug", dest = "debug",
		action = "store_true", default = False,
		help = "enable debug"
		)
	a("--libs", dest = "libs",
		action = "store_true", default = False,
		help = "output all linker flags"
		)
	a("--libs-only-l", dest = "libs_only_l",
		action = "store_true", default = False,
		help = "output -l flags"
		)
	a("--libs-only-L", dest = "libs_only_L",
		action = "store_true", default = False,
		help = "output -L flags"
		)
	a("--libs-only-other", dest = "libs_only_other",
		action = "store_true", default = False,
		help = "output other libs (e.g. -pthread)"
		)
	a("--atleast-version", dest = "atleast_version",
		default=None,
		help = "return 0 if the module is at least version ATLEAST_VERSION"
		)
	a("--exact-version", dest = "exact_version",
		default=None,
		help = "return 0 if the module is exactly version EXACT_VERSION"
		)
	a("--max-version", dest = "max_version",
		default=None,
		help = "return 0 if the module is at no newer than version MAX_VERSION"
		)

	(options, args) = parser.parse_args()
	if len(args) != 1 and not options.versionNumber:
		parser.error("incorrect number of arguments")
	if options.versionNumber:
		print("libtool-config version %s" % REVISION)
		return 0
	ltf = libtool_config(args[0])
	if options.debug:
		print(ltf)
	if options.atleast_version:
		if ltf >= options.atleast_version: return 0
		sys.exit(1)
	if options.exact_version:
		if ltf == options.exact_version: return 0
		sys.exit(1)
	if options.max_version:
		if ltf <= options.max_version: return 0
		sys.exit(1)

	def p(x):
		print(" ".join(x))
	if options.libs: p(ltf.get_libs())
	elif options.libs_only_l: p(ltf.get_libs_only_l())
	elif options.libs_only_L: p(ltf.get_libs_only_L())
	elif options.libs_only_other: p(ltf.get_libs_only_other())
	return 0

if __name__ == '__main__':
	useCmdLine()
