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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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

