[Reproducible-builds] Bug#835371: dispcalgui: please make the build reproducible

Chris Lamb lamby at debian.org
Wed Aug 24 22:36:40 UTC 2016


Source: dispcalgui
Version: 3.1.5.0-1
Severity: wishlist
Tags: patch
User: reproducible-builds at lists.alioth.debian.org
Usertags: timestamps
X-Debbugs-Cc: reproducible-builds at lists.alioth.debian.org

Hi,

Whilst working on the Reproducible Builds effort [0], I noticed
that dispcalgui could not be built reproducibly.

Patch attached.

 [0] https://reproducible-builds.org/


Regards,

-- 
      ,''`.
     : :'  :     Chris Lamb
     `. `'`      lamby at debian.org / chris-lamb.co.uk
       `-
-------------- next part --------------
--- a/.pc/01_reproducible-build.diff/setup.py	2016-08-24 23:08:44.542458026 +0100
--- b/.pc/01_reproducible-build.diff/setup.py	1970-01-01 01:00:00.000000000 +0100
@@ -1,1162 +0,0 @@
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
-
-from __future__ import with_statement
-from ConfigParser import RawConfigParser
-from distutils.sysconfig import get_python_lib
-from distutils.util import change_root, get_platform
-from hashlib import md5, sha1
-from subprocess import call, Popen
-from time import gmtime, strftime, timezone
-import codecs
-import glob
-import math
-import os
-import re
-import shutil
-import subprocess as sp
-import sys
-import time
-if sys.platform == "win32":
-	import msilib
-
-sys.path.insert(0, "DisplayCAL")
-
-from util_os import fs_enc, which
-from util_str import strtr
-
-pypath = os.path.abspath(__file__)
-pydir = os.path.dirname(pypath)
-
-def create_appdmg(zeroinstall=False):
-	if zeroinstall:
-		dmgname = name + "-0install"
-		srcdir = "0install"
-	else:
-		dmgname = name + "-" + version
-		srcdir = "py2app.%s-py%s" % (get_platform(), sys.version[:3])
-	retcode = call(["hdiutil", "create", os.path.join(pydir, "dist", 
-													  dmgname + ".dmg"), 
-					"-volname", dmgname, "-srcfolder", 
-					os.path.join(pydir, "dist", srcdir, dmgname)])
-	if retcode != 0:
-		sys.exit(retcode)
-
-
-def replace_placeholders(tmpl_path, out_path, lastmod_time=0, iterable=None):
-	global longdesc
-	with codecs.open(tmpl_path, "r", "UTF-8") as tmpl:
-		tmpl_data = tmpl.read()
-	if os.path.basename(tmpl_path).startswith("debian"):
-		longdesc_backup = longdesc
-		longdesc = "\n".join([" " + (line if line.strip() else ".") 
-							  for line in longdesc.splitlines()])
-	mapping = {
-		"DATE":
-			strftime("%a %b %d %Y",  # e.g. Tue Jul 06 2010
-					 gmtime(lastmod_time or 
-							os.stat(tmpl_path).st_mtime)),
-		"DATETIME": strftime("%a %b %d %H:%M:%S UTC %Y",  # e.g. Wed Jul 07 15:25:00 UTC 2010
-							  gmtime(lastmod_time or 
-									 os.stat(tmpl_path).st_mtime)),
-		"DEBPACKAGE": name.lower(),
-		"DEBDATETIME": strftime("%a, %d %b %Y %H:%M:%S ",  # e.g. Wed, 07 Jul 2010 15:25:00 +0100
-								 gmtime(lastmod_time or 
-										os.stat(tmpl_path).st_mtime)) +
-								 ("+" if timezone < 0 else "-") +
-								 strftime("%H%M", gmtime(abs(timezone))),
-		"DOMAIN": domain.lower(),
-		"ISODATE": 
-			strftime("%Y-%m-%d", 
-					 gmtime(lastmod_time or 
-							os.stat(tmpl_path).st_mtime)),
-		"ISODATETIME": 
-			strftime("%Y-%m-%dT%H:%M:%S", 
-					 gmtime(lastmod_time or 
-							os.stat(tmpl_path).st_mtime)) +
-					 ("+" if timezone < 0 else "-") +
-					 strftime("%H:%M", gmtime(abs(timezone))),
-		"ISOTIME": 
-			strftime("%H:%M", 
-					 gmtime(lastmod_time or 
-							os.stat(tmpl_path).st_mtime)),
-		"TIMESTAMP": str(int(lastmod_time)),
-		"SUMMARY": description,
-		"DESC": longdesc,
-		"APPDATADESC": "<p>\n\t\t\t" + longdesc.replace("\n", "\n\t\t\t").replace(".\n", ".\n\t\t</p>\n\t\t<p>\n") + "\n\t\t</p>",
-		"APPNAME": name,
-		"APPNAME_HTML": name_html,
-		"APPNAME_LOWER": name.lower(),
-		"AUTHOR": author,
-		"AUTHOR_EMAIL": author_email,
-		"MAINTAINER": author,
-		"MAINTAINER_EMAIL": author_email,
-		"MAINTAINER_EMAIL_SHA1": sha1(author_email).hexdigest(),
-		"PACKAGE": name,
-		"PY_MAXVERSION": ".".join(str(n) for n in py_maxversion),
-		"PY_MINVERSION": ".".join(str(n) for n in py_minversion),
-		"VERSION": version,
-		"VERSION_SHORT": re.sub("(?:\.0){1,2}$", "", version),
-		"URL": "http://%s/" % domain.lower(),
-		"WX_MINVERSION": ".".join(str(n) for n in wx_minversion),
-		"YEAR": strftime("%Y", gmtime())}
-	mapping.update(iterable or {})
-	for key, val in mapping.iteritems():
-		tmpl_data = tmpl_data.replace("${%s}" % key, val)
-	if os.path.basename(tmpl_path).startswith("debian"):
-		longdesc = longdesc_backup
-	if os.path.isfile(out_path):
-		with codecs.open(out_path, "r", "UTF-8") as out:
-			data = out.read()
-		if data == tmpl_data:
-			return
-	elif not os.path.isdir(os.path.dirname(out_path)):
-		os.makedirs(os.path.dirname(out_path))
-	with codecs.open(out_path, "w", "UTF-8") as out:
-		out.write(tmpl_data)
-
-
-def svnversion_bump(svnversion):
-	print "Bumping version number %s ->" % \
-		  ".".join(svnversion),
-	svnversion = svnversion_parse(
-		str(int("".join(svnversion)) + 1))
-	print ".".join(svnversion)
-	return svnversion
-
-
-def svnversion_parse(svnversion):
-	svnversion = [n for n in svnversion]
-	if len(svnversion) > 4:
-		svnversion = ["".join(svnversion[:len(svnversion) - 3])] + \
-					 svnversion[len(svnversion) - 3:]
-		# e.g. ["1", "1", "2", "5", "0"] -> ["11", "2", "5", "0"]
-	elif len(svnversion) < 4:
-		svnversion.insert(0, "0")
-		# e.g. ["2", "8", "3"] -> ["0", "2", "8", "3"]
-	return svnversion
-
-
-def setup():
-
-	if sys.platform == "darwin":
-		bdist_cmd = "py2app"
-	elif sys.platform == "win32":
-		bdist_cmd = "py2exe"
-	else:
-		bdist_cmd = "bdist_bbfreeze"
-	if "bdist_standalone" in sys.argv[1:]:
-		i = sys.argv.index("bdist_standalone")
-		sys.argv = sys.argv[:i] + sys.argv[i + 1:]
-		if not bdist_cmd in sys.argv[1:i]:
-			sys.argv.insert(i, bdist_cmd)
-	elif "bdist_bbfreeze" in sys.argv[1:]:
-		bdist_cmd = "bdist_bbfreeze"
-	elif "bdist_pyi" in sys.argv[1:]:
-		bdist_cmd = "pyi"
-	elif "py2app" in sys.argv[1:]:
-		bdist_cmd = "py2app"
-	elif "py2exe" in sys.argv[1:]:
-		bdist_cmd = "py2exe"
-
-	appdata = "appdata" in sys.argv[1:]
-	arch = None
-	bdist_appdmg = "bdist_appdmg" in sys.argv[1:]
-	bdist_deb = "bdist_deb" in sys.argv[1:]
-	bdist_lipa = "bdist_lipa" in sys.argv[1:]
-	bdist_pyi = "bdist_pyi" in sys.argv[1:]
-	buildservice = "buildservice" in sys.argv[1:]
-	setup_cfg = None
-	dry_run = "-n" in sys.argv[1:] or "--dry-run" in sys.argv[1:]
-	help = False
-	inno = "inno" in sys.argv[1:]
-	onefile = "-F" in sys.argv[1:] or "--onefile" in sys.argv[1:]
-	purge = "purge" in sys.argv[1:]
-	purge_dist = "purge_dist" in sys.argv[1:]
-	use_setuptools = "--use-setuptools" in sys.argv[1:]
-	zeroinstall = "0install" in sys.argv[1:]
-	stability = "testing"
-	
-	argv = list(sys.argv[1:])
-	for i, arg in enumerate(reversed(argv)):
-		n = len(sys.argv) - i - 1
-		arg = arg.split("=")
-		if len(arg) == 2:
-			if arg[0] == "--force-arch":
-				arch = arg[1]
-			elif arg[0] in ("--cfg", "--stability"):
-				if arg[0] == "--cfg":
-					setup_cfg = arg[1]
-				else:
-					stability = arg[1]
-				sys.argv = sys.argv[:n] + sys.argv[n + 1:]
-		elif arg[0] == "-h" or arg[0].startswith("--help"):
-			help = True
-	
-	lastmod_time = 0
-	
-	non_build_args = filter(lambda arg: arg in sys.argv[1:], 
-							["bdist_appdmg", "clean", "purge", "purge_dist", 
-							 "uninstall", "-h", "--help", "--help-commands", 
-							 "--all", "--name", "--fullname", "--author", 
-							 "--author-email", "--maintainer", 
-							 "--maintainer-email", "--contact", 
-							 "--contact-email", "--url", "--license", 
-							 "--licence", "--description", 
-							 "--long-description", "--platforms", 
-							 "--classifiers", "--keywords", "--provides", 
-							 "--requires", "--obsoletes", "--quiet", "-q", 
-							 "register", "--list-classifiers", "upload",
-							 "--use-distutils", "--use-setuptools",
-							 "--verbose", "-v", "finalize_msi"])
-
-	if os.path.isdir(os.path.join(pydir, ".svn")) and (which("svn") or
-													   which("svn.exe")) and (
-	   not sys.argv[1:] or (len(non_build_args) < len(sys.argv[1:]) and 
-							not help)):
-		print "Trying to get SVN version information..."
-		svnversion = None
-		try:
-			p = Popen(["svnversion"], stdout=sp.PIPE, cwd=pydir)
-		except Exception, exception:
-			print "...failed:", exception
-		else:
-			svnversion = p.communicate()[0]
-			svnversion = strtr(svnversion.strip().split(":")[-1], "MPS")
-			svnbasefilename = os.path.join(pydir, "VERSION_BASE")
-			if os.path.isfile(svnbasefilename):
-				with open(svnbasefilename) as svnbasefile:
-					svnbase = int("".join(svnbasefile.read().strip().split(".")),
-								  10)
-				svnversion = int(svnversion)
-				svnversion += svnbase
-			svnversion = svnversion_parse(str(svnversion))
-			svnbase = svnversion
-		
-		print "Trying to get SVN information..."
-		mod = False
-		lastmod = ""
-		entries = []
-		args = ["svn", "status", "--xml"]
-		while not entries:
-			try:
-				p = Popen(args, stdout=sp.PIPE, cwd=pydir)
-			except Exception, exception:
-				print "...failed:", exception
-				break
-			else:
-				from xml.dom import minidom
-				xml = p.communicate()[0]
-				xml = minidom.parseString(xml)
-				entries = xml.getElementsByTagName("entry")
-				if not entries:
-					if "info" in args:
-						break
-					args = ["svn", "info", "-R", "--xml"]
-		timestamp = None
-		for entry in iter(entries):
-			pth = entry.getAttribute("path")
-			mtime = 0
-			if "status" in args:
-				status = entry.getElementsByTagName("wc-status")
-				item = status[0].getAttribute("item")
-				if item.lower() in ("none", "normal"):
-					item = " "
-				props = status[0].getAttribute("props")
-				if props.lower() in ("none", "normal"):
-					props = " "
-				print item.upper()[0] + props.upper()[0] + " " * 5, pth
-				mod = True
-				if item.upper()[0] != "D" and os.path.exists(pth):
-					mtime = os.stat(pth).st_mtime
-					if mtime > lastmod_time:
-						lastmod_time = mtime
-						timestamp = time.gmtime(mtime)
-			schedule = entry.getElementsByTagName("schedule")
-			if schedule:
-				schedule = schedule[0].firstChild.wholeText.strip()
-				if schedule != "normal":
-					print schedule.upper()[0] + " " * 6, pth
-					mod = True
-					mtime = os.stat(pth).st_mtime
-					if mtime > lastmod_time:
-						lastmod_time = mtime
-						timestamp = time.gmtime(mtime)
-			lmdate = entry.getElementsByTagName("date")
-			if lmdate:
-				lmdate = lmdate[0].firstChild.wholeText.strip()
-				dateparts = lmdate.split(".")  # split off milliseconds
-				mtime = time.mktime(time.strptime(dateparts[0], 
-													  "%Y-%m-%dT%H:%M:%S"))
-				mtime += float("." + strtr(dateparts[1], "Z"))
-				if mtime > lastmod_time:
-					lastmod_time = mtime
-					timestamp = time.localtime(mtime)
-		if timestamp:
-			lastmod = strftime("%Y-%m-%dT%H:%M:%S", timestamp) + \
-					  str(round(mtime - int(mtime), 6))[1:] + \
-					  "Z"
-			## print lmdate, lastmod, pth
-		
-		if not dry_run:
-			print "Generating __version__.py"
-			versionpy = open(os.path.join(pydir, "DisplayCAL", "__version__.py"), "w")
-			versionpy.write("# generated by setup.py\n\n")
-			buildtime = time.time()
-			versionpy.write("BUILD_DATE = %r\n" % 
-							(strftime("%Y-%m-%dT%H:%M:%S", 
-									 gmtime(buildtime)) + 
-							 str(round(buildtime - int(buildtime), 6))[1:] + 
-							 "Z"))
-			if lastmod:
-				versionpy.write("LASTMOD = %r\n" % lastmod)
-			if svnversion:
-				if mod:
-					svnversion = svnversion_bump(svnversion)
-				else:
-					print "Version", ".".join(svnversion)
-				versionpy.write("VERSION = (%s)\n" % ", ".join(svnversion))
-				versionpy.write("VERSION_BASE = (%s)\n" % ", ".join(svnbase))
-				versionpy.write("VERSION_STRING = %r\n" % ".".join(svnversion))
-				versiontxt = open(os.path.join(pydir, "VERSION"), "w")
-				versiontxt.write(".".join(svnversion))
-				versiontxt.close()
-			versionpy.close()
-
-	if not help and not dry_run:
-		# Restore setup.cfg.backup if it exists
-		if os.path.isfile(os.path.join(pydir, "setup.cfg.backup")) and \
-		   not os.path.isfile(os.path.join(pydir, "setup.cfg")):
-			shutil.copy2(os.path.join(pydir, "setup.cfg.backup"), 
-						 os.path.join(pydir, "setup.cfg"))
-	
-	if not sys.argv[1:]:
-		return
-	
-	global name, name_html, author, author_email, description, longdesc
-	global domain, py_maxversion, py_minversion
-	global version, version_lin, version_mac
-	global version_src, version_tuple, version_win
-	global wx_minversion
-	from meta import (name, name_html, author, author_email, description,
-					  lastmod, longdesc, domain, py_maxversion, py_minversion,
-					  version, version_lin, version_mac, 
-					  version_src, version_tuple, version_win,
-					  wx_minversion, script2pywname)
-
-	if not lastmod_time:
-		lastmod_time = time.mktime(time.strptime(lastmod,
-												 "%Y-%m-%dT%H:%M:%S.%fZ"))
-
-	msiversion = ".".join((str(version_tuple[0]), 
-						   str(version_tuple[1]), 
-						   str(version_tuple[2]) + 
-						   str(version_tuple[3])))
-
-	if not dry_run and not help:
-		if setup_cfg or ("bdist_msi" in sys.argv[1:] and use_setuptools):
-			if not os.path.exists(os.path.join(pydir, "setup.cfg.backup")):
-				shutil.copy2(os.path.join(pydir, "setup.cfg"), 
-							 os.path.join(pydir, "setup.cfg.backup"))
-		if "bdist_msi" in sys.argv[1:] and use_setuptools:
-			# setuptools parses options globally even if they're not under the
-			# section of the currently run command
-			os.remove(os.path.join(pydir, "setup.cfg"))
-		if setup_cfg:
-			shutil.copy2(os.path.join(pydir, "misc", "setup.%s.cfg" % setup_cfg), 
-						 os.path.join(pydir, "setup.cfg"))
-
-	if purge or purge_dist:
-
-		# remove the "build", "DisplayCAL.egg-info" and 
-		# "pyinstaller/bincache*" directories and their contents recursively
-
-		if dry_run:
-			print "dry run - nothing will be removed"
-
-		paths = []
-		if purge:
-			paths += glob.glob(os.path.join(pydir, "build")) + glob.glob(
-						os.path.join(pydir, name + ".egg-info")) + glob.glob(
-						os.path.join(pydir, "pyinstaller", "bincache*"))
-			sys.argv.remove("purge")
-		if purge_dist:
-			paths += glob.glob(os.path.join(pydir, "dist"))
-			sys.argv.remove("purge_dist")
-		for path in paths:
-			if os.path.exists(path):
-				if dry_run:
-					print path
-					continue
-				try:
-					shutil.rmtree(path)
-				except Exception, exception:
-					print exception
-				else:
-					print "removed", path
-		if len(sys.argv) == 1 or (len(sys.argv) == 2 and dry_run):
-			return
-
-	if "readme" in sys.argv[1:]:
-		if not dry_run:
-			for suffix in ("", "-fr"):
-				replace_placeholders(os.path.join(pydir, "misc", 
-												  "README%s.template.html" %
-												  suffix),
-									 os.path.join(pydir, "README%s.html" %
-												  suffix),
-									 lastmod_time,
-									 {"STABILITY": "Beta" if stability != "stable"
-												   else ""})
-			replace_placeholders(os.path.join(pydir, "misc", 
-											  "history.template.html"),
-								 os.path.join(pydir, "history.html"),
-								 lastmod_time)
-		sys.argv.remove("readme")
-		if len(sys.argv) == 1 or (len(sys.argv) == 2 and dry_run):
-			return
-
-	if ((appdata or 
-		 "install" in sys.argv[1:]) and 
-		not help and not dry_run):
-		from setup import get_scripts
-		import localization as lang
-		scripts = get_scripts()
-		provides = ["<python2>%s</python2>" % name]
-		for script, desc in scripts:
-			provides.append("<binary>%s</binary>" % script)
-		provides = "\n\t\t".join(provides)
-		lang.init()
-		languages = []
-		for code, tdict in sorted(lang.ldict.items()):
-			if code == "en":
-				continue
-			untranslated = 0
-			for key in tdict:
-				if key.startswith("*") and key != "*":
-					untranslated += 1
-			languages.append('<lang percentage="%i">%s</lang>' %
-							 (round((1 - untranslated / (len(tdict) - 1.0)) * 100),
-							 code))
-		languages = "\n\t\t".join(languages)
-		tmpl_name = name + ".appdata.xml"
-		replace_placeholders(os.path.join(pydir, "misc", tmpl_name),
-							 os.path.join(pydir, "dist", tmpl_name),
-							 lastmod_time, {"APPDATAPROVIDES": provides,
-											"LANGUAGES": languages})
-	if appdata:
-		sys.argv.remove("appdata")
-
-	if (("sdist" in sys.argv[1:] or 
-		 "install" in sys.argv[1:] or 
-		 "bdist_deb" in sys.argv[1:]) and 
-		not help):
-		buildservice = True
-	if buildservice and not dry_run:
-		replace_placeholders(os.path.join(pydir, "misc", "debian.copyright"),
-							 os.path.join(pydir, "dist", "copyright"),
-							 lastmod_time)
-	if "buildservice" in sys.argv[1:]:
-		sys.argv.remove("buildservice")
-
-	if bdist_deb:
-		bdist_args = ["bdist_rpm"]
-		if not arch:
-			arch = get_platform().split("-")[1]
-			bdist_args += ["--force-arch=" + arch]
-		i = sys.argv.index("bdist_deb")
-		sys.argv = sys.argv[:i] + bdist_args + sys.argv[i + 1:]
-	
-	if bdist_lipa:
-		i = sys.argv.index("bdist_lipa")
-		sys.argv = sys.argv[:i] + sys.argv[i + 1:]
-
-	if bdist_pyi:
-		i = sys.argv.index("bdist_pyi")
-		sys.argv = sys.argv[:i] + sys.argv[i + 1:]
-		if not "build_ext" in sys.argv[1:i]:
-			sys.argv.insert(i, "build_ext")
-		if "-F" in sys.argv[1:]:
-			sys.argv.remove("-F")
-		if "--onefile" in sys.argv[1:]:
-			sys.argv.remove("--onefile")
-
-	if inno and sys.platform == "win32":
-		for tmpl_type in ("pyi" if bdist_pyi else bdist_cmd, "0install",
-						  "0install-per-user"):
-			inno_template_path = os.path.join(pydir, "misc", "%s-Setup-%s.iss" % 
-											  (name, tmpl_type))
-			inno_template = open(inno_template_path, "r")
-			inno_script = inno_template.read().decode("UTF-8", "replace") % {
-				"AppCopyright": u"© %s %s" % (strftime("%Y"), author),
-				"AppName": name,
-				"AppVerName": version,
-				"AppPublisher": author,
-				"AppPublisherURL": "http://" + domain,
-				"AppSupportURL": "http://" + domain,
-				"AppUpdatesURL": "http://" + domain,
-				"VersionInfoVersion": ".".join(map(str, version_tuple)),
-				"VersionInfoTextVersion": version,
-				"AppVersion": version,
-				"Platform": get_platform(),
-				"PythonVersion": sys.version[:3],
-				"URL": "http://%s/" % domain.lower(),
-				}
-			inno_template.close()
-			inno_path = os.path.join("dist", 
-									 os.path.basename(inno_template_path).replace(
-										bdist_cmd, "%s.%s-py%s" % 
-										(bdist_cmd, get_platform(), 
-										 sys.version[:3])))
-			if not dry_run:
-				if not os.path.exists("dist"):
-					os.makedirs("dist")
-				inno_file = open(inno_path, "w")
-				inno_file.write(inno_script.encode("MBCS", "replace"))
-				inno_file.close()
-		sys.argv.remove("inno")
-		if len(sys.argv) == 1 or (len(sys.argv) == 2 and dry_run):
-			return
-	
-	if "finalize_msi" in sys.argv[1:]:
-		db = msilib.OpenDatabase(r"dist\%s-%s.win32-py%s.msi" %
-								 (name, msiversion, sys.version[:3]), 
-								 msilib.MSIDBOPEN_TRANSACT)
-		view = db.OpenView("SELECT Value FROM Property WHERE Property = 'ProductCode'")
-		view.Execute(None)
-		record = view.Fetch()
-		productcode = record.GetString(1)
-		view.Close()
-		msilib.add_data(db, "Directory", [("ProgramMenuFolder",  # Directory
-										   "TARGETDIR",  # Parent
-										   ".")])  # DefaultDir
-		msilib.add_data(db, "Directory", [("MenuDir",  # Directory
-										   "ProgramMenuFolder",  # Parent
-										   name.upper()[:6] + "~1|" + name)])  # DefaultDir
-		msilib.add_data(db, "Icon", [(name + ".ico",  # Name
-									  msilib.Binary(os.path.join(pydir, name, 
-														"theme", "icons", 
-														name + ".ico")))])  # Data
-		msilib.add_data(db, "Icon", [("uninstall.ico",  # Name
-									  msilib.Binary(os.path.join(pydir, name, 
-														"theme", "icons", 
-														name + "-uninstall.ico")))])  # Data
-		msilib.add_data(db, "RemoveFile", [("MenuDir",  # FileKey
-											name,  # Component
-											None,  # FileName
-											"MenuDir",  # DirProperty
-											2)])  # InstallMode
-		msilib.add_data(db, "Registry", [("DisplayIcon",  # Registry
-										  -1,  # Root
-										  r"Software\Microsoft\Windows\CurrentVersion\Uninstall\%s" % 
-										  productcode,  # Key
-										  "DisplayIcon",  # Name
-										  r"[icons]%s.ico" % name,  # Value
-										  name)])  # Component
-		msilib.add_data(db, "Shortcut", [(name,  # Shortcut
-										  "MenuDir",  # Directory
-										  name.upper()[:6] + "~1|" + name,  # Name
-										  name,  # Component
-										  r"[TARGETDIR]pythonw.exe",  # Target
-										  r'"[TARGETDIR]Scripts\%s"' % name,  # Arguments
-										  None,  # Description
-										  None,  # Hotkey
-										  name + ".ico",  # Icon
-										  None,  # IconIndex
-										  None,  # ShowCmd
-										  name)])  # WkDir
-		msilib.add_data(db, "Shortcut", [("LICENSE",  # Shortcut
-										  "MenuDir",  # Directory
-										  "LICENSE|LICENSE",  # Name
-										  name,  # Component
-										  r"[%s]LICENSE.txt" % name,  # Target
-										  None,  # Arguments
-										  None,  # Description
-										  None,  # Hotkey
-										  None,  # Icon
-										  None,  # IconIndex
-										  None,  # ShowCmd
-										  name)])  # WkDir
-		msilib.add_data(db, "Shortcut", [("README",  # Shortcut
-										  "MenuDir",  # Directory
-										  "README|README",  # Name
-										  name,  # Component
-										  r"[%s]README.html" % name,  # Target
-										  None,  # Arguments
-										  None,  # Description
-										  None,  # Hotkey
-										  None,  # Icon
-										  None,  # IconIndex
-										  None,  # ShowCmd
-										  name)])  # WkDir
-		msilib.add_data(db, "Shortcut", [("Uninstall",  # Shortcut
-										  "MenuDir",  # Directory
-										  "UNINST|Uninstall",  # Name
-										  name,  # Component
-										  r"[SystemFolder]msiexec",  # Target
-										  r"/x" + productcode,  # Arguments
-										  None,  # Description
-										  None,  # Hotkey
-										  "uninstall.ico",  # Icon
-										  None,  # IconIndex
-										  None,  # ShowCmd
-										  "SystemFolder")])  # WkDir
-		if not dry_run:
-			db.Commit()
-		sys.argv.remove("finalize_msi")
-		if len(sys.argv) == 1 or (len(sys.argv) == 2 and dry_run):
-			return
-
-	if zeroinstall:
-		sys.argv.remove("0install")
-
-	if bdist_appdmg:
-		sys.argv.remove("bdist_appdmg")
-
-	if (not bdist_lipa and not zeroinstall and not buildservice and
-		not appdata and not bdist_appdmg) or sys.argv[1:]:
-		print sys.argv[1:]
-		from setup import setup
-		setup()
-	
-	if dry_run or help:
-		return
-
-	if buildservice:
-		# Create control files
-		mapping = {"POST": open(os.path.join(pydir, "util",
-											 "rpm_postinstall.sh"),
-								"r").read().strip(),
-				   "POSTUN": open(os.path.join(pydir, "util",
-											   "rpm_postuninstall.sh"),
-								  "r").read().strip()}
-		tgz = os.path.join(pydir, "dist", "%s-%s.tar.gz" % (name, version))
-		if os.path.isfile(tgz):
-			with open(tgz, "rb") as tgzfile:
-				mapping["MD5"] = md5(tgzfile.read()).hexdigest()
-		for tmpl_name in ("PKGBUILD", "debian.changelog", "debian.control",
-						  "debian.copyright",
-						  "debian.rules", name + ".changes",
-						  name + ".dsc", name + ".spec", 
-						  os.path.join("0install", "PKGBUILD"),
-						  os.path.join("0install", "debian.changelog"),
-						  os.path.join("0install", "debian.control"),
-						  os.path.join("0install", "debian.rules"),
-						  os.path.join("0install", name + ".dsc"),
-						  os.path.join("0install", name + ".spec"),
-						  os.path.join("obs-autopackage-deploy",
-									   name + ".spec")):
-			tmpl_path = os.path.join(pydir, "misc", tmpl_name)
-			replace_placeholders(tmpl_path,
-								 os.path.join(pydir, "dist", tmpl_name),
-								 lastmod_time, mapping)
-
-	if bdist_deb:
-		# Read setup.cfg
-		cfg = RawConfigParser()
-		cfg.read(os.path.join(pydir, "setup.cfg"))
-		# Get dependencies
-		dependencies = [val.strip().split(None, 1) for val in 
-						cfg.get("bdist_rpm", "Requires").split(",")]
-		# Get group
-		if cfg.has_option("bdist_rpm", "group"):
-			group = cfg.get("bdist_rpm", "group")
-		else:
-			group = None
-		# Get maintainer
-		if cfg.has_option("bdist_rpm", "maintainer"):
-			maintainer = cfg.get("bdist_rpm", "maintainer")
-		else:
-			maintainer = None
-		# Get packager
-		if cfg.has_option("bdist_rpm", "packager"):
-			packager = cfg.get("bdist_rpm", "packager")
-		else:
-			packager = None
-		# Convert dependency format:
-		# 'package >= version' to 'package (>= version)'
-		for i in range(len(dependencies)):
-			if len(dependencies[i]) > 1:
-				dependencies[i][1] = "(%s)" % dependencies[i][1]
-			dependencies[i] = " ".join(dependencies[i])
-		release = 1 # TODO: parse setup.cfg
-		rpm_filename = os.path.join(pydir, "dist", "%s-%s-%s.%s.rpm" % 
-									(name, version, release, arch))
-		if not dry_run:
-			# remove target directory (and contents) if it already exists
-			target_dir = os.path.join(pydir, "dist", "%s-%s" % (name, version))
-			if os.path.exists(target_dir):
-				shutil.rmtree(target_dir)
-			if os.path.exists(target_dir + ".orig"):
-				shutil.rmtree(target_dir + ".orig")
-			# use alien to create deb dir from rpm package
-			retcode = call(["alien", "-c", "-g", "-k", 
-							os.path.basename(rpm_filename)], 
-							cwd=os.path.join(pydir, "dist"))
-			if retcode != 0:
-				sys.exit(retcode)
-			# update changelog
-			shutil.copy2(os.path.join(pydir, "dist", "debian.changelog"), 
-						 os.path.join(pydir, "dist", "%s-%s" % (name, version), 
-									  "debian", "changelog"))
-			# update rules
-			shutil.copy2(os.path.join(pydir, "misc", "alien.rules"), 
-						 os.path.join(pydir, "dist", "%s-%s" % (name, version), 
-									  "debian", "rules"))
-			# update control
-			control_filename = os.path.join(pydir, "dist", "%s-%s" % (name, 
-																	  version), 
-											"debian", "control")
-			shutil.copy2(os.path.join(pydir, "dist", "debian.control"), 
-						 control_filename)
-			### read control file from deb dir
-			##control = open(control_filename, "r")
-			##lines = [line.rstrip("\n") for line in control.readlines()]
-			##control.close()
-			### update control with info from setup.cfg
-			##for i in range(len(lines)):
-				##if lines[i].startswith("Depends:"):
-					### add dependencies
-					##lines[i] += ", python"
-					##lines[i] += ", python" + sys.version[:3]
-					##lines[i] += ", " + ", ".join(dependencies)
-				##elif lines[i].startswith("Maintainer:") and (maintainer or 
-															 ##packager):
-					### set maintainer
-					##lines[i] = "Maintainer: " + (maintainer or packager)
-				##elif lines[i].startswith("Section:") and group:
-					### set section
-					##lines[i] = "Section: " + group
-				##elif lines[i].startswith("Description:"):
-					##lines.pop()
-					##lines.pop()
-					##break
-			### write updated control file
-			##control = open(control_filename, "w")
-			##control.write("\n".join(lines))
-			##control.close()
-			### run strip on shared libraries
-			##sos = os.path.join(change_root(target_dir, get_python_lib(True)),
-							   ##name, "*.so")
-			##for so in glob.glob(sos):
-				##retcode = call(["strip", "--strip-unneeded", so])
-			# create deb package
-			retcode = call(["chmod", "+x", "./debian/rules"], cwd=target_dir)
-			retcode = call(["./debian/rules", "binary"], cwd=target_dir)
-			if retcode != 0:
-				sys.exit(retcode)
-
-	if setup_cfg or ("bdist_msi" in sys.argv[1:] and use_setuptools):
-		shutil.copy2(os.path.join(pydir, "setup.cfg.backup"), 
-					 os.path.join(pydir, "setup.cfg"))
-	
-	if bdist_lipa:
-		# Create a Listaller package using lipkgen
-		from setup import get_data, get_scripts
-		scripts = get_scripts()
-		ipkinstall = os.path.join(pydir, "ipkinstall")
-		# Generate doap file
-		replace_placeholders(os.path.join(pydir, "misc", name + ".doap"),
-							 os.path.join(ipkinstall, name + ".doap"),
-							 lastmod_time)
-		# Adjust .desktop files
-		for script, desc in scripts:
-			filename = os.path.join(pydir, "misc", script + ".desktop")
-			if os.path.exists(filename):
-				with open(filename) as file1:
-					filename = os.path.join(ipkinstall, script + ".desktop")
-					with open(filename, "w") as file2:
-						file2.write(file1.read().replace("Exec=" + script,
-														 "Exec=%s.pyw" %
-														 script2pywname(script)))
-		# Collect data
-		collect = ([("%APP%", ["ipkinstall/%s.desktop" % script
-							   for script, desc in
-							   filter(lambda (script, desc):
-									  not script.endswith("-apply-profiles"),
-									  scripts)])] +
-				   [("%%ICON-%s%%" % size,
-					 ["%s/theme/icons/%sx%s/%s.png" %
-					  (name, size, size, script)
-					  for script, desc in scripts])
-					for size in (16, 24, 32, 48, 64, 128, 256)] +
-				   [("%INST%", ["%s.pyw" % script2pywname(script)
-								for script, desc in scripts])] +
-				   get_data("%INST%", "data") +
-				   get_data("%INST%", "doc") +
-				   [("%INST%/" + name,
-					 glob.glob(os.path.join(name, "*.py")))] +
-				   [("%%INST%%/%s/lib" % name,
-					 glob.glob(os.path.join(name, "lib/*.py")))] +
-				   [("%%INST%%/%s/lib/agw" % name,
-					 glob.glob(os.path.join(name, "lib/agw/*.py")))] +
-				   get_data("%INST%/" + name, "package_data", name) +
-				   [("%INST%/scripts", ["scripts/%s" % script
-										for script, desc in scripts])])
-		for bits in (32, 64):
-			collect += [("%%INST%%/%s/lib%s" % (name, bits),
-						 glob.glob(os.path.join(name, "lib%s/*.py" % bits)))]
-			for pycompat in (26, 27):
-				collect += [("%%INST%%/%s/lib%s/python%s"
-							 % (name, bits, pycompat),
-							 glob.glob(os.path.join(name, "lib%s/python%s/*.py"
-														  % (bits, pycompat))) +
-							 glob.glob(os.path.join(name, "lib%s/python%s/*.so"
-														  % (bits, pycompat))))]
-		data = {}
-		for tgt_dir, files in collect:
-			tgt_dir = tgt_dir.replace(os.sep, "/")
-			if not tgt_dir in data:
-				data[tgt_dir] = []
-			data[tgt_dir] += files
-		tgt_dirs = sorted(data.keys())
-		# Generate files list
-		with open(os.path.join(ipkinstall, "files-all.list"), "w") as fileslist:
-			fileslist.write("# IPK file list for %s\n" % name)
-			fileslist.write("# Generated by setup.py, do not edit\n")
-			fileslist.write("\n")
-			cur_tgt_dir = None
-			for tgt_dir in tgt_dirs:
-				if tgt_dir != cur_tgt_dir:
-					cur_tgt_dir = tgt_dir
-					fileslist.write(":: %s\n" % tgt_dir)
-				for filename in sorted(data[tgt_dir]):
-					fileslist.write(os.path.relpath(filename,
-													pydir).replace(os.sep,
-																   "/") + "\n")
-		# Create actual Listaller package
-		if not which("lipkgen"):
-			raise SystemExit("Error: No lipkgen in %s" % os.getenv("PATH"))
-		out_dir = os.path.join(pydir, "dist")
-		if not os.path.isdir(out_dir):
-			os.makedirs(out_dir)
-		retcode = call(["lipkgen", "-b", "-o", out_dir, "--sign"])
-		if retcode != 0:
-			sys.exit(retcode)
-
-	if bdist_pyi:
-
-		# create an executable using pyinstaller
-
-		retcode = call([sys.executable, os.path.join(pydir, "pyinstaller", 
-													 "pyinstaller.py"), 
-						"--workpath", os.path.join(pydir, "build",
-												   "pyi.%s-%s" %
-												   (get_platform(),
-												    sys.version[:3])),
-						"--distpath", os.path.join(pydir, "dist",
-												   "pyi.%s-py%s" %
-													(get_platform(),
-													 sys.version[:3])),
-						os.path.join(pydir, "misc", "%s.pyi.spec" % name)])
-		if retcode != 0:
-			sys.exit(retcode)
-
-	if zeroinstall:
-		from xml.dom import minidom
-		# Create/update 0install feeds
-		from setup import get_data, get_scripts
-		scripts = sorted((script2pywname(script), desc)
-						 for script, desc in get_scripts())
-		cmds = []
-		for script, desc in scripts:
-			cmdname = "run"
-			if script != name:
-				cmdname += "-" + script.replace(name + "-", "")
-			cmds.append((cmdname, script, desc))
-			##if script.endswith("-apply-profiles"):
-				### Add forced calibration loading entry
-				##cmds.append((cmdname + "-force", script, desc))
-		# Get archive digest
-		extract = "%s-%s" % (name, version)
-		archive_name = extract + ".tar.gz"
-		archive_path = os.path.join(pydir, "dist", archive_name)
-		p = Popen(["0install", "digest", archive_path.encode(fs_enc), extract],
-				  stdout=sp.PIPE, cwd=pydir)
-		stdout, stderr = p.communicate()
-		print stdout
-		hash = re.search("(sha\d+\w+[=_][0-9a-f]+)", stdout.strip())
-		if not hash:
-			raise SystemExit(p.wait())
-		hash = hash.groups()[0]
-		for tmpl_name in ("7z.xml", "argyllcms.xml", name + ".xml",
-						  name + "-linux.xml", name + "-mac.xml",
-						  name + "-win32.xml", "numpy.xml", "pygame.xml",
-						  "pywin32.xml", "wmi.xml", "wxpython.xml"):
-			dist_path = os.path.join(pydir, "dist", "0install", tmpl_name)
-			create = not os.path.isfile(dist_path)
-			if create:
-				tmpl_path = os.path.join(pydir, "misc", "0install",
-										 tmpl_name)
-				replace_placeholders(tmpl_path, dist_path, lastmod_time)
-			if tmpl_name.startswith(name):
-				with open(dist_path) as dist_file:
-					xml = dist_file.read()
-					domtree = minidom.parseString(xml)
-				# Get interface
-				interface = domtree.getElementsByTagName("interface")[0]
-				# Get languages
-				langs = [os.path.splitext(os.path.basename(lang))[0] for lang in
-						 glob.glob(os.path.join(name, "lang", "*.json"))]
-				# Get architecture groups
-				groups = domtree.getElementsByTagName("group")
-				if groups:
-					# Get main group
-					group0 = groups[0]
-					# Add languages
-					group0.setAttribute("langs", " ".join(langs))
-				# Update groups
-				for i, group in enumerate(groups[-1:]):
-					if create:
-						# Remove dummy implementations
-						for implementation in group.getElementsByTagName("implementation"):
-							if implementation.getAttribute("released") == "0000-00-00":
-								implementation.parentNode.removeChild(implementation)
-						# Add commands
-						runner = domtree.createElement("runner")
-						if group.getAttribute("arch").startswith("Windows-"):
-							runner.setAttribute("command", "run-win")
-						if group.getAttribute("arch").startswith("Linux"):
-							python = "http://repo.roscidus.com/python/python"
-						else:
-							python = "http://%s/0install/python.xml" % domain.lower()
-						runner.setAttribute("interface", python)
-						runner.setAttribute("version",
-											"%i.%i..!3.0" % py_minversion)
-						for cmdname, script, desc in cmds:
-							# Add command to group
-							cmd = domtree.createElement("command")
-							cmd.setAttribute("name", cmdname)
-							cmd.setAttribute("path", script + ".pyw")
-							##if cmdname.endswith("-apply-profiles"):
-								### Autostart
-								##arg = domtree.createElement("suggest-auto-start")
-								##cmd.appendChild(arg)
-							if cmdname.endswith("-apply-profiles-force"):
-								# Forced calibration loading
-								arg = domtree.createElement("arg")
-								arg.appendChild(domtree.createTextNode("--force"))
-								cmd.appendChild(arg)
-							cmd.appendChild(runner.cloneNode(True))
-							group.appendChild(cmd)
-					# Add implementation if it does not exist yet, update otherwise
-					match = None
-					for implementation in group.getElementsByTagName("implementation"):
-						match = (implementation.getAttribute("version") == version and
-								 implementation.getAttribute("stability") == stability)
-						if match:
-							break
-					if not match:
-						implementation = domtree.createElement("implementation")
-						implementation.setAttribute("version", version)
-						implementation.setAttribute("released",
-													strftime("%Y-%m-%d", 
-															 gmtime(lastmod_time)))
-						implementation.setAttribute("stability", stability)
-						digest = domtree.createElement("manifest-digest")
-						implementation.appendChild(digest)
-						archive = domtree.createElement("archive")
-						implementation.appendChild(archive)
-					else:
-						digest = implementation.getElementsByTagName("manifest-digest")[0]
-						for attrname, value in digest.attributes.items():
-							# Remove existing hashes
-							digest.removeAttribute(attrname)
-						archive = implementation.getElementsByTagName("archive")[0]
-					implementation.setAttribute("id", hash)
-					digest.setAttribute(*hash.split("="))
-					# Update archive
-					if stability == "stable":
-						folder = ""
-					else:
-						folder = "&folder=snapshot"
-					archive.setAttribute("extract", extract)
-					archive.setAttribute("href",
-										 "http://%s/download.php?version=%s&suffix=.tar.gz%s" %
-										 (domain.lower(), version, folder))
-					archive.setAttribute("size", "%s" % os.stat(archive_path).st_size)
-					archive.setAttribute("type", "application/x-compressed-tar")
-					group.appendChild(implementation)
-				if create:
-					for cmdname, script, desc in cmds:
-						# Add entry-points to interface
-						entry_point = domtree.createElement("entry-point")
-						entry_point.setAttribute("command", cmdname)
-						binname = script
-						if cmdname.endswith("-force"):
-							binname += "-force"
-						entry_point.setAttribute("binary-name", binname)
-						cfg = RawConfigParser()
-						desktopbasename = "%s.desktop" % script
-						if cmdname.endswith("-apply-profiles"):
-							desktopbasename = "z-" + desktopbasename
-						cfg.read(os.path.join(pydir, "misc",
-											  desktopbasename))
-						for option, tagname in (("Name", "name"),
-												("GenericName", "summary"),
-												("Comment", "description")):
-							for lang in [None] + langs:
-								if lang:
-									suffix = "[%s]" % lang
-								else:
-									suffix = ""
-								option = "%s%s" % (option, suffix)
-								if cfg.has_option("Desktop Entry", option):
-									value = cfg.get("Desktop Entry",
-													option).decode("UTF-8")
-									if value:
-										tag = domtree.createElement(tagname)
-										if not lang:
-											lang = "en"
-										tag.setAttribute("xml:lang", lang)
-										tag.appendChild(domtree.createTextNode(value))
-										entry_point.appendChild(tag)
-						for ext, mime_type in (("ico", "image/vnd.microsoft.icon"),
-											   ("png", "image/png")):
-							icon = domtree.createElement("icon")
-							if ext == "ico":
-								subdir = ""
-								filename = script
-							else:
-								subdir = "256x256/"
-								filename = script.lower()
-							icon.setAttribute("href",
-											  "http://%s/theme/icons/%s%s.%s" %
-											  (domain.lower(), subdir,
-											   filename, ext))
-							icon.setAttribute("type", mime_type)
-							entry_point.appendChild(icon)
-						interface.appendChild(entry_point)
-				# Update feed
-				print "Updating 0install feed", dist_path
-				with open(dist_path, "wb") as dist_file:
-					xml = domtree.toprettyxml(encoding="utf-8")
-					xml = re.sub(r"\n\s+\n", "\n", xml)
-					xml = re.sub(r"\n\s*([^<]+)\n\s*", r"\1", xml)
-					dist_file.write(xml)
-				# Sign feed
-				zeropublish = which("0publish") or which("0publish.exe")
-				args = []
-				if not zeropublish:
-					zeropublish = which("0install") or which("0install.exe")
-					if zeropublish:
-						args = ["run", "--command", "0publish", "--",
-								"http://0install.de/feeds/ZeroInstall_Tools.xml"]
-				if zeropublish:
-					passphrase_path = os.path.join(pydir, "gpg", "passphrase.txt")
-					print "Signing", dist_path
-					if os.path.isfile(passphrase_path):
-						import wexpect
-						with open(passphrase_path) as passphrase_file:
-							passphrase = passphrase_file.read().strip()
-						p = wexpect.spawn(zeropublish.encode(fs_enc), args +
-										  ["-x", dist_path.encode(fs_enc)])
-						p.expect(":")
-						p.send(passphrase)
-						p.send("\n")
-						try:
-							p.expect(wexpect.EOF, timeout=3)
-						except:
-							p.terminate()
-					else:
-						call([zeropublish] + args + ["-x", dist_path.encode(fs_enc)])
-				else:
-					print "WARNING: 0publish not found, please sign the feed!"
-		# Create 0install app bundles
-		bundletemplate = os.path.join("0install", "template.app", "Contents")
-		bundletemplatepath = os.path.join(pydir, bundletemplate)
-		if os.path.isdir(bundletemplatepath):
-			p = Popen(["0install", "-V"], stdout=sp.PIPE)
-			stdout, stderr = p.communicate()
-			zeroinstall_version = re.search(r" (\d(?:\.\d+)+)", stdout)
-			if zeroinstall_version:
-				zeroinstall_version = zeroinstall_version.groups()[0]
-			if zeroinstall_version < "2.8":
-				zeroinstall_version = "2.8"
-			feeduri = "http://%s/0install/%s.xml" % (domain.lower(), name)
-			dist_dir = os.path.join(pydir, "dist", "0install",
-									name + "-0install")
-			for script, desc in scripts + [("0install-launcher",
-											"0install Launcher"),
-										   ("0install-cache-manager",
-											"0install Cache Manager")]:
-				if script.endswith("-apply-profiles"):
-					continue
-				desc = re.sub("^%s " % name, "", desc).strip()
-				if script == "0install-launcher":
-					bundlename = name
-				else:
-					bundlename = desc
-				bundledistpath = os.path.join(dist_dir, desc + ".app",
-											  "Contents")
-				replace_placeholders(os.path.join(bundletemplatepath,
-												  "Info.plist"),
-									 os.path.join(bundledistpath,
-												  "Info.plist"), lastmod_time,
-									 {"NAME": bundlename,
-									  "EXECUTABLE": script,
-									  "ID": ".".join(reversed(domain.split("."))).replace(name,
-																						  script)})
-				if script.startswith(name):
-					run = "0launch%s -- %s" % (re.sub("^%s" % name,
-													  " --command=run", script),
-											   feeduri)
-				else:
-					run = {"0install-launcher": "0launch --gui " + feeduri,
-						   "0install-cache-manager": "0store manage"}.get(script)
-				replace_placeholders(os.path.join(bundletemplatepath, "MacOS",
-												  "template"),
-									 os.path.join(bundledistpath,
-												  "MacOS", script),
-									 lastmod_time,
-									 {"EXEC": run,
-									  "ZEROINSTALL_VERSION": zeroinstall_version})
-				os.chmod(os.path.join(bundledistpath, "MacOS", script), 0755)
-				for binary in os.listdir(os.path.join(bundletemplatepath,
-													  "MacOS")):
-					if binary == "template":
-						continue
-					src = os.path.join(bundletemplatepath, "MacOS", binary)
-					dst = os.path.join(bundledistpath, "MacOS", binary)
-					if os.path.islink(src):
-						linkto = os.readlink(src)
-						if os.path.islink(dst) and os.readlink(dst) != linkto:
-							os.remove(dst)
-						if not os.path.islink(dst):
-							os.symlink(linkto, dst)
-					else:
-						shutil.copy2(src, dst)
-				resdir = os.path.join(bundledistpath, "Resources")
-				if not os.path.isdir(resdir):
-					os.mkdir(resdir)
-				if script.startswith(name):
-					iconsrc = os.path.join(pydir, name, "theme", "icons",
-										   script + ".icns")
-				else:
-					iconsrc = os.path.join(pydir, "0install",
-										   "ZeroInstall.icns")
-				icondst = os.path.join(resdir, script + ".icns")
-				if os.path.isfile(iconsrc) and not os.path.isfile(icondst):
-					shutil.copy2(iconsrc, icondst)
-			# README as .webloc file (link to homepage)
-			with codecs.open(os.path.join(dist_dir, "README.webloc"), "w",
-							 "UTF-8") as readme:
-				readme.write("""<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>URL</key>
-	<string>http://%s/</string>
-</dict>
-</plist>
-""" % domain.lower())
-			# Copy LICENSE.txt
-			shutil.copy2(os.path.join(pydir, "LICENSE.txt"),
-						 os.path.join(dist_dir, "LICENSE.txt"))
-	
-	if bdist_appdmg:
-		create_appdmg(zeroinstall)
-
-
-if __name__ == "__main__":
-	setup()
--- a/.pc/applied-patches	2016-08-24 23:08:44.542458026 +0100
--- b/.pc/applied-patches	1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-01_reproducible-build.diff
--- a/debian/patches/01_reproducible-build.diff	2016-08-24 23:08:44.478457355 +0100
--- b/debian/patches/01_reproducible-build.diff	2016-08-24 23:30:15.100269789 +0100
@@ -1,11 +1,24 @@
---- a/setup.py
-+++ b/setup.py
+Description: Make the build reproducible
+Author: Chris Lamb <lamby at debian.org>
+Last-Update: 2016-08-24
+
+--- dispcalgui-3.1.5.0.orig/setup.py
++++ dispcalgui-3.1.5.0/setup.py
+@@ -80,7 +80,7 @@ def replace_placeholders(tmpl_path, out_
+ 			strftime("%H:%M", 
+ 					 gmtime(lastmod_time or 
+ 							os.stat(tmpl_path).st_mtime)),
+-		"TIMESTAMP": str(int(lastmod_time)),
++		"TIMESTAMP": str(int(os.environ.get('SOURCE_DATE_EPOCH', lastmod_time))),
+ 		"SUMMARY": description,
+ 		"DESC": longdesc,
+ 		"APPDATADESC": "<p>\n\t\t\t" + longdesc.replace("\n", "\n\t\t\t").replace(".\n", ".\n\t\t</p>\n\t\t<p>\n") + "\n\t\t</p>",
 @@ -99,7 +99,7 @@ def replace_placeholders(tmpl_path, out_
  		"VERSION_SHORT": re.sub("(?:\.0){1,2}$", "", version),
  		"URL": "http://%s/" % domain.lower(),
  		"WX_MINVERSION": ".".join(str(n) for n in wx_minversion),
 -		"YEAR": strftime("%Y", gmtime())}
-+		"YEAR": strftime("%Y", gmtime(lastmod_time or os.stat(tmpl_path).st_mtime))}
++		"YEAR": strftime("%Y", gmtime(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))))}
  	mapping.update(iterable or {})
  	for key, val in mapping.iteritems():
  		tmpl_data = tmpl_data.replace("${%s}" % key, val)
--- a/setup.py	2016-08-24 23:08:44.478457355 +0100
--- b/setup.py	2016-08-24 23:27:35.000000000 +0100
@@ -99,7 +99,7 @@
 		"VERSION_SHORT": re.sub("(?:\.0){1,2}$", "", version),
 		"URL": "http://%s/" % domain.lower(),
 		"WX_MINVERSION": ".".join(str(n) for n in wx_minversion),
-		"YEAR": strftime("%Y", gmtime(lastmod_time or os.stat(tmpl_path).st_mtime))}
+		"YEAR": strftime("%Y", gmtime())}
 	mapping.update(iterable or {})
 	for key, val in mapping.iteritems():
 		tmpl_data = tmpl_data.replace("${%s}" % key, val)


More information about the Reproducible-builds mailing list