]>
Commit | Line | Data |
---|---|---|
7eacbddd AM |
1 | #!/usr/bin/python3 |
2 | ||
3 | # rediff-patches.py name.spec | |
4 | ||
5 | # TODO: | |
6 | # - handle rediff of last patch (need some way to terminate build just after that patch is applied) | |
7 | # - or maybe apply patches on our own instead of using rpmbuild for that | |
7eacbddd AM |
8 | # - cleanup |
9 | ||
1e57e977 AM |
10 | import argparse |
11 | import collections | |
12 | import logging | |
7eacbddd AM |
13 | import os |
14 | import re | |
15 | import rpm | |
16 | import shutil | |
17 | import subprocess | |
18 | import sys | |
19 | import tempfile | |
20 | ||
21 | RPMBUILD_ISPATCH = (1<<1) | |
22 | ||
23 | def unpack(spec, builddir, until_patch=None): | |
2addb8ce | 24 | cmd = [ 'rpmbuild', '-bp', '--define', '_builddir %s' % builddir, '--define', '_default_patch_fuzz 2' ] |
7eacbddd AM |
25 | if until_patch is not None: |
26 | cmd += [ '--define', '%%patch%d exit; #' % until_patch ] | |
27 | cmd += [ spec ] | |
1e57e977 AM |
28 | logging.debug("running %s" % repr(cmd)) |
29 | subprocess.check_call(cmd, stdout=sys.stderr, stderr=sys.stderr, | |
30 | env={'LC_ALL': 'C.UTF-8'}, timeout=600) | |
7eacbddd AM |
31 | |
32 | def diff(diffdir_org, diffdir, builddir, output): | |
33 | diffdir_org = os.path.basename(diffdir_org) | |
34 | diffdir = os.path.basename(diffdir) | |
35 | ||
36 | with open(output, 'wt') as f: | |
37 | cmd = [ 'diff', '-urNp', '-x', '*.orig', diffdir_org, diffdir ] | |
1e57e977 | 38 | logging.debug("running %s" % repr(cmd)) |
7eacbddd | 39 | try: |
1e57e977 AM |
40 | subprocess.check_call(cmd, cwd=builddir, stdout=f, stderr=sys.stderr, |
41 | env={'LC_ALL': 'C.UTF-8'}, timeout=600) | |
7eacbddd AM |
42 | except subprocess.CalledProcessError as err: |
43 | if err.returncode != 1: | |
44 | raise | |
1e57e977 AM |
45 | logging.info("rediff generated as %s" % output) |
46 | ||
7eacbddd | 47 | |
c65b587f | 48 | def main(): |
1e57e977 AM |
49 | parser = parser = argparse.ArgumentParser(description='rediff patches to avoid fuzzy hunks') |
50 | parser.add_argument('spec', type=str, help='spec file name') | |
51 | parser.add_argument('-p', '--patches', type=str, help='comma separated list of patch numbers to rediff') | |
52 | parser.add_argument('-v', '--verbose', help='increase output verbosity', action='store_true') | |
53 | args = parser.parse_args() | |
54 | ||
55 | logging.basicConfig(level=logging.INFO) | |
56 | rpm.setVerbosity(rpm.RPMLOG_ERR) | |
57 | ||
58 | if args.verbose: | |
59 | logging.basicConfig(level=logging.DEBUG) | |
60 | rpm.setVerbosity(rpm.RPMLOG_DEBUG) | |
61 | ||
62 | if args.patches: | |
63 | args.patches = [int(x) for x in args.patches.split(',')] | |
64 | ||
65 | specfile = args.spec | |
c65b587f AM |
66 | |
67 | tempdir = tempfile.TemporaryDirectory(dir="/dev/shm") | |
68 | topdir = tempdir.name | |
69 | builddir = os.path.join(topdir, 'BUILD') | |
70 | ||
71 | rpm.addMacro("_builddir", builddir) | |
72 | ||
73 | r = rpm.spec(specfile) | |
74 | ||
75 | patches = {} | |
76 | ||
77 | for (name, nr, flags) in r.sources: | |
78 | if flags & RPMBUILD_ISPATCH: | |
79 | patches[nr] = name | |
80 | ||
1e57e977 | 81 | applied_patches = collections.OrderedDict() |
c65b587f AM |
82 | re_patch = re.compile(r'^%patch(?P<patch_number>\d+)\w*(?P<patch_args>.*)') |
83 | for line in r.parsed.split('\n'): | |
84 | m = re_patch.match(line) | |
85 | if not m: | |
86 | continue | |
87 | patch_nr = int(m.group('patch_number')) | |
88 | patch_args = m.group('patch_args') | |
89 | applied_patches[patch_nr] = patch_args | |
90 | ||
91 | appsourcedir = rpm.expandMacro("%{_sourcedir}") | |
92 | appbuilddir = rpm.expandMacro("%{_builddir}/%{?buildsubdir}") | |
93 | ||
1e57e977 AM |
94 | for patch_nr, patch_nr_next in zip(applied_patches.keys(), list(applied_patches.keys())[1:] + [None]): |
95 | if args.patches and patch_nr not in args.patches: | |
c65b587f | 96 | continue |
1e57e977 AM |
97 | if patch_nr_next is None: |
98 | logging.warning("can't rediff last patch, see TODO") | |
99 | break | |
100 | patch_name = patches[patch_nr] | |
101 | logging.info("*** patch %d: %s" % (patch_nr, patch_name)) | |
c65b587f AM |
102 | unpack(specfile, builddir, patch_nr) |
103 | os.rename(appbuilddir, appbuilddir + ".org") | |
1e57e977 | 104 | unpack(specfile, builddir, patch_nr_next) |
c65b587f AM |
105 | diff(appbuilddir + ".org", appbuilddir, builddir, os.path.join(topdir, os.path.join(appsourcedir, patch_name + ".rediff"))) |
106 | shutil.rmtree(builddir) | |
107 | tempdir.cleanup() | |
108 | ||
109 | if __name__ == '__main__': | |
110 | main() |