]> git.pld-linux.org Git - packages/rpm-build-tools.git/blob - rediff-patches.py
Generate diffstat for each patch, so it's easier to compare if rediff is not broken.
[packages/rpm-build-tools.git] / rediff-patches.py
1 #!/usr/bin/python3
2
3 # rediff-patches.py name.spec
4
5 import argparse
6 import collections
7 import logging
8 import os
9 import re
10 import rpm
11 import shutil
12 import subprocess
13 import sys
14 import tempfile
15
16 RPMBUILD_ISPATCH = (1<<1)
17
18 def prepare_spec(r, patch_nr, before=False):
19     tempspec = tempfile.NamedTemporaryFile()
20     re_patch = re.compile(r'^%patch(?P<patch_number>\d+)\w*')
21     for line in r.parsed.split('\n'):
22         m = re_patch.match(line)
23         if m:
24             patch_number = int(m.group('patch_number'))
25             if patch_nr == patch_number:
26                 if before:
27                     tempspec.write(b"exit 0\n# here was patch%d\n" % patch_nr)
28                 else:
29                     line = re.sub(r'#.*', "", line)
30                     tempspec.write(b"%s\nexit 0\n" % line.encode('utf-8'))
31                 continue
32         tempspec.write(b"%s\n" % line.encode('utf-8'))
33     tempspec.flush()
34     return tempspec
35
36 def unpack(spec, builddir):
37     cmd = [ 'rpmbuild', '-bp',
38            '--define',  '_builddir %s' % builddir,
39            '--define', '_enable_debug_packages 0',
40            '--define', '_default_patch_fuzz 2',
41            spec ]
42     logging.debug("running %s" % repr(cmd))
43     subprocess.check_call(cmd, stdout=sys.stderr, stderr=sys.stderr,
44                           env={'LC_ALL': 'C.UTF-8'}, timeout=600)
45
46 def diff(diffdir_org, diffdir, builddir, output):
47     diffdir_org = os.path.basename(diffdir_org)
48     diffdir = os.path.basename(diffdir)
49
50     with open(output, 'wt') as f:
51         cmd = [ 'diff', '-urNp', '-x', '*.orig', diffdir_org, diffdir ]
52         logging.debug("running %s" % repr(cmd))
53         try:
54             subprocess.check_call(cmd, cwd=builddir, stdout=f, stderr=sys.stderr,
55                                   env={'LC_ALL': 'C.UTF-8'}, timeout=600)
56         except subprocess.CalledProcessError as err:
57             if err.returncode != 1:
58                 raise
59     logging.info("rediff generated as %s" % output)
60
61 def diffstat(patch):
62     cmd = [ 'diffstat', patch ]
63     logging.info("running diffstat for: %s" % patch)
64     try:
65         subprocess.check_call(cmd, stdout=sys.stdout, stderr=sys.stderr,
66                               env={'LC_ALL': 'C.UTF-8'}, timeout=60)
67     except subprocess.CalledProcessError as err:
68         logging.error("running diffstat failed: %s" % err)
69     except FileNotFoundError as err:
70         logging.error("running diffstat failed: %s, install diffstat package?" % err)
71
72 def main():
73     parser = parser = argparse.ArgumentParser(description='rediff patches to avoid fuzzy hunks')
74     parser.add_argument('spec', type=str, help='spec file name')
75     parser.add_argument('-p', '--patches', type=str, help='comma separated list of patch numbers to rediff')
76     parser.add_argument('-v', '--verbose', help='increase output verbosity', action='store_true')
77     args = parser.parse_args()
78
79     logging.basicConfig(level=logging.INFO)
80     rpm.setVerbosity(rpm.RPMLOG_ERR)
81
82     if args.verbose:
83             logging.basicConfig(level=logging.DEBUG)
84             rpm.setVerbosity(rpm.RPMLOG_DEBUG)
85
86     if args.patches:
87         args.patches = [int(x) for x in args.patches.split(',')]
88
89     specfile = args.spec
90
91     tempdir = tempfile.TemporaryDirectory(dir="/dev/shm")
92     topdir = tempdir.name
93     builddir = os.path.join(topdir, 'BUILD')
94
95     rpm.addMacro("_builddir", builddir)
96
97     r = rpm.spec(specfile)
98
99     patches = {}
100
101     for (name, nr, flags) in r.sources:
102         if flags & RPMBUILD_ISPATCH:
103             patches[nr] = name
104
105     applied_patches = collections.OrderedDict()
106     re_patch = re.compile(r'^%patch(?P<patch_number>\d+)\w*(?P<patch_args>.*)')
107     for line in r.parsed.split('\n'):
108         m = re_patch.match(line)
109         if not m:
110             continue
111         patch_nr = int(m.group('patch_number'))
112         patch_args = m.group('patch_args')
113         applied_patches[patch_nr] = patch_args
114
115     appsourcedir = rpm.expandMacro("%{_sourcedir}")
116     appbuilddir = rpm.expandMacro("%{_builddir}/%{?buildsubdir}")
117
118     for patch_nr in applied_patches.keys():
119         if args.patches and patch_nr not in args.patches:
120             continue
121         patch_name = patches[patch_nr]
122         logging.info("*** patch %d: %s" % (patch_nr, patch_name))
123
124         tempspec = prepare_spec(r, patch_nr, before=True)
125         unpack(tempspec.name, builddir)
126         tempspec.close()
127         os.rename(appbuilddir, appbuilddir + ".org")
128
129         tempspec = prepare_spec(r, patch_nr, before=False)
130         unpack(tempspec.name, builddir)
131         tempspec.close()
132
133         diff(appbuilddir + ".org", appbuilddir, builddir, os.path.join(topdir, os.path.join(appsourcedir, patch_name + ".rediff")))
134
135         diffstat(os.path.join(topdir, os.path.join(appsourcedir, patch_name)))
136         diffstat(os.path.join(topdir, os.path.join(appsourcedir, patch_name + ".rediff")))
137
138         shutil.rmtree(builddir)
139     tempdir.cleanup()
140
141 if __name__ == '__main__':
142     main()
This page took 0.045997 seconds and 4 git commands to generate.