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

[lintian] 07/07: Add support for passing .buildinfo files to Lintian. (Closes: #853274)



This is an automated email from the git hooks/post-receive script.

lamby pushed a commit to branch master
in repository lintian.

commit 459ff1784653250fdbd1fd1802a0e9667bec68fd
Author: Chris Lamb <lamby@debian.org>
Date:   Mon Jan 29 10:08:04 2018 +0000

    Add support for passing .buildinfo files to Lintian. (Closes: #853274)
---
 README.md                          |   4 +-
 commands/lintian.pm                |   8 +-
 debian/changelog                   |   4 +
 lib/Lintian/Collect.pm             |   3 +
 lib/Lintian/Collect/Buildinfo.pm   | 189 +++++++++++++++++++++++++++++++++++++
 lib/Lintian/Lab.pm                 |   1 +
 lib/Lintian/Lab/Manifest.pm        |  12 +++
 lib/Lintian/Processable/Package.pm |   8 +-
 lib/Lintian/ProcessableGroup.pm    |  73 ++++++++------
 lib/Lintian/ProcessablePool.pm     |  28 ++++--
 t/scripts/Lintian/Lab/repair.t     |   2 +-
 11 files changed, 284 insertions(+), 48 deletions(-)

diff --git a/README.md b/README.md
index 2d6c7b8..7184412 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,8 @@ Lintian - Static Debian package analysis tool
 Lintian is a static analysis tool for finding many bugs, policy
 violations and other issues in Debian based packages.  It can process
 binary Debian packages (.deb), micro/installer packages (.udeb),
-Debian source packages (.dsc) and (to a limited degree) the "changes"
-files.
+Debian source packages (.dsc) and (to a limited degree) the "buildinfo"
+and "changes" files.
 
 
 Running Lintian
diff --git a/commands/lintian.pm b/commands/lintian.pm
index 0c80a6f..9f2a838 100755
--- a/commands/lintian.pm
+++ b/commands/lintian.pm
@@ -1508,17 +1508,15 @@ sub setup_work_pool {
     for my $arg (@ARGV) {
         # file?
         if (-f $arg) {
-            if ($arg =~ m/\.(?:u?deb|dsc|changes)$/o){
+            if ($arg =~ m/\.(?:u?deb|dsc|changes|buildinfo)$/o){
                 eval {$pool->add_file($arg);};
                 if ($@) {
                     print STDERR "Skipping $arg: $@";
                     $exit_code = 2;
                 }
             } else {
-                fatal_error(
-                    join(q{ },
-                        "bad package file name $arg",
-                        '(neither .deb, .udeb, .changes or .dsc file)'));
+                fatal_error("bad package file name $arg (neither .deb, "
+                      . '.udeb, .changes .dsc or .buildinfo file)');
             }
         } else {
             # parameter is a package name--so look it up
diff --git a/debian/changelog b/debian/changelog
index bb301ed..e9c825c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -43,6 +43,10 @@ lintian (2.5.73) UNRELEASED; urgency=medium
     + [CL] Detect "backports" (and "backport") as overly generic Python
       module names.  (Closes: #888559)
 
+  * lib/Lintian/*:
+    + [CL] Add support for passing .buildinfo files to Lintian.
+      (Closes: #853274)
+
  -- Chris Lamb <lamby@debian.org>  Fri, 26 Jan 2018 16:33:51 +1100
 
 lintian (2.5.72) unstable; urgency=medium
diff --git a/lib/Lintian/Collect.pm b/lib/Lintian/Collect.pm
index b1a4e30..60bc49d 100644
--- a/lib/Lintian/Collect.pm
+++ b/lib/Lintian/Collect.pm
@@ -87,6 +87,9 @@ sub new {
     } elsif ($type eq 'binary' or $type eq 'udeb') {
         require Lintian::Collect::Binary;
         $object = Lintian::Collect::Binary->new($pkg);
+    } elsif ($type eq 'buildinfo') {
+        require Lintian::Collect::Buildinfo;
+        $object = Lintian::Collect::Buildinfo->new($pkg);
     } elsif ($type eq 'changes') {
         require Lintian::Collect::Changes;
         $object = Lintian::Collect::Changes->new($pkg);
diff --git a/lib/Lintian/Collect/Buildinfo.pm b/lib/Lintian/Collect/Buildinfo.pm
new file mode 100644
index 0000000..6b2b399
--- /dev/null
+++ b/lib/Lintian/Collect/Buildinfo.pm
@@ -0,0 +1,189 @@
+# -*- perl -*-
+# Lintian::Collect::Buildinfo -- interface to .buildinfo file data collection
+
+# Copyright (C) 2018 Chris Lamb
+#
+# 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 2 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/>.
+
+package Lintian::Collect::Buildinfo;
+
+use strict;
+use warnings;
+use parent 'Lintian::Collect';
+
+use Lintian::Util qw(strip);
+
+=head1 NAME
+
+Lintian::Collect::Buildinfo - Lintian interface to .buildinfo file data collection
+
+=head1 SYNOPSIS
+
+    my ($name, $type) = ('foobar_1.2_i386.buildinfo', 'changes');
+    my $collect = Lintian::Collect->new($name, $type);
+    my $files = $collect->files;
+
+    foreach my $file (keys %{$files}) {
+        my $size = $files->{$file}{size};
+        print "File $file has size $size\n";
+    }
+
+=head1 DESCRIPTION
+
+Lintian::Collect::Buildinfo provides an interface to data for .buildinfo
+files.  It implements data collection methods specific to .buildinfo 
+files.
+
+=head1 CLASS METHODS
+
+=over 4
+
+=item new (PACKAGE)
+
+Creates a new Lintian::Collect::Buildinfo object.  Currently, PACKAGE is
+ignored.  Normally, this method should not be called directly, only via
+the L<Lintian::Collect> constructor.
+
+=cut
+
+# Initialize a new .buildinfo file collect object.  Takes the package name,
+# which is currently unused.
+sub new {
+    my ($class, $pkg) = @_;
+    my $self = {};
+    bless($self, $class);
+    return $self;
+}
+
+=back
+
+=head1 INSTANCE METHODS
+
+In addition to the instance methods listed below, all instance methods
+documented in the L<Lintian::Collect> module are also available.
+
+=over 4
+
+=item files
+
+Returns a reference to a hash containing information about files listed
+in the .buildinfo file.  Each hash may have the following keys:
+
+=over 4
+
+=item name
+
+Name of the file.
+
+=item size
+
+The size of the file in bytes.
+
+=item section
+
+The archive section to which the file belongs.
+
+=item priority
+
+The priority of the file.
+
+=item checksums
+
+A hash with the keys being checksum algorithms and the values themselves being
+hashes containing
+
+=over 4
+
+=item sum
+
+The result of applying the given algorithm to the file.
+
+=item filesize
+
+The size of the file as given in the .buildinfo section relating to the given
+checksum.
+
+=back
+
+=back
+
+Needs-Info requirements for using I<files>: L<Lintian::Collect/field ([FIELD[, DEFAULT]])>
+
+=cut
+
+sub files {
+    my ($self) = @_;
+
+    return $self->{files} if exists $self->{files};
+
+    my %files;
+
+    my $file_list = $self->field('files') || '';
+    local $_;
+    for (split /\n/, $file_list) {
+        strip;
+        next if $_ eq '';
+
+        my ($md5sum,$size,$section,$priority,$file) = split(/\s+/o, $_);
+        next if $file =~ m,/,;
+
+        $files{$file}{checksums}{md5} = {
+            'sum' => $md5sum,
+            'filesize' => $size,
+        };
+        $files{$file}{name} = $file;
+        $files{$file}{size} = $size;
+        $files{$file}{section} = $section;
+        $files{$file}{priority} = $priority;
+    }
+
+    foreach my $alg (qw(sha1 sha256)) {
+        my $list = $self->field("checksums-$alg") || '';
+        for (split /\n/, $list) {
+            strip;
+            next if $_ eq '';
+
+            my ($checksum, $size, $file) = split(/\s+/o, $_);
+            next if $file =~ m,/,;
+
+            $files{$file}{checksums}{$alg} = {
+                'sum' => $checksum,
+                'filesize' => $size
+            };
+        }
+    }
+
+    $self->{files} = \%files;
+    return $self->{files};
+}
+
+=back
+
+=head1 AUTHOR
+
+Originally written by Adam D. Barratt <adsb@debian.org> for Lintian.
+
+=head1 SEE ALSO
+
+lintian(1), L<Lintian::Collect>
+
+=cut
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Lab.pm b/lib/Lintian/Lab.pm
index d2a798e..0c3c120 100644
--- a/lib/Lintian/Lab.pm
+++ b/lib/Lintian/Lab.pm
@@ -44,6 +44,7 @@ use constant {
 # A private table of supported types.
 my %SUPPORTED_TYPES = (
     'binary'  => 1,
+    'buildinfo' => 1,
     'changes' => 1,
     'source'  => 1,
     'udeb'    => 1,
diff --git a/lib/Lintian/Lab/Manifest.pm b/lib/Lintian/Lab/Manifest.pm
index 4a44c67e..d01dbc1 100644
--- a/lib/Lintian/Lab/Manifest.pm
+++ b/lib/Lintian/Lab/Manifest.pm
@@ -77,6 +77,8 @@ use constant BINLIST_FORMAT =>
   q{Lintian's list of binary packages in the archive--V5};
 use constant SRCLIST_FORMAT =>
   q{Lintian's list of source packages in the archive--V5};
+use constant BLDLIST_FORMAT =>
+  q{Lintian's list of buildinfo packages in the archive--V1};
 use constant CHGLIST_FORMAT =>
   q{Lintian's list of changes packages in the archive--V1};
 
@@ -95,6 +97,9 @@ my @BIN_FILE_FIELDS = (
     'architecture','file','timestamp','area',
 );
 
+# buildinfo packages lists
+my @BLD_FILE_FIELDS = ('source','version','architecture','file','timestamp',);
+
 # changes packages lists
 my @CHG_FILE_FIELDS = ('source','version','architecture','file','timestamp',);
 
@@ -107,6 +112,8 @@ my @GROUP_QUERY = ('source','version','identifier',);
 
 my @BIN_QUERY = ('package','version','architecture',);
 
+my @BLD_QUERY = ('source','version','architecture',);
+
 my @CHG_QUERY = ('source','version','architecture',);
 
 my %TYPE2INFO = (
@@ -120,6 +127,11 @@ my %TYPE2INFO = (
         'file-header'  => BINLIST_FORMAT,
         'query-fields' => \@BIN_QUERY
     },
+    'buildinfo' => {
+        'file-fields'  => \@BLD_FILE_FIELDS,
+        'file-header'  => BLDLIST_FORMAT,
+        'query-fields' => \@BLD_QUERY
+    },
     'changes' => {
         'file-fields'  => \@CHG_FILE_FIELDS,
         'file-header'  => CHGLIST_FORMAT,
diff --git a/lib/Lintian/Processable/Package.pm b/lib/Lintian/Processable/Package.pm
index 6c9bd77..4dcb7ce 100644
--- a/lib/Lintian/Processable/Package.pm
+++ b/lib/Lintian/Processable/Package.pm
@@ -16,7 +16,7 @@
 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
 # MA 02110-1301, USA.
 
-## Represents something Lintian can process (e.g. a deb, dsc or a changes)
+## Represents something Lintian can process (e.g. a deb, dsc, buildinfo or a changes)
 package Lintian::Processable::Package;
 
 use parent qw(Lintian::Processable Class::Accessor::Fast);
@@ -144,13 +144,13 @@ sub new {
         $self->{pkg_src} = $pkg_name; # it is own source pkg
         $self->{pkg_src_version} = $pkg_version;
         $self->{'extra-fields'} = $dinfo;
-    } elsif ($pkg_type eq 'changes'){
+    } elsif ($pkg_type eq 'buildinfo' or $pkg_type eq 'changes'){
         my $cinfo = get_dsc_info($pkg_path)
-          or croak "$pkg_path is not a valid changes file";
+          or croak "$pkg_path is not a valid $pkg_type file";
         my $pkg_version = $cinfo->{version};
         my $pkg_name = $cinfo->{source}//'';
         unless ($pkg_name) {
-            $pkg_name = _derive_name($pkg_path, 'changes')
+            $pkg_name = _derive_name($pkg_path, $pkg_type)
               or croak "Cannot determine the name of $pkg_path";
         }
         $self->{pkg_name} = $pkg_name;
diff --git a/lib/Lintian/ProcessableGroup.pm b/lib/Lintian/ProcessableGroup.pm
index 1eab039..8732374 100644
--- a/lib/Lintian/ProcessableGroup.pm
+++ b/lib/Lintian/ProcessableGroup.pm
@@ -45,16 +45,17 @@ Lintian::ProcessableGroup -- A group of objects that Lintian can process
 
 Instances of this perl class are sets of
 L<processables|Lintian::Processable>.  It allows at most one source
-and one changes package per set, but multiple binary packages
+and one changes or buildinfo package per set, but multiple binary packages
 (provided that the binary is not already in the set).
 
 =head1 METHODS
 
 =over 4
 
-=item Lintian::ProcessableGroup->new ([LAB[, CHANGES]])
+=item Lintian::ProcessableGroup->new ([LAB[, FILE]])
 
-Creates a group and optionally add all processables from CHANGES.
+Creates a group and optionally add all processables from .changes
+or .buildinfo file FILE.
 
 If the LAB parameter is given, all processables added to this group
 will be stored as a L<lab entry|Lintian::Lab::Entry> from LAB.
@@ -62,11 +63,12 @@ will be stored as a L<lab entry|Lintian::Lab::Entry> from LAB.
 =cut
 
 sub new {
-    my ($class, $lab, $changes) = @_;
+    my ($class, $lab, $file) = @_;
     my $self = {'lab' => $lab,};
     bless $self, $class;
-    $self->_init_group_from_changes($changes)
-      if defined $changes;
+    if (defined $file and $file =~ m/\.(buildinfo|changes)$/) {
+        $self->_init_group_from_file($1, $file);
+    }
     return $self;
 }
 
@@ -81,42 +83,43 @@ sub _lab_proc {
 }
 
 # Internal initialization sub
-#  populates $self from a changes file.
-sub _init_group_from_changes {
-    my ($self, $changes) = @_;
-    my ($cinfo, $cdir);
-    internal_error("$changes does not exist") unless -e $changes;
-    $cinfo = get_dsc_info($changes)
-      or internal_error("$changes is not a valid changes file");
-    $self->add_new_processable($changes, 'changes');
-    $cdir = $changes;
-    if ($changes =~ m,^/+[^/]++$,o){
+#  populates $self from a buildinfo or changes file.
+sub _init_group_from_file {
+    my ($self, $type, $filename) = @_;
+    my ($info, $dir);
+    internal_error("$filename does not exist") unless -e $filename;
+    $info = get_dsc_info($filename)
+      or internal_error("$filename is not a valid $type file");
+    $self->add_new_processable($filename, $type);
+    $dir = $filename;
+    if ($filename =~ m,^/+[^/]++$,o){
         # it is "/files.changes?"
         #  - In case you were wondering, we were told not to ask :)
         #   See #624149
-        $cdir = '/';
+        $dir = '/';
     } else {
         # it is "<something>/files.changes"
-        $cdir =~ s,(.+)/[^/]+$,$1,;
+        $dir =~ s,(.+)/[^/]+$,$1,;
     }
-    for my $line (split(/\n/o, $cinfo->{'files'}//'')) {
+    my $key = $type eq 'buildinfo' ? 'checksums-sha256' : 'files';
+    for my $line (split(/\n/o, $info->{$key}//'')) {
         my ($file);
         next unless defined $line;
         strip($line);
         next if $line eq '';
         # Ignore files that may lead to path traversal issues.
 
-        # We do not need (in order) md5sum, size, section or priority
+        # We do not need (eg.) md5sum, size, section or priority
         # - just the file name please.
-        (undef, undef, undef, undef, $file) = split(/\s+/o, $line);
+        $file = (split(/\s+/o, $line))[-1];
 
         # If the field is malformed, $file may be undefined; we also
         # skip it, if it contains a "/" since that is most likely a
         # traversal attempt
         next if !$file || $file =~ m,/,o;
 
-        if (not -f "$cdir/$file") {
-            print STDERR "$cdir/$file does not exist, exiting\n";
+        if (not -f "$dir/$file") {
+            print STDERR "$dir/$file does not exist, exiting\n";
             exit 2;
         }
 
@@ -125,7 +128,7 @@ sub _init_group_from_changes {
             next;
         }
 
-        $self->add_new_processable("$cdir/$file");
+        $self->add_new_processable("$dir/$file");
 
     }
     return 1;
@@ -165,10 +168,10 @@ sub add_processable{
     my ($self, $processable) = @_;
     my $pkg_type = $processable->pkg_type;
 
-    if ($pkg_type eq 'changes'){
-        internal_error('Cannot add another changes file')
-          if (exists $self->{changes});
-        $self->{changes} = $self->_lab_proc($processable);
+    if ($pkg_type eq 'changes' or $pkg_type eq 'buildinfo'){
+        internal_error("Cannot add another $pkg_type file")
+          if (exists $self->{$pkg_type});
+        $self->{$pkg_type} = $self->_lab_proc($processable);
     } elsif ($pkg_type eq 'source'){
         internal_error('Cannot add another source package')
           if (exists $self->{source});
@@ -264,6 +267,20 @@ sub get_source_processable {
     return $self->{source};
 }
 
+=item $group->get_buildinfo_processable
+
+Returns the processable identified as the "buildinfo" processable (e.g.
+the buildinfo file).
+
+If $group does not have a buildinfo processable, this method returns C<undef>.
+
+=cut
+
+sub get_buildinfo_processable {
+    my ($self) = @_;
+    return $self->{buildinfo};
+}
+
 =item $group->get_changes_processable
 
 Returns the processable identified as the "changes" processable (e.g.
diff --git a/lib/Lintian/ProcessablePool.pm b/lib/Lintian/ProcessablePool.pm
index b11bf73..2917e62 100644
--- a/lib/Lintian/ProcessablePool.pm
+++ b/lib/Lintian/ProcessablePool.pm
@@ -42,6 +42,7 @@ Lintian::ProcessablePool -- Pool of processables
  $pool->add_file('foo.changes');
  $pool->add_file('bar.dsc');
  $pool->add_file('baz.deb');
+ $pool->add_file('qux.buildinfo');
  foreach my $gname ($pool->get_group_names){
     my $group = $pool->get_group($gname);
     process($gname, $group);
@@ -81,11 +82,12 @@ processables from the same source package (if any).
 
 sub add_file {
     my ($self, $file) = @_;
-    if ($file =~ m/\.changes$/o){
+    if ($file =~ m/\.(buildinfo|changes)$/o){
+        my $type = $1;
         croak "$file does not exist" unless -f $file;
         my $pkg_path = Cwd::abs_path($file);
         croak "Cannot resolve $file: $!" unless $pkg_path;
-        return $self->_add_changes_file($pkg_path);
+        return $self->_add_file($type, $pkg_path);
     }
 
     my $proc = Lintian::Processable::Package->new($file);
@@ -186,11 +188,16 @@ sub empty{
 
 #### Internal subs ####
 
-sub _add_changes_file{
-    my ($self, $pkg_path) = @_;
+sub _add_file{
+    my ($self, $type, $pkg_path) = @_;
     my $group = Lintian::ProcessableGroup->new($self->{'lab'}, $pkg_path);
-    my $cproc = $group->get_changes_processable;
-    my $gid = $self->_get_group_id($cproc);
+    my $proc;
+    if ($type eq 'buildinfo') {
+        $proc = $group->get_buildinfo_processable;
+    } elsif ($type eq 'changes') {
+        $proc = $group->get_changes_processable;
+    }
+    my $gid = $self->_get_group_id($proc);
     my $ogroup = $self->{groups}{$gid};
     if (defined($ogroup)){
         # Group already exists...
@@ -198,8 +205,13 @@ sub _add_changes_file{
         # Merge architectures/packages ...
         # Accept all new
 
-        if (!defined($ogroup->get_changes_processable)) {
-            $ogroup->add_processable($cproc);
+        if ($type eq 'buildinfo'
+            && !defined($ogroup->get_buildinfo_processable)) {
+            $ogroup->add_processable($proc);
+            $added = 1;
+        }elsif ($type eq 'changes'
+            && !defined($ogroup->get_changes_processable)) {
+            $ogroup->add_processable($proc);
             $added = 1;
         }
 
diff --git a/t/scripts/Lintian/Lab/repair.t b/t/scripts/Lintian/Lab/repair.t
index 777e7da..12cfc1b 100755
--- a/t/scripts/Lintian/Lab/repair.t
+++ b/t/scripts/Lintian/Lab/repair.t
@@ -60,7 +60,7 @@ sub do_tests {
 
     opendir(my $dirfd, "$DATADIR/changes");
     foreach my $pkgbase (readdir $dirfd) {
-        next unless $pkgbase =~ m/\.(?:changes|u?deb|dsc)$/;
+        next unless $pkgbase =~ m/\.(?:buildinfo|changes|u?deb|dsc)$/;
         my $path = "$DATADIR/changes/$pkgbase";
         my $proc = Lintian::Processable::Package->new($path);
         my $entry = $LAB_A->get_package($proc);

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/lintian/lintian.git


Reply to: