Re: Noarch subpackages: Upcoming Feature Freeze

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Florian Festi wrote:
> 
> Please add the packages you changed or plan to change to
> https://fedoraproject.org/wiki/Features/NoarchSubpackages/PackagesChanged
> Put the later in parenthesis. That way it will be easier to justify a
> release note entry and to argue that the Feature is (at least a bit)
> finished.
> 

I explored what would be changed if we enabled a lenient-hash check on
these files I discovered these things:

1) Asking reviewers to check this is pretty hard.  I'll add the steps at
the end.
2) If reviewers are expected to do this, we need to get our user
interface changes to rpmdiff merged upstream.  rpmlint's rpmdiff can
only ignore timestamp.  Reviewers are going to want to have something
like lenient-hash as well.
3) devhelp documentation should be marked as %doc
4) It might be reasonable for --lenient-hash to not check
f.startswith('/usr/share')... I'm on the fence about this.
5) Most of these would have checked fine even if we used a full hash
check instead of lenient
6) I discoverd a package with architecture specific differences.
Luckily, it's just a problem in documentation.

Check results with:
./rpmdiff.mine -iS -iT --lenient-hash

Script attached.

Summary:
23 packages listed
14 actually built with noarch subpackages
1 false positive (dbus) (files should have been marked %doc)
1 actual problem detected (dbus)
13 problem free runs

So the net change would have been one package.  Not sure if the
maintainer would have caught the noarch vs arch specific difference as
it is an error within documentation and they might have just added %doc
without exploring further.

ConsoleKit: package has failed rebuild
dbus: package has false positives that would be fixed by marking devhelp
as %doc
  - the dbus documentation is not arch-independent:
    * /usr/share/devhelp/books/dbus/api/dbus-arch-deps_8h-source.html
    * /usr/share/devhelp/books/dbus/api/group__DBusTypes.html
em8300: no false positives
evolution: no false positives
evolution-data-server: No false positives
festival: package has failed rebuild
gdl: failed rebuild
gmt: No false positives
gnome-games: No false positives
gnome-session: Latest version reverts noarch subpackage
gtk2: No false positives
javasqlite: not built with noarch subpackage
kst: not rebuilt with noarch subpackage
libtheora: no false positives
libxcb: no false positives with --lenient-hash
  * There is a harmless difference in two png files used in documentation
ncl: no false positives
nted: failed rebuild
PackageKit: no false positives
paraview: no false positives
PolicyKit: no false positives with --lenient-hash
pygobject2: hasn't been rebuilt
pygtk2: no false positives
xemacs: hasn't been rebuilt

Steps a reviewer would have to take:
1) Submit SRPM as a scratch build in koji.
2) Go to the package page
3) Go to the build page for the scratch build.
4) Go to the task page for the build
5) For at least two dissimilar architectures, click on the task for that
arch.
6) For each of those tasks, download any noarch packages that were built
(minimum 2 clicks)
7) run /usr/bin/rpmdiff --ignore-times on each of those pairs of packages.
8) For each of the many differences, decide whether the problem is an
actual problem.

-Toshio
#!/usr/bin/python
#
# Copyright (C) 2006 Mandriva; 2009 Red Hat, Inc.
# Authors: Frederic Lepied, Florian Festi
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Library General Public License as published by
# the Free Software Foundation; version 2 only
#
# 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 Library General Public License for more details.
#
# You should have received a copy of the GNU Library General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

# This library and program is heavily based on rpmdiff from the rpmlint package
# It was modified to be used as standalone library for the Koji project.

import rpm
import os
import itertools

import sys, getopt


