mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2024-11-21 16:46:54 -08:00
292 lines
9.9 KiB
Python
Executable File
292 lines
9.9 KiB
Python
Executable File
#!/usr/bin/env python2
|
|
|
|
# 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
|
|
#
|
|
|
|
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 [options] [patchset ...]
|
|
|
|
Applies all Wine-Staging patch sets, or each specified patch set plus its
|
|
dependencies, to a Wine tree.
|
|
|
|
Options:
|
|
-h, --help print this message
|
|
-v, --version print version information
|
|
-a, --all apply all patches (except those excluded with -W)
|
|
-W, --exclude <patchset> 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=<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`)
|
|
--ignore-missing automatically add dependencies, but do not fail
|
|
if they are missing or disabled
|
|
-r, --rebase-mode alias for --backend=git-am-C1 --no-autoconf --no-patchlist
|
|
-d, --destdir=<path> install to <path> (defaults to current working directory)
|
|
'''
|
|
|
|
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 f:
|
|
for l in f.readlines():
|
|
if l.lower().startswith('depends: '):
|
|
deps.append(l.split(' ')[1].strip())
|
|
elif l.lower().strip() == 'disabled: true':
|
|
return None
|
|
return deps
|
|
|
|
def apply_patch(patch):
|
|
if backend == 'git-am':
|
|
return run(['git','-C',winedir,'am',patch])
|
|
elif backend == 'git-am-C1':
|
|
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 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:]
|
|
if '[' in subject: subject = subject[subject.index(']') + 1:]
|
|
if author and subject: break
|
|
author = author.strip().strip('"').replace('\\','\\\\').replace('"','\\"')
|
|
subject = subject.strip().replace('\\','\\\\').replace('"','\\"')
|
|
patch_data += '+ {"%s", "%s", 1},\n' %(author, subject)
|
|
|
|
def apply_set(patchlist, name):
|
|
if name in applied:
|
|
return True
|
|
for dep in patchlist[name]:
|
|
if dep in patchlist and not apply_set(patchlist, dep):
|
|
return False
|
|
for patch in sorted(os.listdir(patchdir+'/'+name)):
|
|
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
|
|
|
|
def add_patchset(patchlist, name):
|
|
path = patchdir + '/' + name
|
|
if os.path.isdir(path):
|
|
deps = parse_def_file(name, path + '/definition')
|
|
if deps == None:
|
|
print 'Error: attempt to apply %s, but it is disabled.' %name
|
|
sys.exit(1)
|
|
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 <webmaster@fds-team.de>
|
|
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, force_autoconf, winedir
|
|
patchlist = {}
|
|
excluded = []
|
|
no_autoconf = False
|
|
no_patchlist = False
|
|
ignore_missing = False
|
|
|
|
try:
|
|
opts, args = getopt.gnu_getopt(sys.argv[1:], 'ad:hrvW:', \
|
|
['all',
|
|
'backend=',
|
|
'destdir=',
|
|
'force-autoconf',
|
|
'ignore-missing',
|
|
'help',
|
|
'no-autoconf',
|
|
'no-patchlist',
|
|
'version'])
|
|
except getopt.GetoptError as err:
|
|
print str(err)
|
|
sys.exit(2)
|
|
|
|
for o, a in opts:
|
|
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' or o == '--exclude':
|
|
excluded.append(a)
|
|
elif o == '-a' or o == '--all':
|
|
for name in os.listdir(patchdir):
|
|
path = patchdir + '/' + name
|
|
if os.path.isdir(path):
|
|
deps = parse_def_file(name, path + '/definition')
|
|
if deps != None: # it's not disabled
|
|
patchlist[name] = deps
|
|
elif o == '-d' or o == '--destdir':
|
|
winedir = a
|
|
elif o == '--backend':
|
|
backend = a
|
|
elif o == '--no-autoconf':
|
|
no_autoconf = True
|
|
elif o == '--force-autoconf':
|
|
force_autoconf = True
|
|
elif o == '--no-patchlist':
|
|
no_patchlist = True
|
|
elif o == '--ignore-missing':
|
|
ignore_missing = True
|
|
|
|
for a in args: add_patchset(patchlist, a)
|
|
|
|
for p in excluded: del patchlist[p]
|
|
|
|
if not os.access(winedir + '/tools/make_requests', os.F_OK):
|
|
print "Target directory '%s' does not point to a Wine tree." %winedir
|
|
sys.exit(1)
|
|
|
|
if not patchlist:
|
|
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
|
|
for p in patchlist:
|
|
deps = patchlist[p]
|
|
for d in deps:
|
|
if d not in patchlist:
|
|
if not ignore_missing:
|
|
print 'Error: unknown or disabled dependency %s of %s.' %(d,p)
|
|
sys.exit(1)
|
|
else:
|
|
print 'Warning: unknown or disabled dependency %s of %s.' %(d,p)
|
|
|
|
# Now try to apply each patch
|
|
for p in sorted(patchlist.keys()):
|
|
# Try to apply it
|
|
if not apply_set(patchlist, p):
|
|
sys.exit(1)
|
|
|
|
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__':
|
|
main()
|