mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2024-11-21 16:46:54 -08:00
patchupdate.py: Use author information from patch files instead of definition file.
This commit is contained in:
parent
f9c1ebaa12
commit
972842c50e
72
debian/tools/patchupdate.py
vendored
72
debian/tools/patchupdate.py
vendored
@ -21,6 +21,7 @@
|
||||
|
||||
import hashlib
|
||||
import itertools
|
||||
import operator
|
||||
import os
|
||||
import patchutils
|
||||
import pickle
|
||||
@ -54,16 +55,9 @@ class PatchUpdaterError(RuntimeError):
|
||||
"""Failed to update patches."""
|
||||
pass
|
||||
|
||||
class AuthorInfo(object):
|
||||
def __init__(self):
|
||||
self.author = ""
|
||||
self.subject = ""
|
||||
self.revision = ""
|
||||
|
||||
class PatchSet(object):
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
self.authors = []
|
||||
self.fixes = []
|
||||
self.changes = []
|
||||
self.disabled = False
|
||||
@ -82,6 +76,16 @@ def _pairs(a):
|
||||
for k in a[i+1:]:
|
||||
yield (j, k)
|
||||
|
||||
def _unique(iterable, key=None):
|
||||
"List unique elements, preserving order. Remember only the element just seen."
|
||||
# unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
|
||||
# unique_justseen('ABBCcAD', str.lower) --> A B C A D
|
||||
return itertools.imap(next, itertools.imap(operator.itemgetter(1), itertools.groupby(iterable, key)))
|
||||
|
||||
def _escape(s):
|
||||
"""Escape string inside of '...' quotes."""
|
||||
return s.replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "'\\''")
|
||||
|
||||
def _load_dict(filename):
|
||||
"""Load a Python dictionary object from a file."""
|
||||
try:
|
||||
@ -161,7 +165,7 @@ def enum_directories(revision, path):
|
||||
return dirs
|
||||
|
||||
def read_definition(revision, filename, name_to_id):
|
||||
"""Read a definition file and return information as tuple (authors, depends, fixes)."""
|
||||
"""Read a definition file and return information as tuple (depends, fixes)."""
|
||||
|
||||
filename = os.path.join(filename, "definition")
|
||||
if revision is None:
|
||||
@ -175,10 +179,8 @@ def read_definition(revision, filename, name_to_id):
|
||||
except CalledProcessError:
|
||||
raise IOError("Failed to load %s" % filename)
|
||||
|
||||
authors = []
|
||||
depends = set()
|
||||
fixes = []
|
||||
info = AuthorInfo()
|
||||
disabled = False
|
||||
|
||||
for line in content.split("\n"):
|
||||
@ -187,25 +189,10 @@ def read_definition(revision, filename, name_to_id):
|
||||
|
||||
tmp = line.split(":", 1)
|
||||
if len(tmp) != 2:
|
||||
if len(info.author) and len(info.subject):
|
||||
authors.append(info)
|
||||
info = AuthorInfo()
|
||||
continue
|
||||
|
||||
key, val = tmp[0].lower(), tmp[1].strip()
|
||||
if key == "author":
|
||||
if len(info.author): info.author += ", "
|
||||
info.author += val
|
||||
|
||||
elif key == "subject" or key == "title":
|
||||
if len(info.subject): info.subject += " "
|
||||
info.subject += val
|
||||
|
||||
elif key == "revision":
|
||||
if len(info.revision): info.revision += ", "
|
||||
info.revision += val
|
||||
|
||||
elif key == "depends":
|
||||
if key == "depends":
|
||||
if name_to_id is not None:
|
||||
if not name_to_id.has_key(val):
|
||||
raise PatchUpdaterError("Definition file %s references unknown dependency %s" % (filename, val))
|
||||
@ -225,12 +212,16 @@ def read_definition(revision, filename, name_to_id):
|
||||
elif key == "disabled":
|
||||
disabled = parse_int(val)
|
||||
|
||||
elif key == "author" or \
|
||||
key == "subject" or \
|
||||
key == "title" or \
|
||||
key == "revision":
|
||||
pass
|
||||
|
||||
else:
|
||||
print "WARNING: Ignoring unknown command in definition file %s: %s" % (filename, line)
|
||||
|
||||
if len(info.author) and len(info.subject):
|
||||
authors.append(info)
|
||||
return authors, depends, fixes, disabled
|
||||
return depends, fixes, disabled
|
||||
|
||||
def read_patchset(revision = None):
|
||||
"""Read information about all patchsets for a specific revision."""
|
||||
@ -270,7 +261,7 @@ def read_patchset(revision = None):
|
||||
# Now read the definition files in a second step
|
||||
for i, patch in all_patches.iteritems():
|
||||
try:
|
||||
patch.authors, patch.depends, patch.fixes, patch.disabled = \
|
||||
patch.depends, patch.fixes, patch.disabled = \
|
||||
read_definition(revision, os.path.join(config.path_patches, patch.name), name_to_id)
|
||||
except IOError:
|
||||
raise PatchUpdaterError("Missing definition file for %s" % patch.name)
|
||||
@ -484,17 +475,6 @@ def generate_makefile(all_patches):
|
||||
for i, patch in all_patches.iteritems():
|
||||
fp.write("# Patchset %s\n" % patch.name)
|
||||
fp.write("# |\n")
|
||||
fp.write("# | Included patches:\n")
|
||||
|
||||
# List all patches and their corresponding authors
|
||||
for info in patch.authors:
|
||||
if not info.subject: continue
|
||||
s = []
|
||||
if info.revision and info.revision != "1": s.append("rev %s" % info.revision)
|
||||
if info.author: s.append("by %s" % info.author)
|
||||
if len(s): s = " [%s]" % ", ".join(s)
|
||||
fp.write("# | *\t%s\n" % "\n# | \t".join(textwrap.wrap(info.subject + s, 120)))
|
||||
fp.write("# |\n")
|
||||
|
||||
# List all bugs fixed by this patchset
|
||||
if any([bugid is not None for bugid, bugname in patch.fixes]):
|
||||
@ -517,13 +497,11 @@ def generate_makefile(all_patches):
|
||||
fp.write("\t$(call APPLY_FILE,%s)\n" % os.path.join(patch.name, f))
|
||||
|
||||
# Create *.ok file (used to generate patchlist)
|
||||
if len(patch.authors):
|
||||
if len(patch.patches):
|
||||
fp.write("\t@( \\\n")
|
||||
for info in patch.authors:
|
||||
if not info.subject: continue
|
||||
s = info.subject.replace("\\", "\\\\").replace("\"", "\\\"").replace("'", "'\\''")
|
||||
if info.revision and info.revision != "1": s += " [rev %s]" % info.revision
|
||||
fp.write("\t\techo '+ { \"%s\", \"%s\", \"%s\" },'; \\\n" % (patch.name, info.author, s))
|
||||
for p in _unique(patch.patches, key=lambda p: (p.patch_author, p.patch_subject)):
|
||||
fp.write("\t\techo '+ { \"%s\", \"%s\", \"%s\" },'; \\\n" % \
|
||||
(patch.name, _escape(p.patch_author), _escape(p.patch_subject)))
|
||||
fp.write("\t) > %s.ok\n" % patch.name)
|
||||
else:
|
||||
fp.write("\ttouch %s.ok\n" % patch.name)
|
||||
|
58
debian/tools/patchutils.py
vendored
58
debian/tools/patchutils.py
vendored
@ -19,6 +19,7 @@
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
#
|
||||
|
||||
import email.header
|
||||
import collections
|
||||
import difflib
|
||||
import hashlib
|
||||
@ -37,7 +38,11 @@ class PatchApplyError(RuntimeError):
|
||||
pass
|
||||
|
||||
class PatchObject(object):
|
||||
def __init__(self, filename):
|
||||
def __init__(self, filename, header):
|
||||
self.patch_author = header['author'] if header else None
|
||||
self.patch_email = header['email'] if header else None
|
||||
self.patch_subject = header['subject'] if header else None
|
||||
|
||||
self.extracted_patch = None
|
||||
self.unique_hash = None
|
||||
|
||||
@ -134,10 +139,10 @@ def read_patch(filename):
|
||||
tmp, self.peeked = self.peeked, None
|
||||
return tmp[1]
|
||||
|
||||
def _read_single_patch(fp, oldname=None, newname=None):
|
||||
def _read_single_patch(fp, header, oldname=None, newname=None):
|
||||
"""Internal function to read a single patch from a file."""
|
||||
|
||||
patch = PatchObject(fp.filename)
|
||||
patch = PatchObject(fp.filename, header)
|
||||
patch.offset_begin = fp.tell()
|
||||
patch.oldname = oldname
|
||||
patch.newname = newname
|
||||
@ -152,28 +157,39 @@ def read_patch(filename):
|
||||
line = fp.peek()
|
||||
if line is None:
|
||||
break
|
||||
|
||||
elif line.startswith("--- "):
|
||||
patch.oldname = line[4:].strip()
|
||||
|
||||
elif line.startswith("+++ "):
|
||||
patch.newname = line[4:].strip()
|
||||
|
||||
elif line.startswith("old mode") or line.startswith("deleted file mode"):
|
||||
pass # ignore
|
||||
|
||||
elif line.startswith("new mode "):
|
||||
patch.newmode = line[9:].strip()
|
||||
|
||||
elif line.startswith("new file mode "):
|
||||
patch.newmode = line[14:].strip()
|
||||
|
||||
elif line.startswith("new mode") or line.startswith("new file mode"):
|
||||
raise PatchParserError("Unable to parse header line '%s'." % line)
|
||||
|
||||
elif line.startswith("copy from") or line.startswith("copy to"):
|
||||
raise NotImplementedError("Patch copy header not implemented yet.")
|
||||
|
||||
elif line.startswith("rename "):
|
||||
raise NotImplementedError("Patch rename header not implemented yet.")
|
||||
|
||||
elif line.startswith("similarity index") or line.startswith("dissimilarity index"):
|
||||
pass # ignore
|
||||
|
||||
elif line.startswith("index "):
|
||||
r = re.match("^index ([a-fA-F0-9]*)\.\.([a-fA-F0-9]*)", line)
|
||||
if not r: raise PatchParserError("Unable to parse index header line '%s'." % line)
|
||||
patch.oldsha1, patch.newsha1 = r.group(1), r.group(2)
|
||||
|
||||
else:
|
||||
break
|
||||
assert fp.read() == line
|
||||
@ -271,19 +287,51 @@ def read_patch(filename):
|
||||
patch.offset_end = fp.tell()
|
||||
return patch
|
||||
|
||||
header = {}
|
||||
with _FileReader(filename) as fp:
|
||||
while True:
|
||||
line = fp.peek()
|
||||
if line is None:
|
||||
break
|
||||
|
||||
elif line.startswith("From: "):
|
||||
author = ' '.join([data.decode(format or 'utf-8').encode('utf-8') for \
|
||||
data, format in email.header.decode_header(line[6:])])
|
||||
r = re.match("\"?([^\"]*)\"? <(.*)>", author)
|
||||
if r is None: raise NotImplementedError("Failed to parse From - header.")
|
||||
header['author'] = r.group(1).strip()
|
||||
header['email'] = r.group(2).strip()
|
||||
assert fp.read() == line
|
||||
|
||||
elif line.startswith("Subject: "):
|
||||
subject = line[9:].rstrip("\r\n")
|
||||
assert fp.read() == line
|
||||
while True:
|
||||
line = fp.peek()
|
||||
if not line.startswith(" "): break
|
||||
subject += line.rstrip("\r\n")
|
||||
assert fp.read() == line
|
||||
r = re.match("^\\[PATCH[^]]*\\](.*)", subject)
|
||||
if r is not None: subject = r.group(1)
|
||||
r = re.match("(.*)\\(try [0-9]+\\)$", subject)
|
||||
if r is None: r = re.match("(.*), v[0-9]+$", subject)
|
||||
if r is None: r = re.match("^[^:]+ v[0-9]+: (.*)", subject)
|
||||
if r is not None: subject = r.group(1)
|
||||
subject = subject.strip()
|
||||
if not subject.endswith("."): subject += "."
|
||||
header['subject'] = subject.strip()
|
||||
|
||||
elif line.startswith("diff --git "):
|
||||
tmp = line.strip().split(" ")
|
||||
if len(tmp) != 4: raise PatchParserError("Unable to parse git diff header line '%s'." % line)
|
||||
yield _read_single_patch(fp, tmp[2].strip(), tmp[3].strip())
|
||||
yield _read_single_patch(fp, header, tmp[2].strip(), tmp[3].strip())
|
||||
|
||||
elif line.startswith("--- "):
|
||||
yield _read_single_patch(fp)
|
||||
yield _read_single_patch(fp, header)
|
||||
|
||||
elif line.startswith("@@ -") or line.startswith("+++ "):
|
||||
raise PatchParserError("Patch didn't start with a git or diff header.")
|
||||
|
||||
else:
|
||||
assert fp.read() == line
|
||||
|
||||
|
661
patches/Makefile
661
patches/Makefile
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user