patchupdate.py: Instead of showing the current fixes in README.md, also show information about last stable version.

This commit is contained in:
Sebastian Lackner 2014-08-13 01:59:01 +02:00
parent 536e2fe646
commit 731a458cbe
3 changed files with 181 additions and 122 deletions

View File

@ -13,11 +13,21 @@ which are not present in regular wine, and always report such issues to us
Included bugfixes and improvements
----------------------------------
Wine-Compholio contains fixes for the following Wine bugs:
Fixed bugs for the next release (4):
* Other Pipelight-specific enhancements
* Some applications neeed kernel32.GetFinalPathNameByHandle ([Wine Bug #36073](http://bugs.winehq.org/show_bug.cgi?id=36073 "OneDrive crashes on unimplemented function KERNEL32.dll.GetFinalPathNameByHandleW"))
* Support for DwmInvalidateIconicBitmaps ([Wine Bug #32977](http://bugs.winehq.org/show_bug.cgi?id=32977 "Solidworks 2012 needs unimplemented function dwmapi.dll.DwmInvalidateIconicBitmaps (Win7 mode)"))
* nVidia driver for high-end laptop cards does not list all supported resolutions
Fixed bugs in Wine-Compholio 1.7.24 (45):
* ATL IOCS data should not be stored in GWLP_USERDATA ([Wine Bug #21767](http://bugs.winehq.org/show_bug.cgi?id=21767 "JLC's Internet TV crashes on startup"))
* Add Dynamic DST exceptions for Israel Standard Time ([Wine Bug #36374](http://bugs.winehq.org/show_bug.cgi?id=36374 "Israel timezone handled incorrectly"))
* Add default ACLs for user shell folders
* Add implementation of WTSEnumerateProcessesW ([Wine Bug #29903](http://bugs.winehq.org/show_bug.cgi?id=29903 "Some Microsoft debuggers fail to enumerate processes due to wtsapi32.WTSEnumerateProcessesW() being a stub (Microsoft Visual Studio 2005, DbgCLR from .NET 2.0 SDK)"))
* Add support for Dynamic DST (daylight saving time) information in registry
* Add support for extra large and jumbo icon lists in shell32 ([Wine Bug #24721](http://bugs.winehq.org/show_bug.cgi?id=24721 "Explorer++ crashes when choosing to view large icons or extra large icons"))
* Allow special characters in pipe names ([Wine Bug #28995](http://bugs.winehq.org/show_bug.cgi?id=28995 "Unable to use named pipes with \">\" character in the name"))
* Audio stuttering and performance drops in multiple applications ([Wine Bug #30639](http://bugs.winehq.org/show_bug.cgi?id=30639 "Audio stuttering and performance drops in Star Wolves 3"))
@ -29,14 +39,18 @@ Wine-Compholio contains fixes for the following Wine bugs:
* Implement LoadIconMetric function ([Wine Bug #35375](http://bugs.winehq.org/show_bug.cgi?id=35375 "Multiple applications need Vista+ API COMCTL32.dll.380 a.k.a. 'LoadIconMetric' (Solidworks 2013 systray monitor, Microsoft One/SkyDrive)"))
* Implement a Microsoft Yahei replacement font ([Wine Bug #13829](http://bugs.winehq.org/show_bug.cgi?id=13829 "Wine does not have CJK fonts"))
* Implement an Arial replacement font ([Wine Bug #32323](http://bugs.winehq.org/show_bug.cgi?id=32323 "Netflix (Silverlight 4.x) and several .NET Framework 3.x/4.0 WPF apps require either Arial or Verdana to be installed"))
* Lockfree algorithm for filedescriptor cache (improves file access speed)
* Make it possible to change media center / tablet pc status ([Wine Bug #18732](http://bugs.winehq.org/show_bug.cgi?id=18732 "Microsoft Experience Pack for Tablet PC 1 refuses to install"))
* Need for Speed 3 installer requires devices in HKEY_DYN_DATA ([Wine Bug #7115](http://bugs.winehq.org/show_bug.cgi?id=7115 "Need for Speed III installer fails in Win9X mode, reporting \"Could not get 'HardWareKey' value\" (active PnP device keys in 'HKEY_DYN_DATA\\\\Config Manager\\\\Enum' missing)"))
* ~~Old games cannot locate software-only renderer~~ ([Wine Bug #32581](http://bugs.winehq.org/show_bug.cgi?id=32581 "Invalid dwFlags of reference rasterizer's HAL D3DDEVICEDESC"))
* ~~Other Pipelight specific enhancements~~
* Reduced SetTimer minimum value from 10 ms to 5 ms (improves Silverlight framerates)
* Return correct IMediaSeeking stream positions in quartz ([Wine Bug #23174](http://bugs.winehq.org/show_bug.cgi?id=23174 "Fallout 3: Diologue and Video/sound issues"))
* SO_CONNECT_TIME returns the appropriate time
* Set ldr.EntryPoint for main executable ([Wine Bug #33034](http://bugs.winehq.org/show_bug.cgi?id=33034 "Many GFWL (Games For Windows Live) 1.x/2.x/3.x games crash or exit silently on startup (DiRT 2/3, GTA IV Steam)"))
* Some applications neeed kernel32.GetFinalPathNameByHandle ([Wine Bug #36073](http://bugs.winehq.org/show_bug.cgi?id=36073 "OneDrive crashes on unimplemented function KERNEL32.dll.GetFinalPathNameByHandleW"))
* Support for AllocateAndGetTcpExTableFromStack ([Wine Bug #34372](http://bugs.winehq.org/show_bug.cgi?id=34372 "Add missing function AllocateAndGetTcpExTableFromStack() to iphlpapi.dll"))
* Support for DwmInvalidateIconicBitmaps ([Wine Bug #32977](http://bugs.winehq.org/show_bug.cgi?id=32977 "Solidworks 2012 needs unimplemented function dwmapi.dll.DwmInvalidateIconicBitmaps (Win7 mode)"))
* Support for GetSystemTimes ([Wine Bug #19813](http://bugs.winehq.org/show_bug.cgi?id=19813 "Voddler needs GetSystemTimes to run"))
* Support for GetVolumePathName
* Support for ITextRange, ITextFont and ITextPara ([Wine Bug #18303](http://bugs.winehq.org/show_bug.cgi?id=18303 "Adobe Acrobat Pro 7: Crashes when selecting the \"edit\" menu while having a file open."))
* Support for Junction Points ([Wine Bug #12401](http://bugs.winehq.org/show_bug.cgi?id=12401 "Support junction points, i.e. DeviceIoCtl(FSCTL_SET_REPARSE_POINT/FSCTL_GET_REPARSE_POINT)"))
* Support for NtSetInformationFile class FileDispositionInformation ([Wine Bug #30397](http://bugs.winehq.org/show_bug.cgi?id=30397 "Multiple applications need support for NtSetInformationFile class FileDispositionInformation (Cygwin installer, Stylizer 5.x Visual CSS editor, Spoon Studio 2011 (ex Xenocode) application sandboxing scheme)"))
@ -52,19 +66,8 @@ Wine-Compholio contains fixes for the following Wine bugs:
* Support for ws2_32.inet_pton ([Wine Bug #36713](http://bugs.winehq.org/show_bug.cgi?id=36713 "Watch_Dogs requires ws2_32.inet_pton"))
* Use manual relay for RunDLL_CallEntry16 in shell32 ([Wine Bug #23033](http://bugs.winehq.org/show_bug.cgi?id=23033 "Tages Protection v5.x: games report \"DLL not found shell.dll16.dll\" (Runaway 2: The Dream Of The Turtle, ...)"))
* Workaround for TransactNamedPipe not being supported ([Wine Bug #17273](http://bugs.winehq.org/show_bug.cgi?id=17273 "Many apps and games need SetNamedPipeHandleState implementation (support for named pipe message mode)(FireFox+Flash, Win8/NET 4.x SDK/vcrun2012, WiX installers)"))
Besides that the following additional changes are included:
* Add default ACLs for user shell folders
* Add support for Dynamic DST (daylight saving time) information in registry
* Lockfree algorithm for filedescriptor cache (improves file access speed)
* Other Pipelight-specific enhancements
* Reduced SetTimer minimum value from 10 ms to 5 ms (improves Silverlight framerates)
* SO_CONNECT_TIME returns the appropriate time
* Support for GetVolumePathName
* Workaround for shlwapi URLs with relative paths
* XEMBED support for embedding Wine windows inside Linux applications
* nVidia driver for high-end laptop cards does not list all supported resolutions
How to install Wine-Compholio

View File

@ -13,12 +13,6 @@ which are not present in regular wine, and always report such issues to us
Included bugfixes and improvements
----------------------------------
Wine-Compholio contains fixes for the following Wine bugs:
{bugs}
Besides that the following additional changes are included:
{fixes}

View File

@ -39,21 +39,21 @@ cached_patch_result = {}
cached_original_src = {}
class config(object):
path_depcache = "./.depcache"
path_srccache = "./.srccache"
path_depcache = ".depcache"
path_srccache = ".srccache"
path_patches = "./patches"
path_changelog = "./debian/changelog"
path_wine = "./debian/tools/wine"
path_patches = "patches"
path_changelog = "debian/changelog"
path_wine = "debian/tools/wine"
path_template_Makefile = "./debian/tools/Makefile.in"
path_Makefile = "./patches/Makefile"
path_template_Makefile = "debian/tools/Makefile.in"
path_Makefile = "patches/Makefile"
path_README_md = "./README.md"
path_template_README_md = "./debian/tools/README.md.in"
path_README_md = "README.md"
path_template_README_md = "debian/tools/README.md.in"
path_DEVELOPER_md = "./DEVELOPER.md"
path_template_DEVELOPER_md = "./debian/tools/DEVELOPER.md.in"
path_DEVELOPER_md = "DEVELOPER.md"
path_template_DEVELOPER_md = "debian/tools/DEVELOPER.md.in"
class PatchUpdaterError(RuntimeError):
"""Failed to update patches."""
@ -118,20 +118,66 @@ def _winebugs_query_short_desc(bugids):
result[bugid] = element.firstChild.data
return result
def read_definition(filename, name_to_id = None, revision = None):
# Read information from changelog
def _read_changelog():
with open(config.path_changelog) as fp:
for line in fp:
r = re.match("^([a-zA-Z0-9][^(]*)\((.*)\) ([^;]*)", line)
if r: yield (r.group(1).strip(), r.group(2).strip(), r.group(3).strip())
# Get version number of the latest stable release
def _stable_compholio_version():
for package, version, distro in _read_changelog():
if distro.lower() != "unreleased":
return version
# Get latest wine commit
def _latest_wine_commit():
if not os.path.isdir(config.path_wine):
raise PatchUpdaterError("Please create a symlink to the wine repository in %s" % config.path_wine)
commit = subprocess.check_output(["git", "rev-parse", "origin/master"], cwd=config.path_wine).strip()
assert len(commit) == 40
return commit
def enum_directories(revision, path):
"""Enumerate all subdirectories of 'path' at a specific revision."""
dirs = []
if path[0:2] == "./":
path = path[2:]
elif path[0] == "/":
raise RuntimeError("Expected relative path, not an absolute path")
if revision is None:
for name in os.listdir(path):
if name in [".", ".."]: continue
subdirectory = os.path.join(path, name)
if not os.path.isdir(subdirectory):
continue
dirs.append((name, subdirectory))
else:
filename = "%s:%s" % (revision, path)
with open(os.devnull, 'w') as devnull:
content = subprocess.check_output(["git", "show", filename], stderr=devnull)
lines = content.split("\n")
if not lines[0].startswith("tree ") or lines[1] != "":
raise RuntimeError("Unexpected output from 'git show %s'" % filename)
for name in lines[2:]:
if name == "" or name[-1] != "/": continue
name = name[:-1]
dirs.append((name, os.path.join(path, name)))
return dirs
def read_definition(revision, filename, name_to_id):
"""Read a definition file and return information as tuple (authors, depends, fixes)."""
filename = os.path.join(filename, "definition")
filename = "%s:%s" % (revision if revision else "", os.path.join(filename, "definition"))
try:
if revision is not None:
filename = "%s:%s" % (revision, filename)
with open(os.devnull, 'w') as devnull:
content = subprocess.check_output(["git", "show", filename], stderr=devnull)
else:
with open(filename) as fp:
content = fp.read()
except IOError:
raise PatchUpaterError("Missing definition file %s" % filename)
with open(os.devnull, 'w') as devnull:
content = subprocess.check_output(["git", "show", filename], stderr=devnull)
except CalledProcessError:
raise IOError("Failed to read %s" % filename)
authors = []
depends = set()
@ -153,17 +199,21 @@ def read_definition(filename, name_to_id = None, revision = None):
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 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))
depends.add(name_to_id[val])
elif key == "fixes":
r = re.match("^[0-9]+$", val)
if r:
@ -174,6 +224,7 @@ def read_definition(filename, name_to_id = None, revision = None):
fixes.append((int(r.group(1)), r.group(2).strip()))
continue
fixes.append((None, val))
else:
print "WARNING: Ignoring unknown command in definition file %s: %s" % (deffile, line)
@ -181,46 +232,36 @@ def read_definition(filename, name_to_id = None, revision = None):
authors.append(info)
return authors, depends, fixes
def read_patchsets(directory):
"""Read information about all patchsets in a given directory."""
def _iter_kv_from_file(filename):
"""Iterate through all key/value pairs in a file."""
with open(filename) as fp:
for line in fp:
if line.startswith("#"):
continue
tmp = line.split(":", 1)
if len(tmp) != 2:
yield None, None
else:
yield tmp[0].lower(), tmp[1].strip()
def read_patchset(revision = None):
"""Read information about all patchsets for a specific revision."""
unique_id = itertools.count()
all_patches = {}
name_to_id = {}
# Read in sorted order (to ensure created Makefile doesn't change too much)
for name in sorted(os.listdir(directory)):
if name in [".", ".."]: continue
subdirectory = os.path.join(directory, name)
if not os.path.isdir(subdirectory): continue
for name, subdirectory in sorted(enum_directories(revision, config.path_patches)):
patch = PatchSet(name)
# Enumerate .patch files in the given directory, enumerate individual patches and affected files
for f in sorted(os.listdir(subdirectory)):
if not f.endswith(".patch") or not os.path.isfile(os.path.join(subdirectory, f)):
continue
patch.files.append(f)
for p in patchutils.read_patch(os.path.join(subdirectory, f)):
patch.patches.append(p)
patch.modified_files.add(p.modified_file)
if revision is None:
# No single patch within this directory, ignore it
if len(patch.patches) == 0:
del patch
continue
# If its the latest revision, then request additional information
if not os.path.isdir(subdirectory):
raise RuntimeError("Unable to open directory %s" % subdirectory)
# Enumerate .patch files in the given directory, enumerate individual patches and affected files
for f in sorted(os.listdir(subdirectory)):
if not f.endswith(".patch") or not os.path.isfile(os.path.join(subdirectory, f)):
continue
patch.files.append(f)
for p in patchutils.read_patch(os.path.join(subdirectory, f)):
patch.modified_files.add(p.modified_file)
patch.patches.append(p)
# No single patch within this directory, ignore it
if len(patch.patches) == 0:
del patch
continue
i = next(unique_id)
all_patches[i] = patch
@ -228,8 +269,11 @@ def read_patchsets(directory):
# Now read the definition files in a second step
for i, patch in all_patches.iteritems():
patch.authors, patch.depends, patch.fixes = \
read_definition(os.path.join(directory, patch.name), name_to_id=name_to_id)
try:
patch.authors, patch.depends, patch.fixes = \
read_definition(revision, os.path.join(config.path_patches, patch.name), name_to_id)
except IOError:
raise PatchUpaterError("Missing definition file for %s" % patch.name)
return all_patches
@ -475,75 +519,93 @@ def generate_makefile(all_patches):
fp.write("\ttouch %s.ok\n" % patch.name)
fp.write("\n");
def generate_markdown(all_patches):
def generate_markdown(all_patches, stable_patches, stable_compholio_version):
"""Generate README.md and DEVELOPER.md including information about specific patches and bugfixes."""
# Get list of all bugs (including short_desc from Wine bugzilla)
def _all_bugs():
all_bugs = []
for i, patch in all_patches.iteritems():
for bugid, bugname in patch.fixes:
if bugid is not None: all_bugs.append((bugid, bugname))
bug_short_desc = _winebugs_query_short_desc([bugid for bugid, bugname in all_bugs])
for bugid, bugname in sorted(all_bugs, key=lambda x: x[1]):
if bugid is None: continue
def _format_bug(mode, bugid, bugname):
if bugid is not None:
short_desc = bug_short_desc[bugid]
if bugname is None: bugname = short_desc
yield "%s ([Wine Bug #%d](http://bugs.winehq.org/show_bug.cgi?id=%d \"%s\"))" % \
(bugname, bugid, bugid, short_desc.replace("\\", "\\\\").replace("\"", "\\\""))
if mode < 0: bugname = "~~%s~~" % bugname
if bugid is None: return "* %s" % bugname
return "* %s ([Wine Bug #%d](http://bugs.winehq.org/show_bug.cgi?id=%d \"%s\"))" % \
(bugname, bugid, bugid, short_desc.replace("\\", "\\\\").replace("\"", "\\\""))
# Get list of all fixes
def _all_fixes():
all_fixes = []
for i, patch in all_patches.iteritems():
for bugid, bugname in patch.fixes:
if bugid is None: all_fixes.append((bugid, bugname))
for bugid, bugname in sorted(all_fixes, key=lambda x: x[1]):
yield bugname
all_bugids = set()
all_fixes = {}
# Read information from changelog
def _read_changelog():
with open(config.path_changelog) as fp:
for line in fp:
r = re.match("^([a-zA-Z0-9][^(]*)\((.*)\) ([^;]*)", line)
if r: yield (r.group(1).strip(), r.group(2).strip(), r.group(3).strip())
# Get fixes for current version
for i, patch in all_patches.iteritems():
for bugid, bugname in patch.fixes:
if bugid is not None: all_bugids.add(bugid)
key = bugid if bugid is not None else bugname
all_fixes[key] = [1, bugid, bugname]
# Get version number of the latest stable release
def _latest_stable_version():
for package, version, distro in _read_changelog():
if distro.lower() != "unreleased":
return version
# Compare with fixes for latest stable version
for i, patch in stable_patches.iteritems():
for bugid, bugname in patch.fixes:
if bugid is not None: all_bugids.add(bugid)
key = bugid if bugid is not None else bugname
if all_fixes.has_key(key):
all_fixes[key][0] = 0
else:
all_fixes[key] = [-1, bugid, bugname]
# Create enumeration from list
def _enum(x):
return "* " + "\n* ".join(x)
# Generate lists for all new and old fixes
new_fixes = [(mode, bugid, bugname) for dummy, (mode, bugid, bugname) in
all_fixes.iteritems() if mode > 0]
old_fixes = [(mode, bugid, bugname) for dummy, (mode, bugid, bugname) in
all_fixes.iteritems() if mode <= 0]
# Query information from bugzilla
bug_short_desc = _winebugs_query_short_desc(all_bugids)
# Generate information for current version
lines = []
if len(new_fixes):
lines.append("Fixed bugs for the next release (%d):" % len(new_fixes))
lines.append("")
for mode, bugid, bugname in sorted(new_fixes, key=lambda x: x[2]):
lines.append(_format_bug(mode, bugid, bugname))
lines.append("")
lines.append("")
lines.append("Fixed bugs in Wine-Compholio %s (%d):" % (stable_compholio_version, len(old_fixes)))
lines.append("")
for mode, bugid, bugname in sorted(old_fixes, key=lambda x: x[2]):
lines.append(_format_bug(mode, bugid, bugname))
# Update README.md
with open(config.path_template_README_md) as template_fp:
template = template_fp.read()
with open(config.path_README_md, "w") as fp:
fp.write(template.format(bugs=_enum(_all_bugs()), fixes=_enum(_all_fixes())))
fp.write(template.format(fixes="\n".join(lines)))
# Update DEVELOPER.md
with open(config.path_template_DEVELOPER_md) as template_fp:
template = template_fp.read()
with open(config.path_DEVELOPER_md, "w") as fp:
fp.write(template.format(version=_latest_stable_version()))
fp.write(template.format(version=stable_compholio_version))
if __name__ == "__main__":
# Get the latest wine commit (sha1)
if not os.path.isdir(config.path_wine):
raise RuntimeError("Please create a symlink to the wine repository in %s" % config.path_wine)
latest_wine_commit = subprocess.check_output(["git", "rev-parse", "origin/master"], cwd=config.path_wine).strip()
assert len(latest_wine_commit) == 40
try:
all_patches = read_patchsets(config.path_patches)
# Get information about Wine and Compholio version
latest_wine_commit = _latest_wine_commit()
stable_compholio_version = _stable_compholio_version()
# Read current and stable patches
all_patches = read_patchset()
stable_patches = read_patchset(revision="v%s" % stable_compholio_version)
# Check dependencies
verify_dependencies(all_patches)
# Update Makefile, README.md and DEVELOPER.md
generate_makefile(all_patches)
generate_markdown(all_patches, stable_patches, stable_compholio_version)
except PatchUpdaterError as e:
print ""
print "ERROR: %s" % e
print ""
exit(1)
generate_makefile(all_patches)
generate_markdown(all_patches)