#! /usr/bin/env python
# encoding: utf-8
# Thomas Nagy, 2006 (ita)

import os, sys
import ccroot, cpp
import Action, Common, Object, Task, Params, Runner, Utils, Scan
from Params import debug, error, trace, fatal

# kde moc file processing
moc_vardeps = ['MOC', 'MOC_FLAGS', 'MOC_ST']
Action.GenAction('moc', moc_vardeps)

# kde documentation
meinproc_vardeps = ['MEINPROC', 'MEINPROCFLAGS']
def meinproc_build(task):
	reldir = task.m_inputs[0].cd_to()
	com   = task.m_env['MEINPROC']
	flags = task.m_env['MEINPROCFLAGS']
	#srcname = task.m_inputs[0].m_name
	#bldname = task.m_outputs[0].m_name
	cmd = '%s %s --cache %s %s' % (com, flags, task.m_outputs[0].bldpath(), task.m_inputs[0].bldpath())
	return Runner.exec_command(cmd)
meinprocact = Action.GenAction('meinproc', meinproc_vardeps)
meinprocact.m_function_to_run = meinproc_build

# kde .ui file processing
#uic_vardeps = ['UIC', 'UIC_FLAGS', 'UIC_ST']
uic_vardeps = ['UIC', 'QTPLUGINS']
def uic_build(task):
	# outputs : 1. hfile 2. cppfile

	base = task.m_outputs[1].m_name
	base = base[:len(base)-4]

	inc_kde  ='#include <klocale.h>\n#include <kdialog.h>\n'
	inc_moc  ='#include "%s.moc"\n' % base

	ui_path   = task.m_inputs[0].bldpath()
	h_path    = task.m_outputs[0].bldpath()
	cpp_path  = task.m_outputs[1].bldpath()

	qtplugins   = task.m_env['QTPLUGINS']
	uic_command = task.m_env['UIC']

	comp_h   = '%s -L %s -nounload -o %s %s' % (uic_command, qtplugins, h_path, ui_path)
	comp_c   = '%s -L %s -nounload -tr tr2i18n -impl %s %s >> %s' % (uic_command, qtplugins, h_path, ui_path, cpp_path)

	ret = Runner.exec_command( comp_h )
	if ret: return ret

	dest = open( cpp_path, 'w' )
	dest.write(inc_kde)
	dest.close()

	ret = Runner.exec_command( comp_c )
	if ret: return ret

	dest = open( cpp_path, 'a' )
	dest.write(inc_moc)
	dest.close()

	return ret
uicact = Action.GenAction('uic', uic_vardeps)
uicact.m_function_to_run = uic_build

kidl_vardeps = ['DCOPIDL']
skelstub_vardeps = ['DCOPIDL2CPP']

def kidl_build(task):
	reldir = task.m_inputs[0].cd_to()
	#src = task.m_inputs[0].m_name
	src = task.m_inputs[0].bldpath()
	#tgt = src[:len(src)-2]+'.kidl'
	tgt = task.m_outputs[0].bldpath()
	cmd = '%s %s > %s || (rm -f %s ; false)' % (task.m_env['DCOPIDL'], src, tgt, tgt)
	return Runner.exec_command(cmd)
kidlact = Action.GenAction('kidl', kidl_vardeps)
kidlact.m_function_to_run = kidl_build

def skel_build(task):
	reldir = task.m_inputs[0].cd_to()
	src = task.m_inputs[0].m_name
	cmd = 'cd %s && %s --c++-suffix cpp --no-signals --no-stub %s' % (reldir, task.m_env['DCOPIDL2CPP'], src)
	return Runner.exec_command(cmd)
skelact = Action.GenAction('skel', skelstub_vardeps)
skelact.m_function_to_run = skel_build

def stub_build(task):
	reldir = task.m_inputs[0].cd_to()
	src = task.m_inputs[0].m_name
	cmd = 'cd %s && %s --c++-suffix cpp --no-signals --no-skel %s' % (reldir, task.m_env['DCOPIDL2CPP'], src)
	return Runner.exec_command(cmd)
stubact = Action.GenAction('stub', skelstub_vardeps)
stubact.m_function_to_run = stub_build