class Rpmdiff:

    # constants

    TAGS = ( rpm.RPMTAG_NAME, rpm.RPMTAG_SUMMARY,
             rpm.RPMTAG_DESCRIPTION, rpm.RPMTAG_GROUP,
             rpm.RPMTAG_LICENSE, rpm.RPMTAG_URL,
             rpm.RPMTAG_PREIN, rpm.RPMTAG_POSTIN,
             rpm.RPMTAG_PREUN, rpm.RPMTAG_POSTUN)

    PRCO = ( 'REQUIRES', 'PROVIDES', 'CONFLICTS', 'OBSOLETES')

    #{fname : (size, mode, mtime, flags, dev, inode,
    #          nlink, state, vflags, user, group, digest)}
    __FILEIDX = [ ['S', 0],
                  ['M', 1],
                  ['5', 11],
                  ['D', 4],
                  ['N', 6],
                  ['L', 7],
                  ['V', 8],
                  ['U', 9],
                  ['G', 10],
                  ['F', 3],
                  ['T', 2] ]

    try:
        if rpm.RPMSENSE_SCRIPT_PRE:
            PREREQ_FLAG=rpm.RPMSENSE_PREREQ|rpm.RPMSENSE_SCRIPT_PRE|\
                rpm.RPMSENSE_SCRIPT_POST|rpm.RPMSENSE_SCRIPT_PREUN|\
                rpm.RPMSENSE_SCRIPT_POSTUN
    except AttributeError:
        try:
            PREREQ_FLAG=rpm.RPMSENSE_PREREQ
        except:
            #(proyvind): This seems ugly, but then again so does
            #            this whole check as well.
            PREREQ_FLAG=False

    DEPFORMAT = '%-12s%s %s %s %s'
    FORMAT = '%-12s%s'

    ADDED   = 'added'
    REMOVED = 'removed'

    # code starts here

    def __init__(self, old, new, ignore=None, lenient_hash=False):
        self.result = []
        self.ignore = ignore
        if self.ignore is None:
            self.ignore = []

        FILEIDX = self.__FILEIDX
        for tag in self.ignore:
            for entry in FILEIDX:
                if tag == entry[0]:
                    entry[1] = None
                    break

        old = self.__load_pkg(old)
        new = self.__load_pkg(new)

        # Compare single tags
        for tag in self.TAGS:
            old_tag = old[tag]
            new_tag = new[tag]
            if old_tag != new_tag:
                tagname = rpm.tagnames[tag]
                if old_tag == None:
                    self.__add(self.FORMAT, (self.ADDED, tagname))
                elif new_tag == None:
                    self.__add(self.FORMAT, (self.REMOVED, tagname))
                else:
                    self.__add(self.FORMAT, ('S.5........', tagname))

        # compare Provides, Requires, ...
        for  tag in self.PRCO:
            self.__comparePRCOs(old, new, tag)

        # compare the files

        old_files_dict = self.__fileIteratorToDict(old.fiFromHeader())
        new_files_dict = self.__fileIteratorToDict(new.fiFromHeader())
        files = list(set(itertools.chain(old_files_dict.iterkeys(),
                                         new_files_dict.iterkeys())))
        files.sort()

        for f in files:
            diff = 0

            old_file = old_files_dict.get(f)
            new_file = new_files_dict.get(f)

            if not old_file:
                self.__add(self.FORMAT, (self.ADDED, f))
            elif not new_file:
                self.__add(self.FORMAT, (self.REMOVED, f))
            else:
                format = ''
                for entry in FILEIDX:
                    if entry[1] != None and \
                            old_file[entry[1]] != new_file[entry[1]]:
                        format = format + entry[0]
                        diff = 1
                    else:
                        format = format + '.'
                if lenient_hash:
                    # 11 => hash of file
                    # 3 => flags for file
                    if (old_file[11] != new_file[11] and not (
                            new_file[3] == rpm.RPMFILE_DOC or
                            f.endswith('.pyc') or
                            f.endswith('.pyo'))):
                        format = format[:2] + '5' + format[2:]
                        diff = 1
                    else:
                        format = format[:2] + '.' + format[2:]
                if diff:
                    self.__add(self.FORMAT, (format, f))

    # return a report of the differences
    def textdiff(self):
        return '\n'.join((format % data for format, data in self.result))

    # do the two rpms differ
    def differs(self):
        return bool(self.result)

    # add one differing item
    def __add(self, format, data):
        self.result.append((format, data))

    # load a package from a file or from the installed ones
    def __load_pkg(self, filename):
        ts = rpm.ts()
        f = os.open(filename, os.O_RDONLY)
        hdr = ts.hdrFromFdno(f)
        os.close(f)
        return hdr

    # output the right string according to RPMSENSE_* const
    def sense2str(self, sense):
        s = ""
        for tag, char in ((rpm.RPMSENSE_LESS, "<"),
                          (rpm.RPMSENSE_GREATER, ">"),
                          (rpm.RPMSENSE_EQUAL, "=")):
            if sense & tag:
                s += char
        return s

    # compare Provides, Requires, Conflicts, Obsoletes
    def __comparePRCOs(self, old, new, name):
        oldflags = old[name[:-1]+'FLAGS']
        newflags = new[name[:-1]+'FLAGS']
        # fix buggy rpm binding not returning list for single entries
        if not isinstance(oldflags, list): oldflags = [ oldflags ]
        if not isinstance(newflags, list): newflags = [ newflags ]

        o = zip(old[name], oldflags, old[name[:-1]+'VERSION'])
        n = zip(new[name], newflags, new[name[:-1]+'VERSION'])

        if name == 'PROVIDES': # filter our self provide
            oldNV = (old['name'], rpm.RPMSENSE_EQUAL,
                     "%s-%s" % (old['version'], old['release']))
            newNV = (new['name'], rpm.RPMSENSE_EQUAL,
                     "%s-%s" % (new['version'], new['release']))
            o = [entry for entry in o if entry != oldNV]
            n = [entry for entry in n if entry != newNV]

        for oldentry in o:
            if not oldentry in n:
                if name == 'REQUIRES' and oldentry[1] & self.PREREQ_FLAG:
                    tagname = 'PREREQ'
                else:
                    tagname = name
                self.__add(self.DEPFORMAT,
                           (self.REMOVED, tagname, oldentry[0],
                            self.sense2str(oldentry[1]), oldentry[2]))
        for newentry in n:
            if not newentry in o:
                if name == 'REQUIRES' and newentry[1] & self.PREREQ_FLAG:
                    tagname = 'PREREQ'
                else:
                    tagname = name
                self.__add(self.DEPFORMAT,
                           (self.ADDED, tagname, newentry[0],
                            self.sense2str(newentry[1]), newentry[2]))

    def __fileIteratorToDict(self, fi):
        result = {}
        for filedata in fi:
            result[filedata[0]] = filedata[1:]
        return result

