On 05/17/2013 04:46 PM, Daniel P. Berrange wrote:
On Fri, May 17, 2013 at 04:37:13PM +0200, Florian Weimer wrote:
I'd like some scriptable way to unpack SRPMs up and including to the
%prep stage.  Ideally, the results would end up in a directory I
specify, and intermediate directories which only contain a single
subdirectory are skipped.  (The eventual goal is to run two such
directory tress through diff -uraN, and I want to avoid spurious
differences due to an update from foo-1.2 to foo-1.3.)

I've already learned the hard way that I need to use something like
mock because some %prep script assume that build requires are
installed.  But mock does not appear to support an option to stop
after the %prep stage.

I guess you were using the --rebuild arg to mock ?  Can you do
something like this where you invoke rpmbuild explicitly with
the args you need to just run upto %prep:

    mock --init
    mock --installdeps  /path/to/srpm
    mock --copyin /path/to/srpm /srpm
    mock --shell /usr/bin/rpmbuild -bp /srpm

I put the various suggestions in this thread together and the attached script appears to work. Admittedly, some parts of it are rather ugly.

--
Florian Weimer / Red Hat Product Security Team
#!/usr/bin/python

#  Copyright (C) 2013 Red Hat, Inc.
#  Written by Florian Weimer <fwei...@redhat.com>
# 
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
# 
#  This program 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 General Public License for more details.
# 
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.

# This script expects a mock configuration name and a path to a source
# RPM.  It unpacks the source RPM by executing the build process in a
# mock configuration up to and including the %prep stage.  The
# resulting directory tree is then copied into a subdirectory of the
# current directory, named after the source package NVR.

import base64
import getopt
import os.path
import rpm
import subprocess
import sys

MOCK = '/usr/bin/mock'
VERBOSE = False
SKIP_DIRECTORIES = True

def usage():
    sys.stderr.write("""usage: {0} [-Sv] [-d PATH] MOCK-CONFIG SRPM-FILE

Options:
  -S       do not strip leading directories which contain a single subdirectory
  -v       increase verbosity
  -d PATH  set the destination directory to PATH
""".format(sys.argv[0]))
    sys.exit(1)

def run_mock(config, *args):
    argv = [MOCK]
    if VERBOSE:
        argv += ["-v"]
    argv += ["-r", config]
    argv += args
    subprocess.call(argv)
    
def run_mock_shell(config, cmd):
    # Do not depend on multiple unquoting steps within mock.
    quoted = base64.b64encode(cmd)
    run_mock(config, "--shell", "echo {}|base64 -d|bash".format(quoted))

def read_rpm(rpmpath):
    ts = rpm.TransactionSet()
    ts.VSFlags = rpm._RPMVSF_NOSIGNATURES
    with file(rpmpath, "rb") as f:
        return ts.hdrFromFdno(f.fileno())

def skip_directories(target):
    # Prunes leading directories which contain only one subdirectory.
    while True:
        subdirs = os.listdir(target)
        if len(subdirs) == 1:
            tmp = target + ".tmp"
            os.rename(target, tmp)
            os.rename(os.path.join(tmp, subdirs[0]), target)
            os.rmdir(tmp)
        else:
            break

def err(fmt, *args):
    sys.stderr.write("error: " + fmt.format(*args) + "\n")
    sys.exit(1)

def doit(config, rpmpath, target):
    if not os.path.exists(rpmpath):
        err("source RPM file not found: {}", rpmpath)
    rpm_header = read_rpm(rpmpath)
    rpm_name = rpm_header[rpm.RPMTAG_NAME]
    nvr = "-".join((rpm_name,
                    rpm_header[rpm.RPMTAG_VERSION],
                    rpm_header[rpm.RPMTAG_RELEASE]))
    for bad in " /'\"":
        if bad in nvr:
            err("invalid character {!r} in NVR: {!r}", bad, nvr)
    if target is None:
        target = nvr
    if os.path.exists(target):
        err("destination directory already exists: {}", target)

    srpm_in_chroot = "/{}.src.rpm".format(nvr)
    dbdir_in_chroot = "/{}.src.db".format(nvr)
    builddir_in_chroot = "/builddir/build/BUILD"
    spec_in_chroot = "/builddir/build/SPECS/{}.spec".format(rpm_name)

    run_mock(config, "--init")
    run_mock(config, "--installdeps", rpmpath)
    run_mock(config, "--copyin", rpmpath, srpm_in_chroot)

    # Use separate RPM database because even inside the chroot, the
    # host RPM database format is used, which is generally
    # incompatible with the /usr/bin/rpm in the chroot.
    cmd = "rpm -i --dbpath {0} {1} && rpmbuild -bp --nodeps {2} ".format(
        dbdir_in_chroot, srpm_in_chroot, spec_in_chroot)
    if VERBOSE:
        cmd = "set -x; " + cmd
    run_mock_shell(config, cmd)

    run_mock(config, "--copyout", builddir_in_chroot, "./" + target)
    if SKIP_DIRECTORIES:
        skip_directories(target)

try:
    options, arguments = getopt.gnu_getopt(sys.argv[1:], "Sd:v")
except getopt.GetoptError:
    usage()
target = None
for opt, arg in options:
    if opt == "-S":
        SKIP_DIRECTORIES = False
    elif opt == "-d":
        target = arg
    elif opt == "-v":
        VERBOSE = True
    else:
        assert False
if len(arguments) != 2:
    usage()
doit(arguments[0], arguments[1], target)
-- 
devel mailing list
devel@lists.fedoraproject.org
https://admin.fedoraproject.org/mailman/listinfo/devel

Reply via email to