# kconfig_compiler
kcfg_vardeps = ['KCONFIG_COMPILER']
def kcfg_build(task):
	com = task.m_env['KCONFIG_COMPILER']
	reldir = task.m_inputs[0].cd_to()
	kcfg1 = task.m_inputs[0].bldpath()
	kcfg2 = task.m_inputs[1].bldpath()
	cmd = '%s -d%s %s %s' % (com, reldir, kcfg1, kcfg2)
	return Runner.exec_command(cmd)
kcfgact = Action.GenAction('kcfg', kcfg_vardeps)
kcfgact.m_function_to_run = kcfg_build

# translations
po_vardeps = ['POCOM', 'PO_ST']
Action.GenAction('po', po_vardeps)
class kde_translations(Object.genobj):
	def __init__(self, appname):
		Object.genobj.__init__(self, 'other')
		self.m_tasks=[]
		self.m_appname = appname
	def apply(self):
		for file in self.m_current_path.m_files:
			try:
				base, ext = os.path.splitext(file.m_name)
				if ext != '.po': continue
				task = self.create_task('po', self.env, 2)
				task.m_inputs  = self.file_in(base+'.po')
				task.m_outputs = self.file_in(base+'.gmo')
				self.m_tasks.append(task)
			except: pass
	def install(self):
		destfilename = self.m_appname+'.mo'

		current = Params.g_build.m_curdirnode
		for file in self.m_current_path.m_files:
			lang, ext = os.path.splitext(file.m_name)
			if ext != '.po': continue

			node = self.get_mirror_node(self.m_current_path, lang+'.gmo')
			orig = node.relpath_gen(current)

			destfile = os.sep.join([lang, 'LC_MESSAGES', destfilename])
			Common.install_as('KDE_LOCALE', destfile, orig, self.env)

# documentation
class kde_documentation(Object.genobj):
	def __init__(self, appname, lang):
		Object.genobj.__init__(self, 'other')
		self.m_docs     = ''
		self.m_appname  = appname
		self.m_docbooks = []
		self.m_files    = []
		self.m_lang     = lang
	def add_docs(self, s):
		self.m_docs = s+" "+self.m_docs
	def apply(self):
		for filename in self.m_docs.split():
			if not filename: continue
			node = self.m_current_path.find_node( filename.split(os.sep) )
			self.m_files.append(node)
			(base, ext) = os.path.splitext(filename)
			if ext == '.docbook':
				task = self.create_task('meinproc', self.env, 2)
				task.m_inputs  = self.file_in(base+'.docbook')
				task.m_outputs = self.file_in(base+'.cache.bz2')
				self.m_docbooks.append(task)
	def install(self):
		destpath = os.sep.join([self.m_appname, self.m_lang])

		current = Params.g_build.m_curdirnode
		lst = []
		for task in self.m_docbooks:
			lst.append(task.m_outputs[0].relpath_gen(current))
		for doc in self.m_files:
			lst.append(doc.srcpath())

		Common.install_files('KDE_DOC', destpath, lst, self.env)

def handler_ui(obj, node, base=''):
	cppnode = obj.get_node( base+'.cpp' )
	hnode   = obj.get_node( base+'.h' )

	uictask = obj.create_task('uic', obj.env, 2)
	uictask.m_inputs    = obj.file_in(base+'.ui')
	uictask.m_outputs   = [ hnode, cppnode ]

	moctask = obj.create_task('moc', obj.env)
	moctask.m_inputs    = [ hnode ]
	moctask.m_outputs   = obj.file_in(base+'.moc')

	cpptask = obj.create_task('cpp', obj.env)
	cpptask.m_inputs    = [ cppnode ]
	cpptask.m_outputs   = obj.file_in(base+'.o')
	cpptask.m_run_after = [moctask]

	obj.p_compiletasks.append( cpptask )

def handler_kcfgc(obj, node, base=''):
	tree = Params.g_build.m_tree
	if tree.needs_rescan(node):
		tree.rescan(node, Scan.kcfg_scanner, obj.dir_lst)
	kcfg_node = tree.m_depends_on[node][0]
	cppnode = obj.get_node(base+'.cpp')

	# run with priority 2
	task = obj.create_task('kcfg', obj.env, 2)

	task.m_inputs = [ tree.get_mirror_node(kcfg_node), tree.get_mirror_node(node) ]
	task.m_outputs = [ cppnode, obj.get_node(base+'.h') ]

	cpptask = obj.create_task('cpp', obj.env)
	cpptask.m_inputs  = [ cppnode ]
	cpptask.m_outputs = [ obj.get_node(base+'.o') ]

	obj.p_compiletasks.append( cpptask )

