import string
from utilities import subst_vars
import utilities, handlerbase

PTYPE_PROGRAM   = 1
PTYPE_LIBRARY   = 2
PTYPE_LTLIBRARY = 3

def program_type(suffix):
	type = 0
	if suffix == 'PROGRAMS':
		type = PTYPE_PROGRAM
	elif suffix == 'LIBRARIES':
		type = PTYPE_LIBRARY
	else:
		type = PTYPE_LTLIBRARY
	return type
				
class Program:
	def __init__(self, amfile, name, prefix, type):
		self.name = name
		self.prefix = prefix
		self.canon_name = utilities.canon_name(name)
		self.amfile = amfile
		self.ui_files = []
		self.objs = []
		self.cleanfiles = []
		self.final_sources = {}
                self.set_type(type)
		
	def __repr__(self):
		return self.name

        def set_type(self, type):
                self.type = type
                if self.type == PTYPE_LTLIBRARY:
                        self.objext = '.lo'
                else:
                        self.objext = '.o'
	
	def is_cpp(self, ext):
		return ext in utilities.cppext

	def check_target(self, targ):
		if not targ:
			return 0
		if targ.has_rules() or not targ.user_specified:
			return 1
		else:
			print '%s: there are dependencies for the source file %s without' % (self.amfile.filename,
											     targ.target)
			print '\trules to generate it. This should most probably read as'
			print '\tdependencies for the object file.'

	def compile_lines(self, dir, base, ext, extraflags = "", forcecxx = 0, forcelibtoolOff = 0, depfilesuffix = '', deptargetoverride = ''):
				
		libtool=0
		compile = ""
						
		if self.type == PTYPE_LTLIBRARY and forcelibtoolOff == 0:
			libtool=1
			#compile = '$(LIBTOOL) --mode=compile '
			#if self.is_cpp(ext):
			#	compile = compile + '--tag=CXX '

		if self.is_cpp(ext) or forcecxx != 0:
			compile += '$(CXX) '
		else:
			compile += '$(CC) '
			
		compile += self.amfile.default_includes + ' '
		compile += self.add_prefixed_variable("INCLUDES", 1)
