]> git.pld-linux.org Git - packages/rpm-build-tools.git/blobdiff - rediff-patches.py
Fallback to /tmp.
[packages/rpm-build-tools.git] / rediff-patches.py
index 4b5bdaf2ff82d904c872fefad38a8f2723cbc591..d09c9ee93be8a4aac21e01088491f6acf1e118ee 100755 (executable)
@@ -2,12 +2,9 @@
 
 # rediff-patches.py name.spec
 
-# TODO:
-# - handle rediff of last patch (need some way to terminate build just after that patch is applied)
-# - or maybe apply patches on our own instead of using rpmbuild for that
-# - argparse fro arguments
-# - cleanup
-
+import argparse
+import collections
+import logging
 import os
 import re
 import rpm
@@ -18,61 +15,177 @@ import tempfile
 
 RPMBUILD_ISPATCH = (1<<1)
 
-def unpack(spec, builddir, until_patch=None):
-    cmd = [ 'rpmbuild', '-bp', '--define',  '_builddir %s' % builddir ]
-    if until_patch is not None:
-        cmd += [ '--define', '%%patch%d exit; #' % until_patch ]
-    cmd += [ spec ]
-    subprocess.check_call(cmd, stdout=sys.stderr, stderr=sys.stderr, timeout=600)
-
-def diff(diffdir_org, diffdir, builddir, output):
+def prepare_spec(r, patch_nr, before=False):
+    tempspec = tempfile.NamedTemporaryFile()
+    re_patch = re.compile(r'^%patch(?P<patch_number>\d+)\w*')
+    for line in r.parsed.split('\n'):
+        m = re_patch.match(line)
+        if m:
+            patch_number = int(m.group('patch_number'))
+            if patch_nr == patch_number:
+                if before:
+                    tempspec.write(b"exit 0\n# here was patch%d\n" % patch_nr)
+                else:
+                    line = re.sub(r'#.*', "", line)
+                    tempspec.write(b"%s\nexit 0\n" % line.encode('utf-8'))
+                continue
+        tempspec.write(b"%s\n" % line.encode('utf-8'))
+    tempspec.flush()
+    return tempspec
+
+def unpack(spec, appsourcedir, builddir):
+    cmd = [ 'rpmbuild', '-bp',
+           '--define',  '_builddir %s' % builddir,
+           '--define', '_specdir %s' % appsourcedir,
+           '--define', '_sourcedir %s' % appsourcedir,
+           '--define', '_enable_debug_packages 0',
+           '--define', '_default_patch_fuzz 2',
+           spec ]
+    logging.debug("running %s" % repr(cmd))
+    try:
+        res = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True,
+                              env={'LC_ALL': 'C.UTF-8'}, timeout=600)
+    except subprocess.CalledProcessError as err:
+        logging.error("unpacking exited with status code %d." % err.returncode)
+        logging.error("STDOUT:")
+        if err.stdout:
+            for line in err.stdout.decode('utf-8').split("\n"):
+                logging.error(line)
+        logging.error("STDERR:")
+        if err.stderr:
+            for line in err.stderr.decode('utf-8').split("\n"):
+                logging.error(line)
+        raise
+    else:
+        logging.debug("unpacking exited with status code %d." % res.returncode)
+        logging.debug("STDOUT/STDERR:")
+        if res.stdout:
+            for line in res.stdout.decode('utf-8').split("\n"):
+                logging.debug(line)
+
+
+def patch_comment_get(patch):
+    patch_comment = ""
+    patch_got = False
+    with open(patch, 'rt') as f:
+        for line in f:
+            if line.startswith('diff ') or line.startswith('--- '):
+                patch_got = True
+                break
+            patch_comment += line
+    return patch_comment if patch_got else ""
+
+def diff(diffdir_org, diffdir, builddir, patch_comment, output):
     diffdir_org = os.path.basename(diffdir_org)
     diffdir = os.path.basename(diffdir)
 
     with open(output, 'wt') as f:
+        if patch_comment:
+            f.write(patch_comment)
+            f.flush()
         cmd = [ 'diff', '-urNp', '-x', '*.orig', diffdir_org, diffdir ]
+        logging.debug("running %s" % repr(cmd))
         try:
-            subprocess.check_call(cmd, cwd=builddir, stdout=f, stderr=sys.stderr, timeout=600)
+            subprocess.check_call(cmd, cwd=builddir, stdout=f, stderr=sys.stderr,
+                                  env={'LC_ALL': 'C.UTF-8'}, timeout=600)
         except subprocess.CalledProcessError as err:
             if err.returncode != 1:
                 raise