def handler_skel_or_stub(obj, base, type):
	if not base in obj.skel_or_stub:
		kidltask = obj.create_task('kidl', obj.env, 2)
		kidltask.m_inputs  = obj.file_in(base+'.h')
		kidltask.m_outputs = obj.file_in(base+'.kidl')
		obj.skel_or_stub[base] = kidltask

	# this is a cascading builder .h->.kidl->_stub.cpp->_stub.o->link
	# instead of saying "task.run_after(othertask)" we only give priority numbers on tasks

	# the skel or stub (dcopidl2cpp)
	task = obj.create_task(type, obj.env, 4)
	task.m_inputs  = obj.skel_or_stub[base].m_outputs
	task.m_outputs = obj.file_in(''.join([base,'_',type,'.cpp']))

	# compile the resulting file (g++)
	cpptask = obj.create_task('cpp', obj.env)
	cpptask.m_inputs  = task.m_outputs
	cpptask.m_outputs = obj.file_in(''.join([base,'_',type,'.o']))

	obj.p_compiletasks.append( cpptask )

def handler_stub(obj, node, base=''):
	handler_skel_or_stub(obj, base, 'stub')

def handler_skel(obj, node, base=''):
	handler_skel_or_stub(obj, base, 'skel')

# kde3 objects
kdefiles = ['.cpp', '.ui', '.kcfgc', '.skel', '.stub']
class kdeobj(cpp.cppobj):
	def __init__(self, type='program'):
		cpp.cppobj.__init__(self, type)
		self.m_linktask = None
		self.m_latask   = None
		self.skel_or_stub = {}
		self.want_libtool = 1
		global kdefiles
		self.m_src_file_ext = kdefiles

	def get_valid_types(self):
		return ['program', 'shlib', 'staticlib', 'module', 'convenience', 'other']

	def get_node(self, a):
		return self.get_mirror_node(self.m_current_path, a)

	def apply(self):
		trace("apply called for kdeobj")
		if not self.m_type in self.get_valid_types(): fatal('Trying to build a kde file of unknown type')

		self.apply_type_vars()
		self.apply_lib_vars()
		self.apply_obj_vars()
		self.apply_incpaths()

		if self.want_libtool: self.apply_libtool()

		obj_ext = self.env[self.m_type+'_obj_ext'][0]

		# get the list of folders to use by the scanners
		# all our objects share the same include paths anyway
		tree = Params.g_build.m_tree
		self.dir_lst = { 'path_lst' : self._incpaths_lst }

		lst = self.source.split()
		for filename in lst:

			node = self.m_current_path.find_node( filename.split(os.sep) )
			if not node: error("source not found "+filename)
			base, ext = os.path.splitext(filename)

			fun = None
			try:
				fun = self.env['handlers_kdeobj_'+ext]
				#print "fun is", 'handlers_cppobj_'+ext, fun
			except:
				pass

			if fun:
				fun(self, node, base=base)
				continue


			# scan for moc files to produce, create cpp tasks at the same time
			if tree.needs_rescan(node):
				tree.rescan(node, Scan.c_scanner, self.dir_lst)

			moctasks=[]
			mocfiles=[]
			for d in tree.get_raw_deps(node):
				base2, ext2 = os.path.splitext(d)
				if not ext2 == '.moc': continue
				# paranoid check
				if d in mocfiles:
					error("paranoia owns")
					continue
				# process that base.moc only once
				mocfiles.append(d)

				task = self.create_task('moc', self.env)
				task.m_inputs  = self.file_in(base+'.h')
				task.m_outputs = self.file_in(base+'.moc')
				moctasks.append( task )

			# use a cache ?
			for d in tree.m_depends_on[node]:
				name = d.m_name
				if name[len(name)-4:]=='.moc':
					task = self.create_task('moc', self.env)
					task.m_inputs  = self.file_in(base+'.h')
					task.m_outputs = [d]
					moctasks.append( task )
					break

			# create the task for the cpp file
			cpptask = self.create_task('cpp', self.env)

			cpptask.m_scanner = Scan.c_scanner
			cpptask.m_scanner_params = self.dir_lst

			cpptask.m_inputs    = self.file_in(filename)
			cpptask.m_outputs   = self.file_in(base+obj_ext)
			cpptask.m_run_after = moctasks
			self.p_compiletasks.append(cpptask)

		# and after the cpp objects, the remaining is the link step - in a lower priority so it runs alone
		if self.m_type=='staticlib': linktask = self.create_task('cpp_link_static', self.env, ccroot.g_prio_link)
		else:                        linktask = self.create_task('cpp_link', self.env, ccroot.g_prio_link)
		cppoutputs = []
		for t in self.p_compiletasks: cppoutputs.append(t.m_outputs[0])
		linktask.m_inputs  = cppoutputs 
		linktask.m_outputs = self.file_in(self.get_target_name())

		self.m_linktask = linktask

		if self.m_type != 'program' and self.want_libtool:
			latask           = self.create_task('fakelibtool', self.env, 200)
			latask.m_inputs  = linktask.m_outputs
			latask.m_outputs = self.file_in(self.get_target_name('.la'))
			self.m_latask    = latask

		self.apply_libdeps()
		# end posting constraints (apply)

	def install(self):
		if self.m_type == 'module':
			self.install_results('KDE_MODULE', '', self.m_linktask)
			if self.want_libtool: self.install_results('KDE_MODULE', '', self.m_latask)
		else:
			ccroot.ccroot.install(self)

