1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
|
package App::ArchLinux::PackagerTools::Pacman;
use strictures;
use autodie;
use Function::Parameters;
use Log::Any qw($log);
use App::ArchLinux::PackagerTools::CPAN;
# TODO: Split this class so that the external calls (to expac) are in a
# dedicated class and this class becomes testable. Also update
# t/PackagerTools.t accordingly afterwards.
=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: $context, $deps = {}) {
$deps->{config} //= App::ArchLinux::PackagerTools::Config->new($context);
$deps->{cpan} //= App::ArchLinux::PackagerTools::CPAN->new($context);
my $conf = $deps->{config}->get_config();
$deps->{pkgname_cpan_name_map} //= $conf->{pacman}->{pkgname_to_cpan_dist_map};
return $class->new_no_defaults($context, $deps);
}
method new_no_defaults($class: $context, $deps = {}) {
my $self = {};
bless $self, $class;
$deps->{cpan_name_pkgname_map} = {reverse $deps->{pkgname_cpan_name_map}->%*};
$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/^(?<pkgname>[^\s]+) (?<pkgver>.*)$/) {
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) {
if (defined $self->{deps}->{cpan_name_pkgname_map}->{$dist_name}) {
return $self->{deps}->{cpan_name_pkgname_map}->{$dist_name};
}
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__
|