-
-specfile = sys.argv[1]
-
-tempdir = tempfile.TemporaryDirectory(dir="/dev/shm")
-topdir = tempdir.name
-builddir = os.path.join(topdir, 'BUILD')
-
-rpm.addMacro("_builddir", builddir)
-
-r = rpm.spec(specfile)
-
-patches = {}
-
-for (name, nr, flags) in r.sources:
-    if flags & RPMBUILD_ISPATCH:
-        patches[nr] = name
-
-applied_patches = {}
-re_patch = re.compile(r'^%patch(?P<patch_number>\d+)\w*(?P<patch_args>.*)')
-for line in r.parsed.split('\n'):
-    m = re_patch.match(line)
-    if not m:
-        continue
-    patch_nr = int(m.group('patch_number'))
-    patch_args = m.group('patch_args')
-    applied_patches[patch_nr] = patch_args
-
-appsourcedir = rpm.expandMacro("%{_sourcedir}")
-appbuilddir = rpm.expandMacro("%{_builddir}/%{?buildsubdir}")
-
-for (patch_nr, patch_name) in sorted(patches.items()):
-    if patch_nr not in applied_patches:
-        continue
-    print("*** patch %d: %s" % (patch_nr, patch_name), file=sys.stderr)
-    unpack(specfile, builddir, patch_nr)
-    os.rename(appbuilddir, appbuilddir + ".org")
-    unpack(specfile, builddir, patch_nr + 1)
-    diff(appbuilddir + ".org", appbuilddir, builddir, os.path.join(topdir, os.path.join(appsourcedir, patch_name + ".rediff")))
-    shutil.rmtree(builddir)
-tempdir.cleanup()
+    logging.info("rediff generated as %s" % output)
+
+def diffstat(patch):
+    cmd = [ 'diffstat', patch ]
+    logging.info("running diffstat for: %s" % patch)
+    try:
+        subprocess.check_call(cmd, stdout=sys.stdout, stderr=sys.stderr,
+                              env={'LC_ALL': 'C.UTF-8'}, timeout=60)
+    except subprocess.CalledProcessError as err:
+        logging.error("running diffstat failed: %s" % err)
+    except FileNotFoundError as err:
+        logging.error("running diffstat failed: %s, install diffstat package?" % err)
+
+def main():
+    parser = parser = argparse.ArgumentParser(description='rediff patches to avoid fuzzy hunks')
+    parser.add_argument('spec', type=str, help='spec file name')
+    parser.add_argument('-p', '--patches', type=str, help='comma separated list of patch numbers to rediff')
+    parser.add_argument('-s', '--skip-patches', type=str, help='comma separated list of patch numbers to skip rediff')
+    parser.add_argument('-v', '--verbose', help='increase output verbosity', action='store_true')
+    args = parser.parse_args()
+
+    logging.basicConfig(level=logging.INFO)
+    rpm.setVerbosity(rpm.RPMLOG_ERR)
+
+    if args.verbose:
+            logging.basicConfig(level=logging.DEBUG)
+            rpm.setVerbosity(rpm.RPMLOG_DEBUG)
+
+    if args.patches:
+        args.patches = [int(x) for x in args.patches.split(',')]
+
+    if args.skip_patches:
+        args.skip_patches = [int(x) for x in args.skip_patches.split(',')]
+
+    specfile = args.spec
+    appsourcedir = os.path.dirname(os.path.abspath(specfile))
+
+    try:
+        tempdir = tempfile.TemporaryDirectory(dir="/dev/shm")
+    except FileNotFoundError as e:
+        tempdir = tempfile.TemporaryDirectory(dir="/tmp")
+    topdir = tempdir.name
+    builddir = os.path.join(topdir, 'BUILD')
+
+    rpm.addMacro("_builddir", builddir)
+
+    r = rpm.spec(specfile)
+
+    patches = {}
+
+    for (name, nr, flags) in r.sources:
+        if flags & RPMBUILD_ISPATCH:
+            patches[nr] = name
+
+    applied_patches = collections.OrderedDict()
+    re_patch = re.compile(r'^%patch(?P<patch_number>\d+)\w*(?P<patch_args>.*)')
+    for line in r.parsed.split('\n'):
+        m = re_patch.match(line)
+        if not m:
+            continue
+        patch_nr = int(m.group('patch_number'))
+        patch_args = m.group('patch_args')
+        applied_patches[patch_nr] = patch_args
+
+    appbuilddir = rpm.expandMacro("%{_builddir}/%{?buildsubdir}")
+
+    for patch_nr in applied_patches.keys():
+        if args.patches and patch_nr not in args.patches:
+            continue
+        if args.skip_patches and patch_nr in args.skip_patches:
+            continue
+        patch_name = patches[patch_nr]
+        logging.info("*** patch %d: %s" % (patch_nr, patch_name))
+
+        tempspec = prepare_spec(r, patch_nr, before=True)
+        unpack(tempspec.name, appsourcedir, builddir)
+        tempspec.close()
+        os.rename(appbuilddir, appbuilddir + ".org")
+
+        tempspec = prepare_spec(r, patch_nr, before=False)
+        unpack(tempspec.name, appsourcedir, builddir)
+        tempspec.close()
+
+        patch_comment = patch_comment_get(patch_name)
+        diff(appbuilddir + ".org",
+             appbuilddir,
+             builddir,
+             patch_comment,
+             os.path.join(topdir, os.path.join(appsourcedir, patch_name + ".rediff")))
+
+        diffstat(os.path.join(topdir, os.path.join(appsourcedir, patch_name)))
+        diffstat(os.path.join(topdir, os.path.join(appsourcedir, patch_name + ".rediff")))
+
+        shutil.rmtree(builddir)
+    tempdir.cleanup()
+
+if __name__ == '__main__':
+    main()
This page took 0.055433 seconds and 4 git commands to generate.