import string, os
import utilities, handlerbase
from target import DefValue

PTYPE_PROGRAM   = 1
PTYPE_LIBRARY   = 2
PTYPE_LTLIBRARY = 3

__pychecker__ = 'no-shadowbuiltin'

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, mfile, name, prefix, type):
		self.name = name
		self.prefix = prefix
		self.canon_name = utilities.canon_name(name)
		self.mfile = mfile
		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.mfile.filename,
																				 targ.target)
			print '\trules to generate it. This should most probably read as'
			print '\tdependencies for the object file.'
			return 0

	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.mfile.default_includes + ' $(INCLUDES) '
		compile += self.handle_variable("CPPFLAGS")

		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.mfile
		file = dir + base + ext
		
		lines = ['@$(V_ECHO) "compiling %s"' % file]

		# Output in local .libs if a subdir source file is passed on SOURCES line
		base = os.path.basename(base)
		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),
#gcc3	   			  "set %s-c %s -o %s -MT %s -MD -MP -MF $$tmpdepfile; \\" % (compile, file, output, 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.mfile
		
		dir = rulef.source
		insource = 1
		if self.check_target(rulef.target(rulef.build + base + ext)) \
		   or self.check_target(self.mfile.target(base + ext)) \
		   or forcegenerated:
			dir = rulef.build
			insource = 0
			
		lines = self.compile_lines(dir, base, ext)

		dep = dir + base + ext
		pchdep = self.canon_name + '_PCHDEP'
		if self.mfile.is_defined(pchdep):
			dep += ' $(' + pchdep + ')'

		# Output in local .libs if a subdir source file is passed on SOURCES line
		base = os.path.basename(base)
		rulef.insertTarget(rulef.build + base + self.objext,
						   dep,
						   lines, compile_target=1)

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

		if self.is_cpp(ext):
			if final:
				objpref = '_final'
			else:
				objpref = '_nofinal'
		else:
			objpref = ""

		rulef.add_define(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.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.mfile.target(base + oext)
				if targ:
					ndeps = self.mfile.replace_builddir(targ.deps, 0)
					deps = []
					for dep in ndeps:
						deps.append(DefValue(dep))
					rulef.insertTarget(final_target + oext, deps)
		
	def handle_variable(self, var): # Now handles the Paths
		svar = "AM_%s" % var
		if self.mfile.is_defined(svar):
			added = string.join(self.mfile.definition(svar))
		else:
			added = ""
		if self.mfile.is_defined(var) or utilities.subst_vars.has_key(var):
			added += " $(%s) " % var
		else:
			added += " "
		svar = "KDE_%s" % var
		if self.mfile.is_defined(svar):
			added += string.join(self.mfile.definition(svar))
		svar = "%s_%s" % (self.canon_name, var)
		if self.mfile.is_defined(svar) or utilities.subst_vars.has_key(svar): 
			added += " $(%s) " % svar
		else:
			added += " "

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

		if len(adds):
			for add in adds:
				self.mfile.translate_target(add)
			
			self.mfile.del_define(cvar)
			self.mfile.add_define(cvar, self.mfile.replace_builddir(adds, 0))
			return "$(%s) " % cvar
		else:
			return ""

	def create_handle_variable(self, var, replace_srcdir=0):
		self.mfile.add_prefixed_variable("AM_%s" % var, replace_srcdir)
		self.mfile.add_prefixed_variable(var, replace_srcdir)
		self.mfile.add_prefixed_variable("KDE_%s" % var, replace_srcdir)

		targetvar = self.canon_name + "_" + var
		self.mfile.add_prefixed_variable(targetvar, replace_srcdir)

	def create_variables(self):
		self.create_handle_variable("LDFLAGS", 0)
		
		self.mfile.add_prefixed_variable("INCLUDES", replace_srcdir=1)
		self.create_handle_variable("CPPFLAGS", replace_srcdir=1)
		self.create_handle_variable("CXXFLAGS", replace_srcdir=1)
		
	def add_targets(self):
		prefix = self.canon_name
		
		if self.type == PTYPE_LIBRARY:
			link = '@set $(AR) rcu $@ $(%s_OBJECTS) ' % prefix
			link += self.handle_add_variable("LIBADD")
			link = [ link + ' ;\\', '$(V_EXEC)',
					 '@set $(RANLIB) $@ ;\\', '$(V_EXEC)' ]
		else:
			if utilities.subst_vars.has_key("LIBTOOL"):
				link = '@set $(LIBTOOL) --mode=link '
				if self.use_c_linker:
					link += '--tag=CC '
				else:
					link += '--tag=CXX '
			else:
				link = '@set '

			if self.use_c_linker:
				link += '$(CLD) '
				link += self.handle_variable("CFLAGS")
				link += self.handle_variable("CPPFLAGS")
			else:
				link += '$(CXXLD) '
				link += self.handle_variable("CXXFLAGS")
				link += self.handle_variable("CPPFLAGS")
				
			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:
				link = link + "-rpath $(%sdir) " % self.prefix

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

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

			create_deps = []
			if len(var) and is_installed:
				match = utilities.variablere.match(string.strip(var))
				if match:
					list = self.mfile.definition(match.group(1))
					for l in list:
						if l.endswith('.la') and (l.startswith(self.mfile.build) or
												  l.startswith('$(top_builddir)/')):
							create_deps.append(l)
			deps_lines = []
			if len(create_deps):
				depfile= self.mfile.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.mfile.dep_files.append('%s.Ula' % self.canon_name)

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

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

		deps = [DefValue('$(%s_OBJECTS)' % prefix)]
		for var in ['%s_DEPENDENCIES' % self.canon_name,
					'DEPENDENCIES']:
			if self.mfile.is_defined(var):
				ndeps = self.mfile.replace_builddir(self.mfile.definition_rec(var), 0)
				for dep in ndeps:
					deps.append(DefValue(dep))

		for var in [ '%s_%sADD' % (self.canon_name, add_prefix),
					 '%sADD' % add_prefix]:
			if self.mfile.is_defined(var):
				ndeps = self.mfile.definition_rec(var)
				index = 0
				while index < len(ndeps):
					dep = ndeps[index]
					if len(dep) > 1:
						if dep[0] == '-':
							if dep[1] == 'l':
								index += 1
								continue
							elif dep[1] == 'L' or dep[1] == 'R':
								if len(dep) == 2:
									index += 2
								else:
									index += 1
								continue
							elif dep[1:4] == 'Wl,':
								index += 2
								continue
							utilities.print_error("%s: variable '%sADD' contains unknown flag %s. These should be in LDFLAGS!\n" %
												  (self.mfile.filename, add_prefix, dep))
							break
					index += 1
					list = self.mfile.replace_builddir([dep], 1)
					if len(list) == 1:
						deps.append(DefValue(list[0]))
					elif len(list) != 0:
						print list
						assert(False)

		rulef = self.mfile
		rulef.insertTarget(rulef.build + self.name,
						   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):

		if 'KDE_USE_CLOSURE' in utilities.false_conds or not utilities.subst_vars.has_key("LIBTOOL"):
			return
		
		# adding closure
		rulef = self.mfile
		prefix = self.canon_name
		
		if self.type == PTYPE_LTLIBRARY and self.mfile.is_defined(self.canon_name + "_LDFLAGS"):
			flags = self.mfile.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.mfile.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.mfile.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")
			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, compile_target=1)
			
			rulef.add_define('%s_CLOSURE' % prefix, [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):
		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.mfile
		prefix = self.canon_name
		
		if count < 2 or self.mfile.get_opt("nofinal"):
			defs = rulef.definition(prefix + '_OBJECTS')
			defs.extend( rulef.definition(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)])
			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

		assert(added_finals) # I kind of doubt it can not be set (TODO)
		
		if added_finals:
			extern_objs = rulef.definition(prefix + '_OBJECTS')

			rulef.del_define(prefix + "_OBJECTS")
			if 'KDE_USE_FINAL' in utilities.true_conds or \
				   utilities.environment_vars.has_key("UNSERMAKE_FORCE_FINAL"):
				rulef.add_define(prefix + "_OBJECTS", ['$(%s_final_OBJECTS)' % prefix] + extern_objs)
			else:
				rulef.add_define(prefix + "_OBJECTS", ['$(%s_nofinal_OBJECTS)' % prefix] + extern_objs)

	def collect_final_dependencies(self):
		# who cares otherwise?
		if not 'KDE_USE_FINAL' in utilities.true_conds and \
			   not utilities.environment_vars.has_key("UNSERMAKE_FORCE_FINAL"):
			return
		
		objs = self.mfile.definition(self.canon_name + "_nofinal_OBJECTS")
		fobjs = self.mfile.definition(self.canon_name + "_final_OBJECTS")
		for obj in objs:
			objtarg = self.mfile.target(obj)
			assert(objtarg)
			for fobj in fobjs:
				self.mfile.insertTarget(fobj, objtarg.deps)
	
	def add_random(self):
		print 'random:', self.mfile.subdir + "/" + self.name
		self.mfile.insertTarget('random',
								 self.mfile.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.objects = []
		self.use_c_linker = 1
		for source in sources:
			match = utilities.extre.match(source)
			if not match:
				utilities.print_error('%s: "%s" doesnt match extre\n' % (self.mfile.filename, source))
				continue
			base = match.group(1)
			ext = match.group(2)

			if ext in utilities.hext: # ignore headers for _everything_
				continue
			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.mfile.filename, ext, self.name))
				continue

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