#!/usr/bin/python
# Copyright 2006 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2

import os, string, stat, sys, time
if getattr(__builtins__, "set", None) is None:
	from sets import Set as set

for x in ['CFLAGS','CXXFLAGS', 'LDFLAGS','USE']:
	os.environ[x]=''

import xpak, portage, portage_checksum, portage_dep, portage_util, portage_const

argc=len(sys.argv)


if argc >= 2:
	if (sys.argv[1][0] == "-"):
		print "Usage:\tgenpkgindex <pkgdir>"
		print "\t- default dir "+portage.settings["PKGDIR"]+"/All"
		sys.exit(1)

	All=sys.argv[1]
else:
	All=portage.settings["PKGDIR"]+"/All"

All=os.path.normpath(All)

if os.path.exists("/usr/bin/eclean") and "cleanpkgdir" in portage.settings["FEATURES"]:
	os.system("/usr/bin/eclean -d packages")

os.chdir(All)

portage.writemsg(portage.green(' * ')+'Update binary package index %s\n' % All);
start = time.time()

# generic name of our package index.
control_file = ".Packages"

try:
	import re
	profilever = os.path.normpath("///"+os.readlink("/etc/make.profile"))
	basepath = os.path.normpath("///"+portage.settings["PORTDIR"]+"/profiles")
	if re.match(basepath,profilever):
		profilever = profilever[len(basepath)+1:]
	else:
		profilever = "!"+profilever

	del basepath

except SystemExit, e:
	raise # Needed else can't exit
except:
	profilever="unavailable"

packages = []
for pkg in os.listdir('.'):
	if not os.path.basename(pkg).endswith("tbz2"):
		continue

	st = os.stat(pkg)

	if not os.path.exists(portage_const.CACHE_PATH+"/xpak/"):
		os.mkdir(portage_const.CACHE_PATH+"/xpak/")

	fname = portage_const.CACHE_PATH+"/xpak/"+os.path.basename(pkg)[:-5]+".xpak"

	if os.path.exists(fname):
		if st.st_mtime != os.stat(fname).st_mtime:
			#print "unlinking "+fname
			os.unlink(fname)

	if not os.path.exists(fname):
		tbz2 = xpak.tbz2(pkg)
		xpdata = xpak.xpak_mem(tbz2.get_data())
		fp = open(fname, "w")
		fp.write(xpdata+xpak.encodeint(len(xpdata))+"STOP")
		fp.close()

		chksum = portage_checksum.perform_md5(pkg)
		fp = open(fname[:-5]+".md5", "w")
		fp.write(chksum)
		fp.close()

		os.utime(fname, (st.st_mtime, st.st_mtime))

	else:
		if os.path.exists(fname[:-5]+".md5"):
			chksum = "".join(portage.grabfile(fname[:-5]+".md5"))
		else:
			chksum = portage_checksum.perform_md5(pkg)

		tbz2 = xpak.tbz2(fname)

	packages.append((pkg, tbz2, chksum, st))

packages.sort()

timestamp=str(start).split(".")[0]
# trunc the file and begin write operations.
fp = open(control_file, "w")

if argc <= 1:
	fp.write("PROFILE: "+profilever+"\n")
	fp.write("PACKAGES: "+str(len(packages)) +"\n")
	fp.write("TIMESTAMP: "+timestamp+"\n")

	vmask = [ "AUTOCLEAN", "DISTDIR", "PKGDIR", "PORTDIR" , "PORTAGE_TMPDIR" , "PORTAGE_RSYNC_OPTS" ]
	vars = portage_util.grabfile(portage.settings["PORTDIR"]+"/profiles/info_vars")
	for x in vmask:
		vars.remove(x)

	vars.sort()

	for x in vars:
		if portage.settings.has_key(x):
			if (len(portage.settings[x])):
				fp.write(x+": "+portage.settings[x]+"\n")
else:
	fp.write("PACKAGES: "+str(len(packages)) +"\n")
	fp.write("TIMESTAMP: "+timestamp+"\n")

fp.write("\n")

def serialize_depset(src, context='and'):
	l = []
	if not src:
		return ''
	if isinstance(src, basestring):
		return src
	i = iter(src)
	for x in i:
		if isinstance(x, basestring):
			if x != '||':
				l.append(x)
				continue
			x = i.next()
			if len(x) == 1:
				l.append(serialize_depset(x[0]))
			else:
				l.append("|| ( %s )" % serialize_depset(x))
		else:
			# and block.
			if context == 'and':
				v = serialize_depset(x, context=context)
				if v.strip():
					l.append(v)
			else:
				v = serialize_depset(x, context='or')
				if v.strip():
					l.append("( %s )" % v.strip())
	return ' '.join(l)

for pkg, tbz2, chksum, st in packages:
	stuff = tbz2.getboth()
	if not stuff:
		print "Not a tbz2: "+str(pkg)
		continue

	cat = xpak.getitem(stuff, "CATEGORY")

	use = xpak.getitem(stuff, "USE")
	if use is None:
		use = ''
	iuse = xpak.getitem(stuff, "IUSE")
	if iuse is None:
		iuse = ''

	s = xpak.getitem(stuff, "DESCRIPTION")
	if s is not None:
		s = ' '.join(s.split())
		if s:
			fp.write("DESC: %s\n" % s)
	# drop '.tbz2'
	fp.write("PF: %s\nCATEGORY: %s\n" % (pkg[:-5], cat.strip()))
	s = xpak.getitem(stuff, "SLOT")
	if s is not None:
		s = ' '.join(s.split())
		if s and s != "0":
			fp.write("SLOT: %s\n" % s)

	split_use = use.split()
	for name in ("LICENSE", "RDEPEND", "PDEPEND", "PROVIDE"):
		item = xpak.getitem(stuff, name)
		if item is None:
			continue
		val = portage_dep.use_reduce(portage_dep.paren_reduce(' '.join(item.split())), uselist=split_use)
		if val:
			fp.write("%s: %s\n" % (name, serialize_depset(val)))

	# map IUSE->USE and look for matching flags, filter dupes
	# if both flags match then this is what matters.
	s = set(split_use).intersection(iuse.split())
	if s:
		l = list(s)
		l.sort()
		fp.write("USE: %s\n" % ' '.join(l))

	fp.write("SIZE: "+ str(st[stat.ST_SIZE]) +"\n")
	fp.write("MD5: "+chksum+"\n")
	fp.write("\n")

fp.write("\n")
fp.flush()
fp.close()
os.rename(".Packages", "Packages")

# Clean up stale cache files
os.chdir(portage_const.CACHE_PATH+"/xpak")
for pkg in os.listdir('.'):
	p = os.path.basename(pkg)
	if not p.endswith(".xpak"):
		continue
	if not os.path.exists(All + "/" + p[:-5] + ".tbz2"):
		#print "Stale entry: " + All + "/" + p[:-5] + ".tbz2"
		os.unlink(p)
		os.unlink(p[:-5]+".md5")

finish = time.time()

portage.writemsg(portage.green(' * ')+"PKGDIR contains "+ str(len(packages)) + ' packages. (%.01fsec)\n' % (finish - start));
