VERSION="v0.35"
VERSIONSTRING="\
Build package utility from PLD Linux Packages repository
-$VERSION (C) 1999-2016 Free Penguins".
+$VERSION (C) 1999-2020 Free Penguins".
-CLEAN_PATH="/bin:/usr/bin:/usr/sbin:/sbin:/usr/X11R6/bin"
+# Clean PATH without /usr/local or user paths
+CLEAN_PATH="/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin"
# required rpm-build-macros
RPM_MACROS_VER=1.534
"
}
+is_rpmorg() {
+ local v
+
+ v=$(LC_ALL=C LANG=C rpm --version 2>&1)
+ v=${v#RPM version } # rpm 4
+ v=${v#rpm \(RPM\) } # rpm 5
+
+ case "$v" in
+ 4.5|5.*)
+ return 1
+ ;;
+ 4.*)
+ return 0;
+ ;;
+ *)
+ echo "ERROR: unsupported RPM version $v" >&2
+ exit 1
+ esac
+}
+
# create tempfile. as secure as possible
tempfile() {
local prefix=builder.$PACKAGE_NAME${1:+.$1}
%_sourcedir ./
EOF
fi
- if rpm --version 2>&1 | grep -qE '5\.[0-9]+\.[0-9]+'; then
+ if ! is_rpmorg; then
+ local safe_macrofiles
safe_macrofiles=$(rpm $TARGET_SWITCH --showrc | awk -F: '/^macrofiles/ { gsub(/^macrofiles[ \t]+:/, "", $0); print $0 } ')
eval PATH=$CLEAN_PATH $RPMBUILD $TARGET_SWITCH --macros "$safe_macrofiles:$BUILDER_MACROS" $QUIET $RPMOPTS $RPMBUILDOPTS $BCOND $* 2>&1
else
local _rev=$(get_pkgrev "$CVSTAG")
echo "$_rev" | grep -q -E "^ERROR$" || CVSTAG="$_rev"
if git rev-parse --verify -q "$CVSTAG" >/dev/null; then
- git checkout "$CVSTAG" --
+ # checkout only if differs, so this will not trash git reflog
+ if [ $(git rev-parse "$CVSTAG") != $(git rev-parse HEAD) ]; then
+ git checkout "$CVSTAG" --
+ fi
elif git rev-parse --verify -q "refs/remotes/${REMOTE_PLD}/$CVSTAG"; then
git checkout -t "refs/remotes/${REMOTE_PLD}/$CVSTAG" > /dev/null
fi
${GETURI} "$target" "$url"
fi
+
if [ -s "$target" ]; then
cvsignore_df $target
else
without_*)
bcond=${opt#without_}
case "$BCOND" in
- *--without?${bcond}*)
+ *--without?${bcond}\ *|*--without?${bcond})
AVAIL_BCONDS_WITHOUT="$AVAIL_BCONDS_WITHOUT <$bcond>"
;;
*)
with_*)
bcond=${opt#with_}
case "$BCOND" in
- *--with?${bcond}*)
+ *--with?${bcond}\ *|*--with?${bcond})
AVAIL_BCONDS_WITH="$AVAIL_BCONDS_WITH <$bcond>"
;;
*)
# this requires following sudo rules:
# - poldek --noask --caplookup -ug
poldek_install() {
- LANG=C $POLDEK_CMD --noask --caplookup --uniq -ug "$@"
+ LC_ALL=C LANG=C $POLDEK_CMD --noask --caplookup --uniq -ug "$@"
}
# install packages
DEPS=$(cat)
fi
- LANG=C rpm -q --whatprovides $DEPS 2>/dev/null | awk '!/no package provides/ { print }'
+ LC_ALL=C LANG=C rpm -q --whatprovides $DEPS 2>/dev/null | awk '!/no package provides/ { print }'
}
# install deps via information from 'rpm-getdeps' or 'rpm --specsrpm'
--- /dev/null
+#!/usr/bin/python3
+
+# rediff-patches.py name.spec
+
+import argparse
+import collections
+import logging
+import os
+import re
+import rpm
+import shutil
+import subprocess
+import sys
+import tempfile
+
+RPMBUILD_ISPATCH = (1<<1)
+
+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 diff(diffdir_org, diffdir, builddir, output):
+ diffdir_org = os.path.basename(diffdir_org)
+ diffdir = os.path.basename(diffdir)
+
+ with open(output, 'wt') as f:
+ 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,
+ env={'LC_ALL': 'C.UTF-8'}, timeout=600)
+ except subprocess.CalledProcessError as err:
+ if err.returncode != 1:
+ raise
+ 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))
+
+ 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 = 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()
+
+ diff(appbuilddir + ".org", appbuilddir, builddir, 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()
--- /dev/null
+#!/usr/bin/python3
+
+"""
+This script tries to set ordering in which packages ought to be sent to builders.
+Input: file with names of packages (without the .spec suffix). One package name per line.
+Output: sorted packages on stdout.
+
+If the script goes in a infinite loop, that means there is a cycle or other bug.
+"""
+
+import os
+import re
+import sys
+
+BR_PATTERN = re.compile('BuildRequires:\s+(.*?)(\s|$)')
+PACKAGE_PATTERN_WITH_N = re.compile('%package\s+-n\s+(.*)')
+PACKAGE_PATTERN = re.compile('%package\s+(.*)')
+
+DIR = os.getenv("HOME") + '/rpm/packages'
+
+BUILD_REQUIRES = {}
+PACKAGES = {}
+SPECS = {}
+VISITED = {}
+
+
+def parse_spec(name):
+ global PACKAGES, SPECS, BUILD_REQUIRES, VISITED
+ res = []
+ try:
+ with open(os.path.join(DIR, name, name + '.spec'), 'r') as f:
+ for line in f:
+ br = BR_PATTERN.match(line)
+ if br:
+ p = br.group(1)
+ res.append(p)
+ if line.startswith('%package'):
+ pn = PACKAGE_PATTERN_WITH_N.match(line)
+ if pn:
+ package = pn.group(1)
+ PACKAGES[package] = name
+ else:
+ pn = PACKAGE_PATTERN.match(line)
+ if pn:
+ ext = pn.group(1)
+ if ext:
+ package = name + '-' + ext
+ PACKAGES[package] = name
+ BUILD_REQUIRES[name] = res[:]
+ PACKAGES[name] = name
+ SPECS[name] = True
+ VISITED[name] = False
+ except:
+ pass
+
+
+def print_spec(spec):
+ global PACKAGES, SPECS, BUILD_REQUIRES, VISITED
+
+ if not VISITED[spec]:
+ VISITED[spec] = True
+ for br in BUILD_REQUIRES[spec]:
+ name = PACKAGES.get(br, '')
+ if name in SPECS:
+ if not VISITED[name]:
+ print_spec(name)
+ print(spec)
+
+
+if __name__ == "__main__":
+ if len(sys.argv) < 2:
+ print("Usage: %s filename" % sys.argv[0])
+ sys.exit(1)
+ with open(sys.argv[1], 'r') as f:
+ for line in f:
+ spec = line.rstrip()
+ parse_spec(spec)
+
+ for spec in SPECS:
+ print_spec(spec)