From f36c844f11c116f9a0cc017167f9ad93366eb1bc Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Mon, 6 Apr 2020 23:33:07 -0500 Subject: [PATCH] patchinstall.py: Bring up to patchinstall.sh. I think as of this commit it's feature-complete, minus a couple things I'm not sure we still care about. --- staging/patchinstall.py | 195 +++++++++++++++++++++++++++++++++------- 1 file changed, 161 insertions(+), 34 deletions(-) diff --git a/staging/patchinstall.py b/staging/patchinstall.py index ed7ea236..2f686e2b 100755 --- a/staging/patchinstall.py +++ b/staging/patchinstall.py @@ -1,41 +1,71 @@ #!/usr/bin/env python2 -import getopt, os, subprocess, sys +# Copyright (C) 2020 Zebediah Figura +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA +# -patchdir = os.path.dirname(os.path.realpath(__file__)) + '/../patches/' +import email.header, getopt, os, subprocess, sys, tempfile + +stagingdir = os.path.dirname(os.path.realpath(__file__)) + '/../' +patchdir = stagingdir + 'patches' if 'DESTDIR' in os.environ: winedir = os.environ['DESTDIR'] else: winedir = '.' backend = "patch" +force_autoconf = False applied = [] +patch_data = '' + def usage(): print ''' Usage: ./staging/patchinstall.py [DESTDIR=path] [options] [patchset ...] -Applies every patch to a Wine tree. +Applies all Wine-Staging patch sets, or each specified patch set plus its +dependencies, to a Wine tree. Options: - -h, --help print this message - --all apply all patches (except those excluded with -W) - -W exclude a patch - --no-autoconf do not run autoconf and tools/make_requests - --backend= use the given backend to apply patches: - patch use the `gitapply.sh` tool (a wrapper around `patch`) - git-am use `git am` - git-am-C1 use `git am -C1` - git-apply use `git apply` - -r, --rebase-mode alias for --backend=git-am-C1 --no-autoconf + -h, --help print this message + -v, --version print version information + -a, --all apply all patches (except those excluded with -W) + -W, --exclude exclude a patch + --no-autoconf do not run autoreconf and tools/make_requests + --force-autoconf run autoreconf and tools/make_requests after every patch + (ignored when not using git-am or git-am-C1 backends) + --backend= use the given backend to apply patches: + patch use the `gitapply.sh` tool (a wrapper around `patch`) + git-am use `git am` + git-am-C1 use `git am -C1` + git-apply use `git apply` + --no-patchlist do not generate a patch list (needed for `wine --patches`) + -r, --rebase-mode alias for --backend=git-am-C1 --no-autoconf --no-patchlist ''' +def run(*args, **kwargs): + print ' '.join(args[0]) + return subprocess.call(*args, **kwargs) + # return a patch to be shoved in patchlist below def parse_def_file(name, path): deps = [] if os.path.exists(path): - with open(path) as z: - for l in z.readlines(): + with open(path) as f: + for l in f.readlines(): if l.lower().startswith('depends: '): deps.append(l.split(' ')[1].strip()) elif l.lower().strip() == 'disabled: true': @@ -44,14 +74,54 @@ def parse_def_file(name, path): def apply_patch(patch): if backend == 'git-am': - return subprocess.call(['git','-C',winedir,'am',patch]) + return run(['git','-C',winedir,'am',patch]) elif backend == 'git-am-C1': - return subprocess.call(['git','-C',winedir,'am','-C1',patch]) + return run(['git','-C',winedir,'am','-C1',patch]) elif backend == 'patch': with open(patch) as f: + print patchdir+'/gitapply.sh <',patch return subprocess.call([patchdir+'/gitapply.sh'],stdin=f) elif backend == 'git-apply': - return subprocess.call(['git','-C',winedir,'apply','--index',patch]) + return run(['git','-C',winedir,'apply','--index',patch]) + +def run_autoconf(patch): + if not force_autoconf: return + + if backend != 'git-am' and backend != 'git-am-C1': + print 'Warning: ignoring --force-autoconf for backend ',backend + + need_autoreconf = False + need_make_requests = False + + with open(patch) as f: + for line in f.readlines(): + line = line.strip() + if line == '--- a/configure.ac' or line == '--- a/aclocal.m4': need_autoreconf = True + elif line == '--- a/server/protocol.def': need_make_requests = True + + if need_autoreconf: + run(['autoreconf','-f'], cwd=winedir) + if need_make_requests: + run(['./tools/make_requests'], cwd=winedir) + if need_autoreconf or need_make_requests: + run(['git','-C',winedir,'commit','-a','--amend','--no-edit']) + +def add_patch_data(patch): + global patch_data + author = '' + subject = '' + with open(patch) as f: + for line in f.readlines(): + header = email.header.decode_header(line) + if header[0][0] == 'From:': + author = header[1][0] + elif line[:5] == 'From:': + author = line[6:line.index('<')-1] + elif line[:8] == 'Subject:': + subject = line[9:].rstrip() + if '[' in subject: subject = subject[subject.index(']') + 1:] + if author and subject: break + patch_data += '+ {"%s", "%s", 1},\n' %(author, subject) def apply_set(patchlist, name): if name in applied: @@ -59,11 +129,14 @@ def apply_set(patchlist, name): for dep in patchlist[name]: if not apply_set(patchlist, dep): return False - print 'Applying',name for patch in sorted(os.listdir(patchdir+'/'+name)): - if patch.endswith('.patch') and patch.startswith('0') and apply_patch(patchdir+'/'+name+'/'+patch): - print 'Failed to apply patch %s/%s' %(name, patch) - return False + if patch.endswith('.patch') and patch.startswith('0'): + patch_file = patchdir + '/' + name + '/' + patch + if apply_patch(patch_file): + print 'Failed to apply patch %s/%s' %(name, patch) + return False + run_autoconf(patch_file) + add_patch_data(patch_file) applied.append(name) return True @@ -77,15 +150,58 @@ def add_patchset(patchlist, name): patchlist[name] = deps for dep in deps: add_patchset(patchlist, dep) +def generate_patchlist(patchlist): + with tempfile.NamedTemporaryFile(mode='w') as f: + f.write('''From: Wine Staging Team +Subject: Autogenerated patch list. + +diff --git a/libs/wine/config.c b/libs/wine/config.c +index 5262c76..0a3182f 100644 +--- a/libs/wine/config.c ++++ b/libs/wine/config.c +@@ -478,10 +478,''' + str(21 + patch_data.count('\n')) + ''' @@ const char *wine_get_version(void) + return PACKAGE_VERSION; + } + ++static const struct ++{ ++ const char *author; ++ const char *subject; ++ int revision; ++} ++wine_patch_data[] = ++{ +''' + patch_data + '''+ {NULL, NULL, 0} ++}; ++ + /* return the applied non-standard patches */ + const void *wine_get_patches(void) + { +- return NULL; ++ return &wine_patch_data[0]; + } + + /* return the build id string */ +''') + f.flush() + apply_patch(f.name) + def main(): - global backend + global backend, force_autoconf patchlist = {} excluded = [] no_autoconf = False - force_autoconf = False + no_patchlist = False try: - opts, args = getopt.getopt(sys.argv[1:], 'hrW:', ['all', 'backend', 'help', 'no-autoconf']) + opts, args = getopt.gnu_getopt(sys.argv[1:], 'ahrvW:', \ + ['all', + 'backend=', + 'force-autoconf', + 'help', + 'no-autoconf', + 'no-patchlist', + 'version']) except getopt.GetoptError as err: print str(err) sys.exit(2) @@ -94,12 +210,19 @@ def main(): if o == '-h' or o == '--help': usage() sys.exit(0) + elif o == '-v' or o == '--version': + with open(stagingdir + 'staging/VERSION') as f: + print f.read().rstrip() + print 'wine-staging is distributed under the GNU LGPL 2.1.' + print 'See individual files for copyright information.' + sys.exit(0) elif o == '-r' or o == '--rebase-mode': no_autoconf = True + no_patchlist = True backend = "git-am-C1" - elif o == '-W': + elif o == '-W' or o == '--exclude': excluded.append(a) - elif o == '--all': + elif o == '-a' or o == '--all': for name in os.listdir(patchdir): path = patchdir + '/' + name if os.path.isdir(path): @@ -110,13 +233,17 @@ def main(): backend = a elif o == '--no-autoconf': no_autoconf = True + elif o == '--force-autoconf': + force_autoconf = True + elif o == '--no-patchlist': + no_patchlist = True for a in args: add_patchset(patchlist, a) for p in excluded: del patchlist[p] if not patchlist: - print 'Error: no patches specified' + print 'No patches specified, either use -a or specify one or more patch sets as arguments.' sys.exit(1) # Check that all of our dependencies exist @@ -133,12 +260,12 @@ def main(): if not apply_set(patchlist, p): sys.exit(1) - # we successfully applied everything, autogen some files so we can build - if not no_autoconf: - print 'Calling autoreconf -f...' - subprocess.call(['autoreconf','-f'],cwd=winedir) - print 'Calling tools/make_requests...' - subprocess.call(['./tools/make_requests'],cwd=winedir) + if 'Staging' in patchlist and not no_patchlist: + generate_patchlist(patchlist) + + if not no_autoconf and not force_autoconf: + run(['autoreconf','-f'],cwd=winedir) + run(['./tools/make_requests'],cwd=winedir) sys.exit(0) if __name__ == '__main__':