def detect_kde(conf):
	env = conf.env
	# Detect the qt and kde environment using kde-config mostly
	def getstr(varname):
		#if env.has_key('ARGS'): return env['ARGS'].get(varname, '')
		v=''
		try: v = getattr(Params.g_options, varname)
		except: return ''
		return v

	def getpath(varname):
		v = getstr(varname)
		#if not env.has_key('ARGS'): return None
		#v=env['ARGS'].get(varname, None)
		if v: v=os.path.abspath(v)
		return v

	prefix      = getpath('prefix')
	execprefix  = getpath('execprefix')
	datadir     = getpath('datadir')
	libdir      = getpath('libdir')

	kdedir      = getstr('kdedir')
	kdeincludes = getpath('kdeincludes')
	kdelibs     = getpath('kdelibs')

	qtdir       = getstr('qtdir')
	qtincludes  = getpath('qtincludes')
	qtlibs      = getpath('qtlibs')
	libsuffix   = getstr('libsuffix')

	p=Params.pprint

	if libdir: libdir = libdir+libsuffix

	## Detect the kde libraries
	print "Checking for kde-config            :",
	str="which kde-config 2>/dev/null"
	if kdedir: str="which %s 2>/dev/null" % (kdedir+'/bin/kde-config')
	kde_config = os.popen(str).read().strip()
	if len(kde_config):
		p('GREEN', 'kde-config was found as '+kde_config)
	else:
		if kdedir: p('RED','kde-config was NOT found in the folder given '+kdedir)
		else: p('RED','kde-config was NOT found in your PATH')
		print "Make sure kde is installed properly"
		print "(missing package kdebase-devel?)"
		sys.exit(1)
	if kdedir: env['KDEDIR']=kdedir
	else: env['KDEDIR'] = os.popen(kde_config+' -prefix').read().strip()

	print "Checking for kde version           :",
	kde_version = os.popen(kde_config+" --version|grep KDE").read().strip().split()[1]
	if int(kde_version[0]) != 3 or int(kde_version[2]) < 2:
		p('RED', kde_version)
		p('RED',"Your kde version can be too old")
		p('RED',"Please make sure kde is at least 3.2")
	else:
		p('GREEN',kde_version)

	## Detect the Qt library
	print "Checking for the Qt library        :",
	if not qtdir: qtdir = os.getenv("QTDIR")
	if qtdir:
		p('GREEN',"Qt is in "+qtdir)
	else:
		try:
			tmplibdir = os.popen(kde_config+' --expandvars --install lib').read().strip()
			libkdeuiSO = os.path.join(tmplibdir, getSOfromLA(os.path.join(tmplibdir,'/libkdeui.la')) )
			m = re.search('(.*)/lib/libqt.*', os.popen('ldd ' + libkdeuiSO + ' | grep libqt').read().strip().split()[2])
		except: m=None
		if m:
			qtdir = m.group(1)
			p('YELLOW',"Qt was found as "+m.group(1))
		else:
			p('RED','Qt was not found')
			p('RED','Please set QTDIR first (/usr/lib/qt3?) or try waf -h for more options')
			sys.exit(1)
	env['QTDIR'] = qtdir.strip()
	env['LIB_QT'] = 'qt-mt'

	## Find the necessary programs uic and moc
	print "Checking for uic                   :",
	uic = qtdir + "/bin/uic"
	if os.path.isfile(uic):
		p('GREEN',"uic was found as "+uic)
	else:
		uic = os.popen("which uic 2>/dev/null").read().strip()
		if len(uic):
			p('YELLOW',"uic was found as "+uic)
		else:
			uic = os.popen("which uic 2>/dev/null").read().strip()
			if len(uic):
				p('YELLOW',"uic was found as "+uic)
			else:
				p('RED',"uic was not found - set QTDIR put it in your PATH ?")
				sys.exit(1)
	env['UIC'] = uic

	print "Checking for moc                   :",
	moc = qtdir + "/bin/moc"
	if os.path.isfile(moc):
		p('GREEN',"moc was found as "+moc)
	else:
		moc = os.popen("which moc 2>/dev/null").read().strip()
		if len(moc):
			p('YELLOW',"moc was found as "+moc)
		elif os.path.isfile("/usr/share/qt3/bin/moc"):
			moc = "/usr/share/qt3/bin/moc"
			p('YELLOW',"moc was found as "+moc)
		else:
			p('RED',"moc was not found - set QTDIR or put it in your PATH ?")
			sys.exit(1)
	env['MOC'] = moc

	## check for the qt and kde includes
	print "Checking for the Qt includes       :",
	if qtincludes and os.path.isfile(qtincludes + "/qlayout.h"):
		# The user told where to look for and it looks valid
		p('GREEN',"ok "+qtincludes)
	else:
		if os.path.isfile(qtdir + "/include/qlayout.h"):
			# Automatic detection
			p('GREEN',"ok "+qtdir+"/include/")
			qtincludes = qtdir + "/include/"
		elif os.path.isfile("/usr/include/qt3/qlayout.h"):
			# Debian probably
			p('YELLOW','the Qt headers were found in /usr/include/qt3/')
			qtincludes = "/usr/include/qt3"
		else:
			p('RED',"the Qt headers were not found")
			sys.exit(1)

	print "Checking for the kde includes      :",
	kdeprefix = os.popen(kde_config+" --prefix").read().strip()
	if not kdeincludes:
		kdeincludes = kdeprefix+"/include/"
	if os.path.isfile(kdeincludes + "/klineedit.h"):
		p('GREEN',"ok "+kdeincludes)
	else:
		if os.path.isfile(kdeprefix+"/include/kde/klineedit.h"):
			# Debian, Fedora probably
			p('YELLOW',"the kde headers were found in %s/include/kde/"%kdeprefix)
			kdeincludes = kdeprefix + "/include/kde/"
		else:
			p('RED',"The kde includes were NOT found")
			sys.exit(1)

	# kde-config options
	kdec_opts = {'KDE_BIN'    : 'exe',     'KDE_APPS'      : 'apps',
		     'KDE_DATA'   : 'data',    'KDE_ICONS'     : 'icon',
		     'KDE_MODULE' : 'module',  'KDE_LOCALE'    : 'locale',
		     'KDE_KCFG'   : 'kcfg',    'KDE_DOC'       : 'html',
		     'KDE_MENU'   : 'apps',    'KDE_XDG'       : 'xdgdata-apps',
		     'KDE_MIME'   : 'mime',    'KDE_XDGDIR'    : 'xdgdata-dirs',
		     'KDE_SERV'   : 'services','KDE_SERVTYPES' : 'servicetypes',
		     'CPPPATH_KDECORE': 'include' }

	if prefix:
		## use the user-specified prefix
		if not execprefix: execprefix=prefix
		if not datadir: datadir=os.path.join(prefix,'share')
		if not libdir: libdir=os.path.join(execprefix, "lib"+libsuffix)

		subst_vars = lambda x: x.replace('${exec_prefix}', execprefix)\
				.replace('${datadir}', datadir)\
				.replace('${libdir}', libdir)\
				.replace('${prefix}', prefix)
		debian_fix = lambda x: x.replace('/usr/share', '${datadir}')
		env['PREFIX'] = prefix
		env['KDE_LIB'] = libdir
		for (var, option) in kdec_opts.items():
			dir = os.popen(kde_config+' --install ' + option).read().strip()
			if var == 'KDE_DOC': dir = debian_fix(dir)
			env[var] = subst_vars(dir)

	else:
		env['PREFIX'] = os.popen(kde_config+' --expandvars --prefix').read().strip()
		env['KDE_LIB'] = os.popen(kde_config+' --expandvars --install lib').read().strip()
		for (var, option) in kdec_opts.items():
			dir = os.popen(kde_config+' --expandvars --install ' + option).read().strip()
			env[var] = dir

	env['QTPLUGINS']=os.popen(kde_config+' --expandvars --install qtplugins').read().strip()

	## kde libs and includes
	env['CPPPATH_KDECORE']=kdeincludes
	if not kdelibs:
		kdelibs=os.popen(kde_config+' --expandvars --install lib').read().strip()
	env['LIBPATH_KDECORE']=kdelibs

	## qt libs and includes
	env['CPPPATH_QT']=qtincludes
	if not qtlibs:
		qtlibs=qtdir+"/lib"+libsuffix
	env['LIBPATH_QT']=qtlibs

	# rpath settings
	try:
		if Params.g_options.want_rpath:
			env['RPATH_QT']=['-Wl,--rpath='+qtlibs]
			env['RPATH_KDECORE']=['-Wl,--rpath='+kdelibs]
	except:
		pass

	env['LIB_KDECORE']  = 'kdecore'
        env['LIB_KIO']      = 'kio'
        env['LIB_KMDI']     = 'kmdi'
        env['LIB_KPARTS']   = 'kparts'
        env['LIB_KDEPRINT'] = 'kdeprint'

	env['KCONFIG_COMPILER'] = 'kconfig_compiler'

	env['MEINPROC']         = 'meinproc'
	env['MEINPROCFLAGS']    = '--check'
	env['MEINPROC_ST']      = '--cache %s %s'
	
	env['POCOM']            = 'msgfmt'
	env['PO_ST']            = '%s -o %s'

	env['MOC_FLAGS']        = ''
	env['MOC_ST']           = '%s -o %s'

	env['DCOPIDL']          = 'dcopidl'
	env['DCOPIDL2CPP']      = 'dcopidl2cpp'

	env['module_CXXFLAGS']  = ['-fPIC', '-DPIC']
	env['module_LINKFLAGS'] = ['-shared']
	env['module_obj_ext']   = ['.os']
	env['module_PREFIX']    = 'lib'
	env['module_SUFFIX']    = '.so'

	try: env['module_CXXFLAGS']=env['shlib_CXXFLAGS']
	except: pass

	try: env['module_LINKFLAGS']=env['shlib_LINKFLAGS']
	except: pass

def setup(env):
	if not sys.platform == "win32":
		Params.g_colors['moc']='\033[94m'
		Params.g_colors['skel']='\033[94m'
		Params.g_colors['kidl']='\033[94m'
		Params.g_colors['meinproc']='\033[94m'
		Params.g_colors['uic']='\033[94m'
		Params.g_colors['kcfg']='\033[94m'
		Params.g_colors['po']='\033[94m'

	if not env['handlers_kdeobj_.ui']:    env['handlers_kdeobj_.ui']   = handler_ui
	if not env['handlers_kdeobj_.skel']:  env['handlers_kdeobj_.skel'] = handler_skel
	if not env['handlers_kdeobj_.stub']:  env['handlers_kdeobj_.stub'] = handler_stub
	if not env['handlers_kdeobj_.kcfgc']: env['handlers_kdeobj_.kcfgc'] = handler_kcfgc

        Object.register('kde_translations', kde_translations)
        Object.register('kde_documentation', kde_documentation)
        Object.register('kde', kdeobj)

def detect(conf):
	conf.env['KDE_IS_FOUND'] = 0

	detect_kde(conf)

	conf.env['KDE_IS_FOUND'] = 1
	return 0


