package App::ArchLinux::PackagerTools::Pacman; use strictures; use autodie; use Function::Parameters; use Log::Any qw($log); use App::ArchLinux::PackagerTools::CPAN; =head1 NAME App::ArchLinux::PackagerTools::Pacman - Methods to interact with pacman =head1 SYNOPSIS use App::ArchLinux::PackagerTools::Pacman; # synopsis... =head1 DESCRIPTION This module allows to query pacman for a list of packages in the repository and extract a list of CPAN distributions that are part of the repository. =head1 METHODS =head2 Constructors =head3 new my $pacman = App::ArchLinux::PackagerTools::Pacman->new(); Returns a new instance. =cut method new($class: $deps = {}) { $deps->{cpan} //= App::ArchLinux::PackagerTools::CPAN->new(); $deps->{pkgname_cpan_name_map} //= { # pkgname => cpan_name 'perl-critic' => 'Perl::Critic', 'perl-libintl-perl' => 'libintl-perl', }; return $class->new_no_defaults($deps); } method new_no_defaults($class: $deps = {}) { my $self = {}; bless $self, $class; $self->{deps} = $deps; return $self; } =head2 Public Methods =head3 get_packages my $packages = $pacman->get_packages(); Returns an arrayref of package hashes. Each package hash contains at least the pkgname and pkgver keys. =cut method get_packages() { my @packages; open my $fh, '-|', 'expac -S \'%n %v\''; my %seen; while (<$fh>) { if (m/^(?[^\s]+) (?.*)$/) { push @packages, {%+}; } } return \@packages; } =head3 get_perl_packages my $packages = $pacman->get_perl_packages(); Similar to get_packages(), but returns a list filtered to perl packages. =cut method get_perl_packages() { my @packages = grep {$_->{pkgname} =~ /^perl-/} $self->get_packages->@*; $log->debugf("Found %d perl packages", scalar(@packages)); return \@packages; } =head3 get_perl_distributions my $dists = $pacman->get_perl_distributions(); Returns a list of CPAN distributions that are in the pacman repo. Returns an arrayref of hashrefs that contain dist_name and version keys. =cut method get_perl_distributions() { my @distributions; for my $package ($self->get_perl_packages()->@*) { my $cleaned_name = $package->{pkgname} =~ s/^perl-//r; my $dist; if (defined $self->{deps}->{pkgname_cpan_name_map}->{$package->{pkgname}}) { $dist = $self->{deps}->{cpan}->get_dist($self->{deps}->{pkgname_cpan_name_map}->{$package->{pkgname}}); } else { $dist = $self->{deps}->{cpan}->search_dist($cleaned_name); } push @distributions, $dist if defined $dist; $log->warningf("Failed to find distribution for package '%s'", $package) if !defined $dist; } return \@distributions; } =head3 get_package_version my $version = $pacman->get_package_version($pkgname); Return the version of a package in the pacman package repositories. =cut method get_package_version($pkgname) { # TODO do not fork expac for each package here! works for now, but it's sloooow my $version = `expac -S -1 '%v' -- '$pkgname'`; chomp $version; return $version; } =head3 get_packages_versions my $versions = $pacman->get_packages_versions($package_names); Return a hash that maps each package name to the version of that package in the pacman repository. =cut method get_packages_versions($package_names) { my %versions; open my $fh, '-|', qw(expac -S -1), '%n %v', '--', $package_names->@*; while (<$fh>) { my ($pkgname, $version) = split /\s/; $versions{$pkgname} = $version; $log->debugf("Package '%s' has version '%s' in repository", $pkgname, $version); } if ($package_names->@* != keys %versions) { croak $log->error("Failed to find version for all packages. Got %d packages and %d versions were detected", scalar($package_names->@*), scalar(keys %versions)); } return \%versions; } =head3 get_perl_pkgname my $pkgname = $pacman->get_perl_pkgname($dist_name); Return the pacman package name for a CPAN distribution. In most cases, this prefixes the distribution with "perl-" and replaces "::" with "-", but it may also return other names if they are mapped differently. =cut method get_perl_pkgname($dist_name) { # TODO implement mapping my $pkgname = lc($dist_name); $pkgname =~ s/::/-/g; $pkgname = "perl-$pkgname"; return $pkgname; } =head3 get_perl_distribution_version my $version = $pacman->get_perl_distribution_version($dist_name); Return the version of a CPAN distribution's pacman package. This removes pacman-specific information from the pacman's package version. =cut method get_perl_distribution_version($dist_name) { my $pkgname = $self->get_perl_pkgname($dist_name); my $version = $self->get_package_version($pkgname); $version = $self->_clean_version($version); $log->tracef("Found version '%s' for package '%s'", $version, $pkgname); return $version; } =head3 get_perl_distribution_versions my $distribution_names = ['App::ArchLinux::PackagerTools', 'DBI']; my $versions = $pacman->get_perl_distribution_versions($distribution_names); print $versions->{'DBI'}; # prints "1.642" Return a hash that maps a CPAN distribution name to the respective version of the CPAN distribution of the pacman repository package. =cut method get_perl_distribution_versions($distribution_names) { my %pkgname_dist; for my $dist_name ($distribution_names->@*) { $pkgname_dist{$self->get_perl_pkgname($dist_name)} = $dist_name; } my $versions = $self->get_packages_versions([keys %pkgname_dist]); my %dist_versions; for my $key (keys $versions->%*) { $dist_versions{$pkgname_dist{$key}} = $self->_clean_version($versions->{$key}); $log->debugf("Cleaned version of package '%s' is now '%s'", $key, $versions->{$key}); } return \%dist_versions; } =head3 _clean_version my $version = $self->_clean_version($version); Strip pacman-specific information (pkgrel, epoch) from a version string. =cut method _clean_version($version) { # remove pkgrel $version =~ s/-\d+(\.\d+)?$//; # remove epoch $version =~ s/^\d+://; return $version; } 1; __END__