#		compile += self.handle_variable("CPPFLAGS") # Handle the Includepaths in CPPFLAGS
		compile += self.handle_variable("CPPFLAGS", 1)

                if len(extraflags):
                        compile += extraflags + ' '

		if self.is_cpp(ext) or forcecxx:
			compile += self.handle_variable("CXXFLAGS")
		else:
			compile += self.handle_variable("CFLAGS")

		if libtool:
			compile = compile + "-fPIC -DPIC "
			
		rulef = self.amfile.rulef
		file = dir + base + ext
		
		lines = ['@$(V_ECHO) "compiling %s"' % file]
			
		output='$@'
		if libtool:
			output=rulef.build + '.libs/' + base + '.o'
			lines.extend(['@if test ! -d "' + rulef.build + '.libs"; then mkdir "' + rulef.build + '.libs"; \\',
				      'status=$$?; if test "$$status" -ne 0 && test ! -d "' + rulef.build + '.libs"; then \\',
				      'exit $$status; fi; fi'])

		target = rulef.build + base + self.objext
		if deptargetoverride:
			target = deptargetoverride

		lines.extend(["@depfile='%s$(DEPDIR)/%s.U%s' tmpdepfile='%s$(DEPDIR)/%s.TU%s' targetfile='$%s';\\"
					  % (rulef.build, base + depfilesuffix, self.objext[1:],
						 rulef.build, base + depfilesuffix, self.objext[1:],
						 target),
					  "set %s-c %s -o %s -Wp,-MD,$$tmpdepfile; \\" % (compile, file, output),
					  "$(V_COMPILE)"])

		if libtool:
			outname = rulef.build + base + '.lo'
			lines.extend(["@echo '# Generated by libtool replacement' > " + outname,
				      "@echo \"pic_object='.libs/" + base + ".o'\" >> " + outname,
				      "@echo \"non_pic_object=none\" >> " + outname])
		return lines
					  
	def handle_source(self, base, ext, forcegenerated=0, final=0):

		rulef = self.amfile.rulef
		
		dir = rulef.source
		insource = 1
		if self.check_target(rulef.target(rulef.build + base + ext)) \
		   or self.check_target(self.amfile.target(base + ext)) \
		   or forcegenerated:
			dir = rulef.build
			insource = 0
			
		lines = self.compile_lines(dir, base, ext)

                dep = dir + base + ext
		pchdep = self.amfile.canon_subdir + "_" + self.canon_name + '_PCHDEP'
		if self.amfile.rulef.is_defined(pchdep):
			dep += ' $(' + pchdep + ')'
		
		rulef.insertTarget(rulef.build + base + self.objext,
						   dep,
						   lines)

		rulef.dep_files.append('$(DEPDIR)/%s.U%s' % (base, self.objext[1:]))
		self.amfile.translate_target(base + ext)
		self.amfile.translate_target(base + self.objext)

		if self.is_cpp(ext):
			if final:
				objpref = '_final'
			else:
				objpref = '_nofinal'
		else:
			objpref = ""
			
		rulef.add_define(self.amfile.canon_subdir + "_" +
						 self.canon_name + "%s_OBJECTS" % objpref,
						 rulef.build + base + self.objext)
		
		self.objs.append(base + self.objext)
		# no final for no cpp
		if not self.is_cpp(ext):
			return
		
		if not self.final_sources.has_key(ext):
			# first
			rulef.insertTarget(rulef.build + self.canon_name + '_all_' + ext[1:] + ext,
							   rulef.source + "Makefile.rules.in")
			if insource:
				self.final_sources[ext] = ([ base + ext ], [] )
			else:
				self.final_sources[ext] = ([], [ base + ext ] )
		else:
			if insource:
				self.final_sources[ext][0].append( base + ext )
			else:
				self.final_sources[ext][1].append( base + ext )

		if not final:
			final_target = rulef.build + self.canon_name + '_all_' + ext[1:]
			rulef.insertTarget(final_target + ext, dir + base + ext)
			for oext in ['.o', '.lo']:
				targ = self.amfile.target(base + oext)
				if targ:
					rulef.insertTarget(final_target + oext, self.amfile.replace_builddir(targ.deps, 0))
		
	def prefix_variable(self, var, replace_srcdir=0):
		subdir_var = "%s_%s" % (self.amfile.canon_subdir, var)
		if self.amfile.is_defined(var) and not self.amfile.rulef.is_defined(subdir_var):
			rec = self.amfile.definition_rec(var)
			if replace_srcdir:
				rec = self.amfile.replace_srcdir(rec)
				self.amfile.rulef.add_define(subdir_var, rec)
			else:
				self.amfile.rulef.add_define(subdir_var, rec)
		return subdir_var
	
	def add_prefixed_variable(self, var, replace_srcdir=0):
		adds = []
		subdir_var = self.prefix_variable(var, replace_srcdir)
		if self.amfile.rulef.is_defined(subdir_var):
			adds = self.amfile.rulef.definition_rec(subdir_var)

		if len(adds):
			for add in adds:
				self.amfile.translate_target(add)
			return "$(%s) " % subdir_var
		else:
			return ""
			
	def handle_variable(self, var, replace_srcdir=0): # Now handles the Paths
		added = self.add_prefixed_variable("AM_%s" % var, replace_srcdir)
		if self.amfile.is_defined(var):
			added += self.add_prefixed_variable(var, replace_srcdir)
		else:
			added += "$(%s) " % var
		added += self.add_prefixed_variable("KDE_%s" % var, replace_srcdir)

                targetvar = self.canon_name + "_" + var
		if self.amfile.is_defined(targetvar) or self.amfile.rulef.is_defined(self.prefix_variable(targetvar)):
			added += self.add_prefixed_variable(targetvar, replace_srcdir)

		return added
	
	def handle_add_variable(self, var, canon_name):
		adds = []
		if not self.amfile.is_defined(canon_name + "_" + var) and self.amfile.is_defined(var):
			adds = self.amfile.definition_rec(var)
		elif self.amfile.is_defined(canon_name + "_" + var):
			adds = self.amfile.definition_rec(canon_name + "_" + var)

		if len(adds):
			for add in adds:
				self.amfile.translate_target(add)
			cvar = self.amfile.canon_subdir + "_" + canon_name + "_" + var
			if not self.amfile.rulef.is_defined(cvar):
				self.amfile.rulef.add_define(cvar, self.amfile.replace_builddir(adds, 0))
			return "$(%s) " % cvar
		else:
			return ""
				
	def add_targets(self):

		prefix = self.amfile.canon_subdir + "_" + self.canon_name
		
		if self.type == PTYPE_LIBRARY:
			link = '@set $(AR) rcu $@ $(%s_OBJECTS) ' % prefix
			if self.amfile.is_defined("%s_LIBADD" % self.canon_name):
				link = link + "$(%s_LIBADD) " % self.canon_name
			link = [ link + ' ;\\', '$(V_EXEC)',
			         '@set $(RANLIB) $@ ;\\', '$(V_EXEC)' ]
		else:
			link = '@set $(LIBTOOL) --mode=link '
			if self.use_c_linker:
				link += '--tag=CC $(CLD) '
				link += self.handle_variable("CFLAGS")
			else:
				link += '--tag=CXX $(CXXLD) '
				link += self.handle_variable("CXXFLAGS")
			link += self.handle_variable("LDFLAGS")

			link += "-o $@ "

			is_installed = 1
			if self.prefix in ['noinst', 'check', 'EXTRA']:
				is_installed = 0
				
			if self.type == PTYPE_LTLIBRARY and is_installed:
				varname = '%sdir' % self.prefix
				dirprefix = '%s_%sdir' % (self.amfile.canon_subdir, self.prefix)
				if (not subst_vars.has_key(varname)) or (varname in self.amfile.handled_overwrites):
					if not self.amfile.rulef.is_defined(dirprefix):
						self.amfile.rulef.add_define(dirprefix, self.amfile.definition_rec(varname))
					link = link + "-rpath $(%s) " % dirprefix
				else:
					link = link + "-rpath $(%sdir) " % self.prefix

			if self.amfile.is_defined(self.canon_name + "_LDFLAGS"):
				ldflags = self.amfile.definition_rec(self.canon_name + "_LDFLAGS")
				if '-no-undefined' in ldflags and not '$(KDE_PLUGIN)' in ldflags:
					link += '$(KDE_NO_UNDEFINED) '

			if self.type == PTYPE_LTLIBRARY:
				var = self.handle_add_variable("LIBADD", self.canon_name)
			else:
				var = self.handle_add_variable("LDADD", self.canon_name)

			create_deps = []
			if len(var) and is_installed:
				match = utilities.variablere.match(string.strip(var))
				if match:
					list = self.amfile.rulef.definition(match.group(1))
					for l in list:
						if l.endswith('.la') and (l.startswith(self.amfile.rulef.build) or
												  l.startswith('$(top_builddir)/')):
							create_deps.append(l)
			deps_lines = []
			if len(create_deps):
				depfile= self.amfile.rulef.build + '$(DEPDIR)/%s.Ula' % self.canon_name
				deps_lines = [ "@echo \"# DESTDIR deps\" > %s.tmp" % depfile,
							   "@for file in " + string.join(create_deps) + " ; do \\",
							   "  ( . $$file ;\\",
							   "    if test -n \"$$libdir\"; then \\",
							   "      base=`basename $$file` ;\\",
							   "      echo \'$(DESTDIR)$(%sdir)/%s: " % (self.prefix, self.name)
							   + "$(DESTDIR)\'\"$$libdir/$$base\" >> %s.tmp; fi ) ;\\" % depfile,
							   "done; mv %s.tmp %s" % (depfile,depfile)]
				self.amfile.rulef.dep_files.append('$(DEPDIR)/%s.Ula' % self.canon_name)

			link = [ link + var + "$(%s_OBJECTS) $(LIBS) ;\\" % prefix, '$(V_EXEC)' ] + deps_lines

		if self.type == PTYPE_LTLIBRARY:
			add_prefix = "LIB"
		else:
			add_prefix = "LD"

		deps = []
		for var in ['%s_DEPENDENCIES' % self.canon_name,
					'DEPENDENCIES']:
			if self.amfile.is_defined(var):
				deps.extend(self.amfile.replace_builddir(self.amfile.definition_rec(var), 0))

		for var in [ '%s_%sADD' % (self.canon_name, add_prefix),
					 '%sADD' % add_prefix]:
			if self.amfile.is_defined(var):
				deps.extend(self.amfile.replace_builddir(self.amfile.definition_rec(var), 1))

		rulef = self.amfile.rulef
		rulef.insertTarget(rulef.build + self.name,
						   ['$(%s_OBJECTS)' % prefix] + deps,
						   ["@rm -f " + rulef.build + self.name,
						    '@$(V_ECHO) "linking %s"' % (rulef.build + self.name)] + link )
		if not self.prefix in ['check', 'EXTRA']:
			rulef.insertTarget('compile', '$(%s_OBJECTS)' % prefix, phony=1)
		lines = ["rm -f $(%s_OBJECTS)" % prefix,
			 "rm -f " + rulef.build + self.name]
		if len(self.cleanfiles):
			line = "rm -f"
			for file in self.cleanfiles:
				line = line + " " + rulef.build + file
			lines.append(line)
		if self.type == PTYPE_LTLIBRARY:
			lines.append("rm -rf " + rulef.build + ".libs")
		rulef.insertTarget("clean-%s" % prefix, "", lines, phony=1)
		rulef.insertTarget("clean", "clean-%s" % prefix, phony=1)
		self.add_closure_target(deps)
		
	def add_closure_target(self, deps):
		# adding closure
		rulef = self.amfile.rulef
		prefix = self.amfile.canon_subdir + "_" + self.canon_name
		
		if self.type == PTYPE_LTLIBRARY and self.amfile.is_defined(self.canon_name + "_LDFLAGS"):
			flags = self.amfile.definition(self.canon_name + "_LDFLAGS")
			if not '-no-undefined' in flags and not '$(KDE_PLUGIN)' in flags:
				if self.name.startswith('lib'):
					pass
					# print self.amfile.filename, 'lib', self.name, 'with undefined'
				return

			closure = rulef.build + self.name + ".closure"
			linkline = '@$(LIBTOOL) --mode=link '
			if self.use_c_linker:
				linkline += '--tag=CC $(CLD) '
			else:
				linkline += '--tag=CXX $(CXXLD) '
			linkline += self.handle_variable("LDFLAGS")
			if self.amfile.is_defined(self.canon_name + "_LDFLAGS"):
				linkline = linkline + "$(%s_LDFLAGS) " % prefix
				
			linkline = linkline + '-o %s ' % closure
			linkline = linkline + '%s_closure.lo ' % (rulef.build + self.canon_name)
			linkline = linkline + '$(%s_OBJECTS) ' % prefix
			linkline = linkline + self.handle_add_variable("LIBADD", self.canon_name)
			linkline = linkline + '$(LIBS) ;\\'

			lines = ['@echo "int main() {return 0;}" > ' + rulef.build + self.canon_name + '_closure.cpp']
			lines.extend(self.compile_lines(rulef.build, self.canon_name + '_closure', '.cpp'))
			lines.extend([
				'@echo "creating %s"' % closure,
				linkline,
				'stat=$$? ;\\',
				'rm -f %s_closure.* %s ;\\' % (rulef.build + self.canon_name, closure),
				'if test "$$stat" = 0; then echo "timestamp" > %s; fi' % closure])
			
			rulef.insertTarget(closure,
							   ['$(%s_OBJECTS)' % prefix] + deps,
							   lines)

			rulef.conds['%s_CLOSURE' % prefix] = ('KDE_USE_CLOSURE', [closure], [])
			rulef.insertTarget(rulef.build + self.name, '$(%s_CLOSURE)' % prefix)

			rulef.insertTarget('clean-closure-' + prefix,
							   [],
							   'rm -f ' + closure, phony=1)
			rulef.insertTarget('clean', 'clean-closure-' + prefix, phony=1)
			

	def add_final_target(self, compile_first):
		count = 0
		for ext in self.final_sources.keys():
			count = count + len(self.final_sources[ext][0]) + len(self.final_sources[ext][1])

		rulef = self.amfile.rulef
		prefix = self.amfile.canon_subdir + "_" + self.canon_name
		
		if count < 2 or self.amfile.get_opt("nofinal"):
			defs = rulef.value_list(prefix + '_OBJECTS')
			defs.extend( rulef.value_list(prefix + '_nofinal_OBJECTS') )
			
			if rulef.defs.has_key(prefix + '_OBJECTS'):
				del rulef.defs[prefix + '_OBJECTS']
			if rulef.defs.has_key(prefix + '_nofinal_OBJECTS'):
				del rulef.defs[prefix + '_nofinal_OBJECTS']
			rulef.add_define(prefix + '_OBJECTS', defs)
			return
		
		# adding final target
		for ext in self.final_sources.keys():
			finaltarget = rulef.build + self.canon_name + '_all_' + ext[1:] + ext
			lines = ["@echo 'creating %s'; \\" % finaltarget,
					 "rm -f %s.final; \\" % finaltarget,
					 "echo \"#define KDE_USE_FINAL 1\" >> %s.final ;\\" % finaltarget,
					 "echo \"#include <config.h>\" >> %s.final;\\" % finaltarget]
			if len(self.final_sources[ext][0]):
				lines.extend(["for file in " + string.join(self.final_sources[ext][0]) + "; do \\",
							  "  echo \"#include \\\"$$file\\\"\" >> %s.files; \\" % finaltarget,
							  "  grep '^#pragma +implementation' %s$$file >> %s.final;\\" % (rulef.source, finaltarget),
							  "done; \\"])
			if len(self.final_sources[ext][1]):
				lines.extend(["for file in " + string.join(self.final_sources[ext][1]) + "; do \\",
							  "  echo \"#include \\\"$$file\\\"\" >> %s.files; \\" % finaltarget,
							  "done; \\"])
			lines.extend(["cat %s.final %s.files > %s; \\" % (finaltarget, finaltarget, finaltarget),
						  "rm -f %s.final %s.files" % (finaltarget, finaltarget)])
			if len(self.amfile.moc_files):
				rulef.insertTarget(finaltarget, '$(%s_MOCS)' % self.amfile.canon_subdir)
			for dep in compile_first:
				rulef.insertTarget(finaltarget, self.amfile.replace_builddir([dep]))
			for file in self.final_sources[ext][0] + self.final_sources[ext][1]:
				match = utilities.extre.match(file)
				assert(match)
				targ = rulef.target(rulef.build + match.group(1) + self.objext)
				if targ:
					rulef.insertTarget(finaltarget, targ.deps)
				else:
					print 'no target for', rulef.source + match.group(1) + self.objext
			rulef.insertTarget(finaltarget,
							   [],
							   lines)
			self.handle_source( self.canon_name + '_all_' + ext[1:], ext, 1, 1)
			added_finals = 1

		if added_finals:
			extern_objs = rulef.value_list(prefix + '_OBJECTS')

			if rulef.defs.has_key(prefix + '_OBJECTS'):
				del rulef.defs[prefix + '_OBJECTS']
			
			rulef.conds[prefix + "_OBJECTS"] = ('KDE_USE_FINAL', ['$(%s_final_OBJECTS)' % prefix] + extern_objs,
												['$(%s_nofinal_OBJECTS)' % prefix] + extern_objs)
			
	def add_random(self):
		print 'random:', self.amfile.subdir + "/" + self.name
		self.amfile.rulef.insertTarget('random',
									   self.amfile.rulef.build + self.name, phony=1)
		
	# For a binary B (an executable or lib) this takes B->sources
	# and produces B->objects, B->deps and B->fin_sources.
	# An example should illustrate the behaviour best: Say, we have
	# bla_SOURCES = a.c b.cpp c.skel d.ui
	# then this produces
	# bla->objects = a.o b.o c.o d.o
	# bla->deps = c.cpp d.cpp
	# and bla->fin_sources = a.c b.cpp c.cpp d.cpp
	# i.e. The ->deps only contain the files, which are not already in ->objects
	def handle_sources(self, sources):
		self.sources = sources
		self.objects = []
		self.use_c_linker = 1
		for source in self.sources:
			match = utilities.extre.match(source)
			if not match:
				utilities.print_error('%s: "%s" doesnt match extre\n' % (self.amfile.filename, source))
				continue
			base = match.group(1)
			ext = match.group(2)

			if handlerbase.ext_dict.has_key(ext):
				handlerbase.ext_dict[ext].source(self, base, ext)
			elif self.is_cpp(ext) or ext == '.c':
				self.handle_source(base, ext)
			else:
				utilities.print_error('%s: unknown source extension %s for %s\n' % (self.amfile.filename, ext, self.name))
				continue

			if ext != '.c':
				self.use_c_linker = 0