def _usage(exit=1):
    print "Usage: %s [<options>] <old package> <new package>" % sys.argv[0]
    print "Options:"
    print "  -h, --help            Output this message and exit"
    print "  -i, --ignore          Tag to ignore when calculating differences"
    print "                          (may be used multiple times)"
    print "                        Valid values are: SM5DNLVUGFT"
    print "  --lenient-hash        Ignore hash only for certain files: %doc,"
    print "                          *.pyc, *.pyo"
    print "                          (this implies -i5)"
    sys.exit(exit)

def main():

    ignore_tags = []
    try:
        opts, args = getopt.getopt(sys.argv[1:], "hi:", ["help", "ignore=",
            "lenient-hash"])
    except getopt.GetoptError, e:
        print "Error: %s" % e
        _usage()

    lenient = False
    for option, argument in opts:
        if option in ("-h", "--help"):
            _usage(0)
        if option in ("-i", "--ignore"):
            ignore_tags.append(argument)
        elif option in ("--lenient-hash"):
            ignore_tags.append("5")
            lenient = True

    if len(args) != 2:
        _usage()

    d = Rpmdiff(args[0], args[1], ignore=ignore_tags, lenient_hash=lenient)
    print d.textdiff()
    sys.exit(int(d.differs()))

if __name__ == '__main__':
    main()

# rpmdiff ends here

Attachment: signature.asc
Description: OpenPGP digital signature

-- 
fedora-devel-list mailing list
fedora-devel-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/fedora-devel-list

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Fedora Announce]     [Fedora Kernel]     [Fedora Testing]     [Fedora Formulas]     [Fedora PHP Devel]     [Kernel Development]     [Fedora Legacy]     [Fedora Maintainers]     [Fedora Desktop]     [PAM]     [Red Hat Development]     [Gimp]     [Yosemite News]
  Powered by Linux