File Coverage

blib/lib/CPAN/Plugin/Sysdeps.pm
Criterion Covered Total %
statement 1352 1499 90.1
branch 169 306 55.2
condition 50 78 64.1
subroutine 384 392 97.9
pod 2 2 100.0
total 1957 2277 85.9


line stmt bran cond sub pod time code
1             package CPAN::Plugin::Sysdeps;
2              
3 8     8   1058329 use strict;
  8         17  
  8         339  
4 8     8   40 use warnings;
  8         18  
  8         735  
5              
6             our $VERSION = '0.80';
7              
8 8     8   53 use List::Util 'first';
  8         12  
  8         813  
9              
10             our $TRAVERSE_ONLY; # only for testing
11              
12 8     8   47 use constant SUPPORTED_NUMERICAL_OPS => ['<','<=','==','>','>='];
  8         37  
  8         1465  
13 8         13 use constant SUPPORTED_NUMERICAL_OPS_RX => do {
14 8         14 my $rx = '^(' . join('|', map { quotemeta } @{SUPPORTED_NUMERICAL_OPS()}) . ')$';
  40         110  
  8         22  
15 8         26861 qr{$rx};
16 8     8   52 };
  8         17  
17              
18             our @OS_RELEASE_PATH_CANDIDATES = ('/etc/os-release', '/usr/lib/os-release');
19              
20             sub new {
21 40     40 1 1726917 my($class, @args) = @_;
22              
23 40         89 my $installer;
24 40         80 my $batch = 0;
25 40         86 my $dryrun = 0;
26 40         94 my $debug = 0;
27 40         163 my @additional_mappings;
28             my @args_errors;
29 40         0 my $options;
30 40         117 for my $arg (@args) {
31 114 100       902 if (ref $arg eq 'HASH') {
    100          
    100          
    100          
    100          
    50          
    0          
32 15 50       36 if ($options) {
33 0         0 die "Cannot handle multiple option hashes";
34             } else {
35 15         37 $options = $arg;
36             }
37             } elsif ($arg =~ m{^(apt-get|aptitude|pkg|pkg_add|yum|dnf|chocolatey|homebrew)$}) { # XXX are there more package installers?
38 22         101 $installer = $1;
39             } elsif ($arg eq 'batch') {
40 30         71 $batch = 1;
41             } elsif ($arg eq 'interactive') {
42 1         3 $batch = 0;
43             } elsif ($arg eq 'dryrun') {
44 30         81 $dryrun = 1;
45             } elsif ($arg =~ m{^mapping=(.*)$}) {
46 16         64 push @additional_mappings, $1;
47             } elsif ($arg =~ m{^debug(?:=(\d+))?$}) {
48 0 0       0 $debug = defined $1 ? $1 : 1;
49             } else {
50 0         0 push @args_errors, $arg;
51             }
52             }
53 40 50       128 if (@args_errors) {
54 0 0       0 die 'Unrecognized ' . __PACKAGE__ . ' argument' . (@args_errors != 1 ? 's' : '') . ": @args_errors\n";
55             }
56              
57 40 50       151 if (exists $ENV{CPAN_PLUGIN_SYSDEPS_DEBUG}) {
58 0         0 $debug = $ENV{CPAN_PLUGIN_SYSDEPS_DEBUG};
59             }
60 40 50       125 if ($debug) {
61 0         0 require Data::Dumper; # we'll need it
62             }
63              
64 40   66     282 my $os = $options->{os} || $^O;
65 40         111 my $osvers = '';
66 40         79 my $linuxdistro = '';
67 40         85 my $linuxdistroversion = 0;
68 40         100 my $linuxdistrocodename = '';
69 40 100 33     188 if ($os eq 'linux') {
    50 33        
70 31         65 my $linux_info;
71             my $get_linux_info = sub {
72 75 100   75   328 return $linux_info if $linux_info;
73 25         80 return $linux_info = _detect_linux_distribution();
74 31         167 };
75 31 100       112 if (defined $options->{linuxdistro}) {
76 6         15 $linuxdistro = $options->{linuxdistro};
77             } else {
78 25         70 $linuxdistro = $get_linux_info->()->{linuxdistro};
79             }
80              
81 31 100       106 if (defined $options->{linuxdistroversion}) {
82 6         13 $linuxdistroversion = $options->{linuxdistroversion};
83             } else {
84 25         70 $linuxdistroversion = $get_linux_info->()->{linuxdistroversion}; # XXX make it a version object? or make sure it's just X.Y?
85             }
86              
87 31 100       89 if (defined $options->{linuxdistrocodename}) {
88 6         34 $linuxdistrocodename = $options->{linuxdistrocodename};
89             } else {
90 25         71 $linuxdistrocodename = $get_linux_info->()->{linuxdistrocodename};
91             }
92             } elsif (($os eq 'freebsd') || ($os eq 'openbsd') || ($os eq 'dragonfly')) {
93             # Note: don't use $Config{osvers}, as this is just the OS
94             # version of the system which built the current perl, not the
95             # actually running OS version.
96 9 50       29 if (defined $options->{osvers}) {
97 9         20 $osvers = $options->{osvers};
98             } else {
99 0         0 chomp($osvers = `/sbin/sysctl -n kern.osrelease`);
100             }
101             }
102              
103 40 100       153 if (!$installer) {
104 18 100 66     142 if ($os eq 'freebsd' || $os eq 'dragonfly') {
    50          
    50          
    50          
    0          
    0          
105 9         18 $installer = 'pkg';
106             } elsif ($os eq 'gnukfreebsd') {
107 0         0 $installer = 'apt-get';
108             } elsif ($os eq 'openbsd') {
109 0         0 $installer = 'pkg_add';
110             } elsif ($os eq 'linux') {
111 9 100       55 if (__PACKAGE__->_is_linux_debian_like($linuxdistro)) {
    50          
112 8         22 $installer = 'apt-get';
113             } elsif (__PACKAGE__->_is_linux_fedora_like($linuxdistro)) {
114 1 50       4 if (_detect_dnf()) {
115 0         0 $installer = 'dnf';
116             } else {
117 1         22143 $installer = 'yum';
118             }
119             } else {
120 0         0 die __PACKAGE__ . " has no support for linux distribution $linuxdistro $linuxdistroversion\n";
121             }
122             } elsif( $os eq 'MSWin32' ) {
123 0         0 $installer = 'chocolatey';
124             } elsif ($os eq 'darwin') {
125 0         0 $installer = 'homebrew';
126             } else {
127 0         0 die __PACKAGE__ . " has no support for operating system $os\n";
128             }
129             }
130              
131 40         82 my @mapping;
132 40         109 for my $mapping (@additional_mappings, 'CPAN::Plugin::Sysdeps::Mapping') {
133 56 100       1068 if (-r $mapping) {
134 16 50       732 open my $fh, '<', $mapping
135             or die "Can't load $mapping: $!";
136 16         84 local $/;
137 16         533 my $buf = <$fh>;
138 16         3491 push @mapping, eval $buf;
139 16 50       421 die "Error while loading $mapping: $@" if $@;
140             } else {
141 40 50       3378 eval "require $mapping"; die "Can't load $mapping: $@" if $@;
  40         224  
142 40         277 push @mapping, $mapping->mapping;
143             }
144             }
145              
146 40         730 my %config =
147             (
148             installer => $installer,
149             batch => $batch,
150             dryrun => $dryrun,
151             debug => $debug,
152             os => $os,
153             osvers => $osvers,
154             linuxdistro => $linuxdistro,
155             linuxdistroversion => $linuxdistroversion,
156             linuxdistrocodename => $linuxdistrocodename,
157             mapping => \@mapping,
158             );
159 40         320 my $self = bless \%config, $class;
160 40 50       96 if (eval { require Hash::Util; 1 }) {
  40         6221  
  40         33283  
161 40         187 Hash::Util::lock_keys($self);
162             }
163 40         1120 $self;
164             }
165              
166             # CPAN.pm plugin hook method
167             sub post_get {
168 15     15 1 1511 my($self, $dist) = @_;
169              
170 15         86 my @packages = $self->_map_cpandist($dist);
171 14 100       112 if (@packages) {
172 2         11 my @uninstalled_packages = $self->_filter_uninstalled_packages(@packages);
173 2 50       16 if (@uninstalled_packages) {
174 2         32 my @cmds = $self->_install_packages_commands(@uninstalled_packages);
175 2         10 for my $cmd (@cmds) {
176 2 50       41 if ($self->{dryrun}) {
177 2         354 warn "DRYRUN: @$cmd\n";
178             } else {
179 0         0 warn "INFO: run @$cmd...\n";
180              
181 0         0 system @$cmd;
182 0 0       0 if ($? != 0) {
183 0         0 die "@$cmd failed, stop installation";
184             }
185             }
186             }
187             }
188             }
189             }
190              
191             # Helpers/Internal functions/methods
192             sub _detect_linux_distribution {
193 26     26   1009 my $info = _detect_linux_distribution_os_release();
194 26 50       178 return $info if $info;
195 0 0       0 if (-x '/usr/bin/lsb_release') {
196 0         0 _detect_linux_distribution_lsb_release();
197             } else {
198 0         0 _detect_linux_distribution_fallback();
199             }
200             }
201              
202             sub _detect_linux_distribution_os_release {
203 39     39   238481 my %info;
204 39         134 for my $candidate_file (@OS_RELEASE_PATH_CANDIDATES) {
205 39 100       2691 if (open my $fh, '<', $candidate_file) {
206 38         146 my %c;
207 38         2209 while(<$fh>) {
208 452 100       3190 if (my($k,$v) = $_ =~ m{^(.*?)=["']?(.*?)["']?$}) {
209 444         2016 $c{$k} = $v;
210             }
211             }
212 38         165 $info{linuxdistro} = $c{ID};
213 38         115 $info{linuxdistroversion} = $c{VERSION_ID};
214 38         94 $info{linuxdistrocodename} = $c{VERSION_CODENAME};
215             # heuristics
216 38 100 100     217 if (!defined $info{linuxdistrocodename} && $info{linuxdistro} eq 'debian' && $c{VERSION} =~ m{^\d+\s+\((.+)\)$}) {
      66        
217 2         7 $info{linuxdistrocodename} = $1;
218             }
219 38         801 return \%info;
220             }
221             }
222 1         6 undef;
223             }
224              
225             sub _detect_linux_distribution_lsb_release {
226 0     0   0 my %info;
227 0         0 my @cmd = ('lsb_release', '-irc');
228 0 0       0 open my $fh, '-|', @cmd
229             or die "Error while running '@cmd': $!";
230 0         0 while(<$fh>) {
231 0         0 chomp;
232 0 0       0 if (m{^Distributor ID:\s+(.*)}) {
    0          
    0          
233 0         0 $info{linuxdistro} = lc $1;
234             } elsif (m{^Release:\s+(.*)}) {
235 0         0 $info{linuxdistroversion} = $1;
236             } elsif (m{^Codename:\s+(.*)}) {
237 0         0 $info{linuxdistrocodename} = $1;
238             } else {
239 0         0 warn "WARNING: unexpected '@cmd' output '$_'";
240             }
241             }
242 0 0       0 close $fh
243             or die "Error while running '@cmd': $!";
244 0         0 \%info;
245             }
246              
247             sub _detect_linux_distribution_fallback {
248 0 0   0   0 if (open my $fh, '<', '/etc/redhat-release') {
249 0         0 my $contents = <$fh>;
250 0 0       0 if ($contents =~ m{^(CentOS|Rocky|RedHat|Fedora) (?:Linux )?release (\d+)\S*( \((.*?)\))?}) {
251 0 0       0 return {linuxdistro => lc $1, linuxdistroversion => $2, linuxdistrocodename => defined $3 ? $3 : ''};
252             }
253             }
254 0 0       0 if (open my $fh, '<', '/etc/issue') {
255 0         0 chomp(my $line = <$fh>);
256 0 0       0 if ($line =~ m{^Linux Mint (\d+) (\S+)}) {
    0          
    0          
257 0         0 return {linuxdistro => lc('LinuxMint'), linuxdistroversion => $1, linuxdistrocodename => $2};
258             } elsif ($line =~ m{^(Debian) GNU/Linux (\d+)}) {
259 0         0 my %info = (linuxdistro => lc $1, linuxdistroversion => $2);
260             $info{linuxdistrocodename} =
261             {
262             6 => 'squeeze',
263             7 => 'wheezy',
264             8 => 'jessie',
265             9 => 'stretch',
266             10 => 'buster',
267             11 => 'bullseye',
268             12 => 'bookworm',
269             13 => 'trixie',
270 0         0 }->{$info{linuxdistroversion}};
271 0         0 return \%info;
272             } elsif ($line =~ m{^(Ubuntu) (\d+\.\d+)}) {
273 0         0 my %info = (linuxdistro => lc $1, linuxdistroversion => $2);
274             $info{linuxdistrocodename} =
275             {
276             '12.04' => 'precise',
277             '14.04' => 'trusty',
278             '16.04' => 'xenial',
279             '18.04' => 'bionic',
280             '20.04' => 'focal',
281             '22.04' => 'jammy',
282             '24.04' => 'noble',
283 0         0 }->{$info{linuxdistroversion}};
284 0         0 return \%info;
285             } else {
286 0         0 warn "WARNING: don't know how to handle '$line'";
287             }
288             } else {
289 0         0 warn "WARNING: no /etc/issue available";
290             }
291 0         0 return {};
292             }
293              
294             sub _is_linux_debian_like {
295 4258     4258   7960 my(undef, $linuxdistro) = @_;
296 4258         20602 $linuxdistro =~ m{^(debian|ubuntu|linuxmint)$};
297             }
298              
299             sub _is_linux_fedora_like {
300 2641     2641   5027 my(undef, $linuxdistro) = @_;
301 2641         12942 $linuxdistro =~ m{^(fedora|redhat|centos|rocky)$};
302             }
303              
304 15     15   169 sub _is_apt_installer { shift->{installer} =~m{^(apt-get|aptitude)$} }
305              
306             # Run a process in an elevated window, wait for its exit
307             sub _win32_run_elevated {
308 0     0   0 my($exe, @args) = @_;
309            
310 0 0       0 my $args = join " ", map { if(/[ "]/) { s!"!\\"!g; qq{"$_"} } else { $_ }} @args;
  0         0  
  0         0  
  0         0  
  0         0  
311              
312 0         0 my $ps1 = sprintf q{powershell -NonInteractive -NoProfile -Command "$process = Start-Process '%s' -PassThru -ErrorAction Stop -ArgumentList '%s' -Verb RunAs -Wait; Exit $process.ExitCode"},
313             $exe, $args;
314              
315 0         0 $ps1;
316             }
317              
318             sub _debug {
319 92273     92273   115875 my $self = shift;
320 92273 50       167422 if ($self->{debug}) {
321 0         0 print STDERR 'DEBUG: ';
322             print STDERR join('', map {
323 0 0       0 if (ref $_) {
  0         0  
324 0         0 Data::Dumper->new([$_])->Terse(1)->Indent(0)->Dump;
325             } else {
326 0         0 $_;
327             }
328             } @_);
329 0         0 print STDERR "\n";
330             }
331             }
332              
333             sub _map_cpandist {
334 29     29   85 my($self, $dist) = @_;
335              
336             # compat for older CPAN.pm (1.76)
337 29 50       277 if (!$dist->can('base_id')) {
338 8     8   82 no warnings 'once';
  8         17  
  8         2176  
339             *CPAN::Distribution::base_id = sub {
340 0     0   0 my $self = shift;
341 0         0 my $id = $self->id();
342 0         0 my $base_id = File::Basename::basename($id);
343 0         0 $base_id =~ s{\.(?:tar\.(bz2|gz|Z)|t(?:gz|bz)|zip)$}{}i;
344 0         0 return $base_id;
345 0         0 };
346             }
347              
348             # smartmatch for regexp/string/array without ~~, 5.8.x compat!
349             # also add support for numerical comparisons
350             my $smartmatch = sub ($$) {
351 15047     15047   30434 my($left, $right) = @_;
352 8     8   61 no warnings 'uninitialized';
  8         1808  
  8         29191  
353 15047 100       34534 if (ref $right eq 'Regexp') {
    100          
    100          
354 174 100       2366 return 1 if $left =~ $right;
355             } elsif (ref $right eq 'ARRAY') {
356 1915 50 66     10943 return 1 if first { (!defined $left && !defined $_) || ($_ eq $left) } @$right;
  5903 100       27924  
357             } elsif (ref $right eq 'HASH') {
358 389         1385 for my $op (keys %$right) {
359 400 50       2111 if ($op !~ SUPPORTED_NUMERICAL_OPS_RX) {
360 0         0 die "Unsupported operator '$op', only supported: @{SUPPORTED_NUMERICAL_OPS()}";
  0         0  
361             }
362 400         916 my $val = $right->{$op};
363 400         787 my $code = 'no warnings q(numeric); $left '.$op.' $val';
364 400     3   51909 my $res = eval $code;
  3     3   25  
  3     3   6  
  3     3   162  
  3     3   59  
  3     3   8  
  3     3   106  
  3     2   23  
  3     2   7  
  3     2   108  
  3     2   25  
  3     2   7  
  3     2   142  
  3     2   23  
  3     2   8  
  3     2   113  
  3     2   23  
  3     2   8  
  3     2   118  
  3     2   21  
  3     2   7  
  3     2   102  
  2     2   15  
  2     2   4  
  2     2   90  
  2     2   20  
  2     2   6  
  2     2   89  
  2     2   19  
  2     2   6  
  2     2   83  
  2     2   18  
  2     2   6  
  2     1   88  
  2     1   16  
  2     1   5  
  2     1   75  
  2     1   14  
  2     1   5  
  2     1   70  
  2     1   15  
  2     1   5  
  2     1   67  
  2     1   15  
  2     1   4  
  2     1   70  
  2     1   16  
  2     1   4  
  2     1   76  
  2     1   16  
  2     1   4  
  2     1   70  
  2     1   29  
  2     1   5  
  2     1   69  
  2     1   12  
  2     1   6  
  2     1   61  
  2     1   14  
  2     1   5  
  2     1   63  
  2     1   13  
  2     1   6  
  2     1   92  
  2     1   15  
  2     1   61  
  2     1   104  
  2     1   32  
  2     1   4  
  2     1   69  
  2     1   13  
  2     1   5  
  2     1   67  
  2     1   14  
  2     1   5  
  2     1   90  
  2     1   17  
  2     1   4  
  2     1   84  
  2     1   29  
  2     1   5  
  2     1   67  
  2     1   14  
  2     1   5  
  2     1   72  
  2     1   15  
  2     1   4  
  2     1   76  
  2     1   15  
  2     1   4  
  2     1   68  
  2     1   47  
  2     1   61  
  2     1   76  
  2     1   13  
  2     1   15  
  2     1   73  
  2     1   17  
  2     1   6  
  2     1   99  
  1     1   6  
  1     1   3  
  1     1   33  
  1     1   8  
  1     1   2  
  1     1   38  
  1     1   8  
  1     1   2  
  1     1   34  
  1     1   8  
  1     1   2  
  1     1   36  
  1     1   7  
  1     1   3  
  1     1   37  
  1     1   7  
  1     1   3  
  1     1   36  
  1     1   6  
  1     1   2  
  1     1   29  
  1     1   7  
  1     1   3  
  1     1   36  
  1     1   7  
  1     1   3  
  1     1   33  
  1     1   8  
  1     1   2  
  1     1   34  
  1     1   8  
  1     1   2  
  1     1   62  
  1     1   7  
  1     1   3  
  1     1   70  
  1     1   7  
  1     1   3  
  1     1   35  
  1     1   7  
  1     1   3  
  1     1   53  
  1     1   7  
  1     1   2  
  1     1   33  
  1     1   7  
  1     1   3  
  1     1   70  
  1     1   8  
  1     1   2  
  1     1   34  
  1     1   6  
  1     1   3  
  1     1   32  
  1     1   6  
  1     1   3  
  1     1   32  
  1     1   7  
  1     1   3  
  1     1   31  
  1     1   8  
  1     1   2  
  1     1   60  
  1     1   7  
  1     1   2  
  1     1   34  
  1     1   10  
  1     1   4  
  1     1   42  
  1     1   9  
  1     1   2  
  1     1   40  
  1     1   10  
  1     1   4  
  1     1   50  
  1     1   9  
  1     1   3  
  1     1   40  
  1     1   9  
  1     1   3  
  1     1   40  
  1     1   10  
  1     1   3  
  1     1   45  
  1     1   9  
  1     1   3  
  1     1   38  
  1     1   7  
  1     1   2  
  1     1   34  
  1     1   7  
  1     1   3  
  1     1   36  
  1     1   10  
  1     1   3  
  1     1   39  
  1     1   8  
  1     1   2  
  1     1   49  
  1     1   8  
  1     1   3  
  1     1   36  
  1     1   8  
  1     1   3  
  1     1   40  
  1     1   10  
  1     1   3  
  1     1   39  
  1     1   8  
  1     1   3  
  1     1   40  
  1     1   10  
  1     1   3  
  1     1   42  
  1     1   9  
  1     1   3  
  1     1   38  
  1     1   8  
  1     1   3  
  1     1   57  
  1     1   10  
  1     1   2  
  1     1   38  
  1     1   8  
  1     1   2  
  1     1   37  
  1     1   9  
  1     1   3  
  1     1   38  
  1     1   42  
  1     1   4  
  1     1   41  
  1     1   10  
  1     1   3  
  1     1   39  
  1     1   13  
  1     1   3  
  1     1   39  
  1     1   10  
  1     1   3  
  1     1   44  
  1     1   9  
  1     1   3  
  1     1   39  
  1     1   10  
  1     1   3  
  1     1   40  
  1     1   8  
  1     1   3  
  1     1   40  
  1     1   8  
  1     1   3  
  1     1   37  
  1     1   8  
  1     1   3  
  1     1   37  
  1     1   8  
  1     1   3  
  1     1   38  
  1     1   8  
  1     1   2  
  1     1   36  
  1     1   9  
  1     1   3  
  1     1   57  
  1     1   9  
  1     1   3  
  1     1   37  
  1     1   13  
  1     1   5  
  1     1   168  
  1     1   10  
  1     1   37  
  1     1   35  
  1     1   7  
  1     1   2  
  1     1   37  
  1     1   12  
  1     1   2  
  1     1   58  
  1     1   6  
  1     1   2  
  1     1   27  
  1     1   6  
  1     1   2  
  1     1   25  
  1     1   6  
  1     1   2  
  1     1   30  
  1     1   9  
  1     1   3  
  1     1   40  
  1     1   10  
  1     1   4  
  1     1   81  
  1     1   6  
  1     1   2  
  1     1   29  
  1     1   12  
  1     1   2  
  1     1   54  
  1     1   6  
  1     1   2  
  1     1   27  
  1     1   6  
  1     1   2  
  1     1   28  
  1     1   7  
  1     1   2  
  1     1   27  
  1     1   5  
  1     1   1  
  1     1   32  
  1     1   79  
  1     1   3  
  1     1   22  
  1     1   5  
  1     1   2  
  1     1   41  
  1     1   5  
  1     1   2  
  1     1   25  
  1     1   5  
  1     1   2  
  1     1   25  
  1     1   9  
  1     1   3  
  1     1   33  
  1     1   6  
  1     1   2  
  1     1   46  
  1     1   6  
  1     1   1  
  1     1   25  
  1     1   4  
  1     1   2  
  1     1   19  
  1     1   5  
  1     1   2  
  1     1   26  
  1     1   5  
  1     1   3  
  1     1   25  
  1     1   78  
  1     1   3  
  1     1   29  
  1     1   5  
  1     1   2  
  1     1   26  
  1     1   25  
  1     1   1  
  1     1   28  
  1     1   4  
  1     1   2  
  1     1   43  
  1     1   5  
  1     1   1  
  1     1   27  
  1     1   6  
  1     1   2  
  1         46  
  1         7  
  1         3  
  1         36  
  1         5  
  1         2  
  1         27  
  1         13  
  1         3  
  1         59  
  1         10  
  1         2  
  1         40  
  1         10  
  1         3  
  1         55  
  1         10  
  1         2  
  1         47  
  1         10  
  1         3  
  1         40  
  1         9  
  1         4  
  1         41  
  1         9  
  1         3  
  1         38  
  1         9  
  1         3  
  1         39  
  1         8  
  1         2  
  1         63  
  1         8  
  1         2  
  1         35  
  1         13  
  1         2  
  1         65  
  1         7  
  1         1  
  1         27  
  1         12  
  1         2  
  1         56  
  1         9  
  1         3  
  1         50  
  1         6  
  1         20  
  1         42  
  1         5  
  1         3  
  1         24  
  1         9  
  1         2  
  1         43  
  1         5  
  1         3  
  1         90  
  1         6  
  1         2  
  1         29  
  1         7  
  1         2  
  1         42  
  1         5  
  1         3  
  1         26  
  1         5  
  1         2  
  1         29  
  1         7  
  1         2  
  1         30  
  1         7  
  1         2  
  1         28  
  1         8  
  1         2  
  1         31  
  1         7  
  1         2  
  1         29  
  1         4  
  1         2  
  1         24  
  1         74  
  1         2  
  1         29  
  1         8  
  1         1  
  1         34  
  1         6  
  1         1  
  1         46  
  1         7  
  1         2  
  1         37  
  1         9  
  1         4  
  1         34  
  1         11  
  1         2  
  1         100  
  1         5  
  1         3  
  1         23  
  1         7  
  1         2  
  1         31  
  1         11  
  1         2  
  1         59  
  1         9  
  1         2  
  1         39  
  1         5  
  1         3  
  1         26  
  1         6  
  1         1  
  1         29  
  1         7  
  1         1  
  1         35  
  1         8  
  1         2  
  1         57  
  1         5  
  1         2  
  1         26  
  1         13  
  1         2  
  1         79  
  1         8  
  1         2  
  1         27  
  1         12  
  1         1  
  1         64  
  1         17  
  1         2  
  1         77  
  1         10  
  1         13  
  1         50  
  1         4  
  1         2  
  1         23  
  1         18  
  1         4  
  1         118  
  1         7  
  1         2  
  1         28  
  1         5  
  1         2  
  1         27  
  1         11  
  1         3  
  1         58  
  1         7  
  1         2  
  1         25  
  1         17  
  1         3  
  1         76  
  1         7  
  1         3  
  1         32  
  1         14  
  1         3  
  1         58  
  1         9  
  1         2  
  1         58  
  1         10  
  1         2  
  1         38  
  1         6  
  1         2  
  1         30  
  1         5  
  1         3  
  1         20  
  1         11  
  1         3  
  1         45  
  1         5  
  1         25  
  1         34  
  1         10  
  1         3  
  1         58  
  1         9  
  1         3  
  1         34  
  1         15  
  1         2  
  1         63  
  1         7  
  1         2  
  1         32  
  1         14  
  1         2  
  1         67  
  1         5  
  1         2  
  1         47  
  1         12  
  1         5  
  1         46  
  1         17  
  1         3  
  1         73  
  1         11  
  1         3  
  1         43  
  1         7  
  1         3  
  1         25  
  1         6  
  1         3  
  1         27  
  1         7  
  1         3  
  1         37  
  1         9  
  1         2  
  1         61  
  1         6  
  1         3  
  1         25  
  1         12  
  1         2  
  1         167  
  1         5  
  1         2  
  1         27  
  1         11  
  1         3  
  1         56  
  1         15  
  1         3  
  1         71  
  1         29  
  1         5  
  1         71  
  1         6  
  1         10  
  1         27  
  1         18  
  1         4  
  1         109  
  1         27  
  1         3  
  1         51  
  1         7  
  1         3  
  1         31  
  1         17  
  1         4  
  1         89  
  1         7  
  1         2  
  1         31  
  1         8  
  1         2  
  1         78  
  1         12  
  1         2  
  1         164  
  1         11  
  1         3  
  1         72  
  1         8  
  1         3  
  1         32  
  1         7  
  1         2  
  1         31  
  1         6  
  1         3  
  1         22  
  1         5  
  1         2  
  1         24  
  1         9  
  1         2  
  1         36  
  1         11  
  1         3  
  1         46  
  1         10  
  1         2  
  1         56  
  1         9  
  1         2  
  1         31  
  1         18  
  1         5  
  1         70  
  1         8  
  1         1  
  1         46  
  1         11  
  1         2  
  1         62  
  1         17  
  1         4  
  1         92  
  1         16  
  1         5  
  1         80  
  1         9  
  1         6  
  1         44  
  1         12  
  1         3  
  1         68  
  1         11  
  1         4  
  1         52  
  1         14  
  1         3  
  1         93  
  1         12  
  1         4  
  1         46  
  1         18  
  1         3  
  1         118  
  1         8  
  1         5  
  1         37  
  1         18  
  1         3  
  1         93  
  1         19  
  1         4  
  1         93  
  1         18  
  1         16  
  1         81  
  1         9  
  1         4  
  1         38  
  1         12  
  1         2  
  1         55  
  1         16  
  1         4  
  1         61  
  1         11  
  1         3  
  1         58  
  1         12  
  1         2  
  1         60  
  1         5  
  1         1  
  1         26  
  1         10  
  1         4  
  1         45  
  1         8  
  1         9  
  1         44  
  1         9  
  1         3  
  1         38  
  1         6  
  1         3  
  1         24  
  1         7  
  1         2  
  1         28  
  1         6  
  1         1  
  1         20  
  1         4  
  1         3  
  1         23  
  1         8  
  1         2  
  1         44  
  1         7  
  1         3  
  1         40  
  1         6  
  1         2  
  1         30  
  1         11  
  1         2  
  1         47  
  1         8  
  1         2  
  1         33  
  1         10  
  1         3  
  1         65  
  1         5  
  1         2  
  1         34  
  1         15  
  1         3  
  1         66  
  1         10  
  1         2  
  1         58  
  1         9  
  1         3  
  1         43  
  1         7  
  1         2  
  1         25  
  1         7  
  1         2  
  1         29  
  1         8  
  1         11  
  1         29  
  1         10  
  1         3  
  1         73  
  1         6  
  1         2  
  1         29  
  1         15  
  1         3  
  1         80  
  1         8  
  1         4  
  1         37  
  1         10  
  1         3  
  1         57  
  1         11  
  1         6  
  1         113  
  1         11  
  1         4  
  1         44  
  1         5  
  1         2  
  1         28  
  1         10  
  1         3  
  1         58  
  1         8  
  1         2  
  1         27  
  1         7  
  1         3  
  1         24  
  1         10  
  1         3  
  1         62  
  1         238  
  1         3  
  1         36  
  1         7  
  1         2  
  1         30  
  1         4  
  1         2  
  1         20  
  1         13  
  1         4  
  1         58  
  1         7  
  1         3  
  1         31  
  1         9  
  1         4  
  1         40  
  1         13  
  1         4  
  1         56  
  1         6  
  1         3  
  1         30  
  1         6  
  1         3  
  1         28  
  1         8  
  1         3  
  1         38  
  1         8  
  1         474  
  1         41  
  1         8  
  1         2  
  1         30  
  1         10  
  1         3  
  1         144  
  1         12  
  1         3  
  1         61  
  1         10  
  1         4  
  1         72  
  1         12  
  1         2  
  1         49  
  1         10  
  1         3  
  1         85  
  1         9  
  1         3  
  1         59  
  1         8  
  1         2  
  1         37  
  1         10  
  1         3  
  1         43  
  1         10  
  1         5  
  1         46  
  1         9  
  1         4  
  1         76  
  1         6  
  1         3  
  1         28  
  1         9  
  1         2  
  1         76  
  1         6  
  1         3  
  1         50  
  1         11  
  1         2  
  1         50  
  1         10  
  1         2  
  1         49  
  1         6  
  1         2  
  1         33  
  1         4  
  1         3  
  1         31  
  1         10  
  1         2  
  1         49  
  1         6  
  1         2  
  1         27  
  1         9  
  1         2  
  1         28  
  1         10  
  1         2  
  1         53  
  1         5  
  1         3  
  1         23  
  1         7  
  1         2  
  1         28  
  1         6  
  1         3  
  1         31  
  1         8  
  1         3  
  1         32  
  1         9  
  1         3  
  1         44  
  1         6  
  1         3  
  1         26  
  1         5  
  1         2  
  1         25  
  1         8  
  1         3  
  1         29  
  1         11  
  1         3  
  1         35  
  1         5  
  1         2  
  1         22  
  1         9  
  1         4  
  1         36  
  1         5  
  1         2  
  1         25  
  1         10  
  1         2  
  1         59  
  1         4  
  1         2  
  1         25  
  1         6  
  1         18  
  1         34  
  1         13  
  1         3  
  1         59  
  1         7  
  1         2  
  1         29  
  1         5  
  1         2  
  1         27  
  1         5  
  1         2  
  1         25  
  1         7  
  1         2  
  1         29  
  1         9  
  1         3  
  1         75  
  1         7  
  1         4  
  1         28  
  1         11  
  1         3  
  1         50  
  1         22  
  1         3  
  1         27  
  1         9  
  1         2  
  1         47  
  1         23  
  1         3  
  1         54  
  1         10  
  1         2  
  1         59  
  1         11  
  1         3  
  1         56  
  1         10  
  1         2  
  1         47  
  1         7  
  1         2  
  1         31  
  1         8  
  1         3  
  1         62  
  1         10  
  1         3  
  1         50  
  1         7  
  1         2  
  1         26  
  1         8  
  1         10  
  1         32  
  1         4  
  1         3  
  1         21  
  1         9  
  1         3  
  1         33  
  1         9  
  1         3  
  1         61  
  1         10  
  1         3  
  1         41  
  1         8  
  1         3  
  1         56  
  1         5  
  1         2  
  1         22  
  1         6  
  1         2  
  1         23  
  1         5  
  1         3  
  1         25  
  1         6  
  1         3  
  1         27  
  1         27  
  1         2  
  1         29  
  1         8  
  1         1  
  1         53  
  1         5  
  1         2  
  1         25  
  1         14  
  1         1  
  1         57  
  1         7  
  1         3  
  1         28  
  1         7  
  1         3  
  1         54  
  1         11  
  1         1  
  1         51  
  1         8  
  1         2  
  1         33  
  1         6  
  1         2  
  1         29  
  1         8  
  1         2  
  1         35  
  1         8  
  1         2  
  1         28  
365 400 50       1577 die "Evaluation of '$code' failed: $@" if $@;
366 400 100       3054 return 0 if !$res;
367             }
368 138         1019 return 1;
369             } else {
370 12569 50 66     22336 return 1 if !defined $left && !defined $right;
371 12569 100       49383 return 1 if $left eq $right;
372             }
373 29         262 };
374              
375 29         68 my $handle_mapping_entry; $handle_mapping_entry = sub {
376 35362     35362   53557 my($entry, $level) = @_;
377 35362         68007 for(my $map_i=0; $map_i <= $#$entry; $map_i++) {
378 63959         99717 my $key_or_subentry = $entry->[$map_i];
379 63959 100       110936 if (ref $key_or_subentry eq 'ARRAY') {
    100          
380 27960         73314 $self->_debug(' ' x $level . ' traverse another tree level');
381 27960         48208 my $res = $handle_mapping_entry->($key_or_subentry, $level+1);
382 27959 100 100     126624 return $res if $res && !$TRAVERSE_ONLY;
383             } elsif (ref $key_or_subentry eq 'CODE') {
384 1         26 my $res = $key_or_subentry->($self, $dist);
385 1 50 33     6 return $res if $res && !$TRAVERSE_ONLY;
386             } else {
387 35998         45638 my $key = $key_or_subentry;
388 35998         52032 my $match = $entry->[++$map_i];
389 35998         98134 $self->_debug(' ' x $level . " match '$key' against '", $match, "'");
390 35998 100       110531 if ($key eq 'cpandist') {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
391 37 100 100     197 return 0 if !$smartmatch->($dist->base_id, $match) && !$TRAVERSE_ONLY;
392             } elsif ($key eq 'cpanmod') {
393 7365         9326 my $found = 0;
394 7365         17792 for my $mod ($dist->containsmods) {
395 7365         59784 $self->_debug(' ' x $level . " found module '$mod' in dist, check now against '", $match, "'");
396 7365 100       11882 if ($smartmatch->($mod, $match)) {
397 15         20 $found = 1;
398 15         23 last;
399             }
400             }
401 7365 100 100     28216 return 0 if !$found && !$TRAVERSE_ONLY;
402             } elsif ($key eq 'os') {
403 5223 100 100     9273 return 0 if !$smartmatch->($self->{os}, $match) && !$TRAVERSE_ONLY;
404             } elsif ($key eq 'osvers') {
405 166 100 100     428 return 0 if !$smartmatch->($self->{osvers}, $match) && !$TRAVERSE_ONLY;
406             } elsif ($key eq 'linuxdistro') {
407 7502 100       27029 if ($match =~ m{^~(debian|fedora)$}) {
    100          
408 6889         16857 my $method = "_is_linux_$1_like";
409 6889         18395 $self->_debug(' ' x $level . " translate $match to $method");
410 6889 50 66     19792 return 0 if !$self->$method($self->{linuxdistro}) && !$TRAVERSE_ONLY;
411             } elsif ($match =~ m{^~}) {
412 1         14 die "'like' matches only for debian and fedora";
413             } else {
414 612 50 66     1178 return 0 if !$smartmatch->($self->{linuxdistro}, $match) && !$TRAVERSE_ONLY;
415             }
416             } elsif ($key eq 'linuxdistroversion') {
417 372 50 66     825 return 0 if !$smartmatch->($self->{linuxdistroversion}, $match) && !$TRAVERSE_ONLY;
418             } elsif ($key eq 'linuxdistrocodename') {
419 1272 50 66     2515 return 0 if !$smartmatch->($self->{linuxdistrocodename}, $match) && !$TRAVERSE_ONLY; # XXX should also do a smart codename comparison additionally!
420             } elsif ($key eq 'package') {
421 14061         43340 $self->_debug(' ' x $level . " found $match"); # XXX array?
422 14061         41001 return { package => $match };
423             } else {
424 0         0 die "Invalid key '$key'"; # XXX context/position?
425             }
426             }
427             }
428 29         309 };
429              
430 29 50       59 for my $entry (@{ $self->{mapping} || [] }) {
  29         163  
431 7402         12192 my $res = $handle_mapping_entry->($entry, 0);
432 7401 100 66     18157 if ($res && !$TRAVERSE_ONLY) {
433 10 100       73 return ref $res->{package} eq 'ARRAY' ? @{ $res->{package} } : $res->{package};
  2         39  
434             }
435             }
436              
437 18         173 ();
438             }
439              
440             sub _detect_dnf {
441 1     1   4 my @cmd = ('dnf', '--help');
442 1         736 require IPC::Open3;
443 1         4018 require Symbol;
444 1         8 my $err = Symbol::gensym();
445 1         20 my $fh;
446 1         2 return eval {
447 1 0       6 if (my $pid = IPC::Open3::open3(undef, $fh, $err, @cmd)) {
448 0         0 waitpid $pid, 0;
449 0         0 return $? == 0;
450             }
451             };
452             }
453              
454             sub _find_missing_deb_packages {
455 5     5   20 my($self, @packages) = @_;
456 5 100       24 return () if !@packages;
457              
458             # taken from ~/devel/deb-install.pl
459 4         12 my %seen_packages;
460             my @missing_packages;
461              
462 4         16 my @cmd = ('dpkg-query', '-W', '-f=${Package} ${Status}\n', @packages);
463 4         1693 require IPC::Open3;
464 4         10037 require Symbol;
465 4         27 my $err = Symbol::gensym();
466 4         73 my $fh;
467 4 50       21 my $pid = IPC::Open3::open3(undef, $fh, $err, @cmd)
468             or die "Error running '@cmd': $!";
469 4         61118 while(<$fh>) {
470 0         0 chomp;
471 0 0       0 if (m{^(\S+) (.*)}) {
472 0 0       0 if ($2 ne 'install ok installed') {
473 0         0 push @missing_packages, $1;
474             }
475 0         0 $seen_packages{$1} = 1;
476             } else {
477 0         0 warn "ERROR: cannot parse $_, ignore line...\n";
478             }
479             }
480 4         167 waitpid $pid, 0;
481 4         47 for my $package (@packages) {
482 6 50       78 if (!$seen_packages{$package}) {
483 6         46 push @missing_packages, $package;
484             }
485             }
486 4         296 @missing_packages;
487             }
488              
489             sub _find_missing_rpm_packages {
490 6     6   613383 my($self, @packages) = @_;
491 6 100       87 return () if !@packages;
492              
493 5         41 my @missing_packages;
494             {
495 5         37 my %packages = map{($_,1)} @packages;
  5         54  
  6         87  
496              
497 5         158 local $ENV{LC_ALL} = 'C';
498 5         41 my @cmd = ('rpm', '-q', @packages);
499 5 50       34904 open my $fh, '-|', @cmd
500             or die "Error running '@cmd': $!";
501 5         2906984 while(<$fh>) {
502 8 100       18886 if (m{^package (\S+) is not installed}) {
503 4         88 my $package = $1;
504 4 50       47 if (!exists $packages{$package}) {
505 0         0 die "Unexpected: package $package listed as non-installed, but not queried in '@cmd'?!";
506             }
507 4         19188 push @missing_packages, $package;
508             }
509             }
510             }
511 5 100       163 return () if !@missing_packages;
512              
513             # maybe the packages are just provided by another package?
514 4         16 my @definitively_missing_packages;
515             {
516 4         11 my %packages = map{($_,1)} @missing_packages;
  4         40  
  4         38  
517              
518 4         74 local $ENV{LC_ALL} = 'C';
519 4         41 my @cmd = ('rpm', '-q', '--whatprovides', @missing_packages);
520 4 50       23411 open my $fh, '-|', @cmd
521             or die "Error running '@cmd': $!";
522 4         2224088 while(<$fh>) {
523 5 100       6999 if (m{^no package provides (\S+)}) {
524 3         59 my $package = $1;
525 3 50       23 if (!exists $packages{$package}) {
526 0         0 die "Unexpected: package $package listed as non-installed, but not queried in '@cmd'?!";
527             }
528 3         17588 push @definitively_missing_packages, $package;
529             }
530             }
531             }
532              
533 4         244 @definitively_missing_packages;
534             }
535              
536             sub _find_missing_freebsd_pkg_packages {
537 0     0   0 my($self, @packages) = @_;
538 0 0       0 return () if !@packages;
539              
540 0         0 my @missing_packages;
541 0         0 for my $package (@packages) {
542 0         0 my @cmd = ('pkg', 'info', '--exists', $package);
543 0         0 system @cmd;
544 0 0       0 if ($? != 0) {
545 0         0 push @missing_packages, $package;
546             }
547             }
548              
549 0         0 @missing_packages;
550             }
551              
552             sub _find_missing_openbsd_pkg_packages {
553 0     0   0 my($self, @packages) = @_;
554 0 0       0 return () if !@packages;
555              
556 0         0 require IPC::Open3;
557 0         0 require Symbol;
558              
559 0         0 my @missing_packages;
560 0         0 for my $package (@packages) {
561 0         0 my $err = Symbol::gensym();
562 0         0 my $fh;
563             my $package_in_repository;
564 0         0 eval {
565 0 0       0 if (my $pid = IPC::Open3::open3(undef, $fh, $err, 'pkg_info', $package)) {
566 0         0 waitpid $pid, 0;
567 0 0       0 if ($? == 0) {
568 0         0 $package_in_repository = 1;
569             }
570             }
571             };
572 0 0       0 if ($package_in_repository) {
573 0         0 my @cmd = ('pkg_info', '-q', '-e', "${package}->=0");
574 0         0 system @cmd;
575 0 0       0 if ($? != 0) {
576 0         0 push @missing_packages, $package;
577             }
578             }
579             }
580              
581 0         0 @missing_packages;
582             }
583              
584             sub _find_missing_homebrew_packages {
585 0     0   0 my($self, @packages) = @_;
586 0 0       0 return () if !@packages;
587              
588 0         0 my @missing_packages;
589 0         0 for my $package (@packages) {
590 0         0 my @cmd = ('brew', 'ls', '--versions', $package);
591 0 0       0 open my $fh, '-|', @cmd
592             or die "Error running @cmd: $!";
593 0         0 my $has_package;
594 0         0 while(<$fh>) {
595 0         0 $has_package = 1;
596 0         0 last;
597             }
598 0         0 close $fh; # earlier homebrew versions returned always 0,
599             # newer (since Oct 2016) return 1 if the package is
600             # missing
601 0 0       0 if (!$has_package) {
602 0         0 push @missing_packages, $package;
603             }
604             }
605 0         0 @missing_packages;
606             }
607              
608             sub _find_missing_chocolatey_packages {
609 0     0   0 my($self, @packages) = @_;
610 0 0       0 return () if !@packages;
611              
612             my %installed_packages = map {
613 0 0       0 /^(.*)\|(.*)$/
614             or next;
615 0         0 $1 => $2
616             } grep {
617 0         0 /^(.*)\|(.*)$/
  0         0  
618             } `$self->{installer} list --localonly --limit-output`;
619 0         0 my @missing_packages = grep { ! $installed_packages{ $_ }} @packages;
  0         0  
620 0         0 @missing_packages;
621             }
622              
623             sub _filter_uninstalled_packages {
624 4     4   2638 my($self, @packages) = @_;
625 4         8 my $find_missing_packages;
626 4 50 0     26 if ($self->_is_apt_installer) {
    0 0        
    0          
    0          
    0          
    0          
627 4         8 $find_missing_packages = '_find_missing_deb_packages';
628             } elsif (($self->{installer} eq 'yum') || ($self->{installer} eq 'dnf')) {
629 0         0 $find_missing_packages = '_find_missing_rpm_packages';
630             } elsif ($self->{os} eq 'freebsd' || $self->{os} eq 'dragonfly') {
631 0         0 $find_missing_packages = '_find_missing_freebsd_pkg_packages';
632             } elsif ($self->{os} eq 'openbsd') {
633 0         0 $find_missing_packages = '_find_missing_openbsd_pkg_packages';
634             } elsif ($self->{os} eq 'MSWin32') {
635 0         0 $find_missing_packages = '_find_missing_chocolatey_packages';
636             } elsif ($self->{installer} eq 'homebrew') {
637 0         0 $find_missing_packages = '_find_missing_homebrew_packages';
638             } else {
639 0         0 warn "check for installed packages is NYI for $self->{os}/$self->{linuxdistro}";
640             }
641 4 50       14 if ($find_missing_packages) {
642 4         15 my @plain_packages;
643             my @missing_packages;
644 4         10 for my $package_spec (@packages) {
645 5 100       39 if ($package_spec =~ m{\|}) { # has alternatives
646 1         17 my @single_packages = split /\s*\|\s*/, $package_spec;
647 1         25 my @missing_in_packages_spec = $self->$find_missing_packages(@single_packages);
648 1 50       19 if (@missing_in_packages_spec == @single_packages) {
649 1         24 push @missing_packages, $single_packages[0]; # XXX this could go wrong if the first alternative is not available at all; see problem with jpeg|jpeg-turbo in Mapping.pm
650             }
651             } else {
652 4         10 push @plain_packages, $package_spec;
653             }
654             }
655 4         26 push @missing_packages, $self->$find_missing_packages(@plain_packages);
656 4         58 @packages = @missing_packages;
657             }
658 4         103 @packages;
659             }
660              
661             sub _install_packages_commands {
662 11     11   78 my($self, @packages) = @_;
663 11         26 my @pre_cmd;
664             my @install_cmd;
665              
666             # sudo or not?
667 11 100       64 if ($self->{installer} eq 'homebrew') {
    50          
668             # may run as unprivileged user
669             } elsif ($self->{installer} eq 'chocolatey') {
670             # no sudo on Windows systems?
671             } else {
672 10 50       91 if ($< != 0) {
673 0         0 push @install_cmd, 'sudo';
674             }
675             }
676              
677             # the installer executable
678 11 100       45 if ($self->{installer} eq 'homebrew') {
679 1         4 push @install_cmd, 'brew';
680             } else {
681 10         53 push @install_cmd, $self->{installer};
682             }
683              
684             # batch, default or interactive
685 11 100       46 if ($self->{batch}) {
686 3 50 0     23 if ($self->_is_apt_installer) {
    0          
    0          
    0          
687 3         14 push @install_cmd, '-y';
688             } elsif (($self->{installer} eq 'yum') || ($self->{installer} eq 'dnf')) {
689 0         0 push @install_cmd, '-y';
690             } elsif ($self->{installer} eq 'pkg') { # FreeBSD's pkg
691             # see below
692             } elsif ($self->{installer} eq 'homebrew') {
693             # batch by default
694             } else {
695 0         0 warn "batch=1 NYI for $self->{installer}";
696             }
697             } else {
698 8 100 100     27 if ($self->_is_apt_installer) {
    100          
    100          
    50          
    100          
699 3         17 @pre_cmd = ('sh', '-c', 'echo -n "Install package(s) '."@packages".'? (y/N) "; read yn; [ "$yn" = "y" ]');
700             } elsif (($self->{installer} eq 'yum') || ($self->{installer} eq 'dnf')) {
701             # interactive by default
702             } elsif ($self->{installer} eq 'pkg') { # FreeBSD's pkg
703             # see below
704             } elsif ($self->{installer} =~ m{^(chocolatey)$}) {
705             # Nothing to do here
706             } elsif ($self->{installer} eq 'homebrew') {
707             # the sh builtin echo does not understand -n -> use printf
708 1         7 @pre_cmd = ('sh', '-c', 'printf %s "Install package(s) '."@packages".'? (y/N) "; read yn; [ "$yn" = "y" ]');
709             } else {
710 1         48 warn "batch=0 NYI for $self->{installer}";
711             }
712             }
713              
714             # special options
715 11 100       47 if ($self->{installer} eq 'pkg') { # FreeBSD's pkg
716 1         3 push @install_cmd, '--option', 'LOCK_RETRIES=86400'; # wait quite long in case there are concurrent pkg runs
717             }
718              
719             # the installer subcommand
720 11 100       36 if ($self->{installer} ne 'pkg_add') {
721 10         27 push @install_cmd, 'install';
722             }
723              
724             # post options
725 11 50 66     74 if ($self->{batch} && $self->{installer} eq 'pkg') {
726 0         0 push @install_cmd, '-y';
727             }
728 11 50 66     103 if ($self->{batch} && $self->{installer} eq 'chocolatey') {
729 0         0 push @install_cmd, '-y';
730             }
731 11 50 66     46 if ($self->{batch} && $self->{installer} eq 'pkg_add') {
732 0         0 push @install_cmd, '-I';
733             }
734              
735 11         31 push @install_cmd, @packages;
736            
737 11 50       38 if ($self->{os} eq 'MSWin32') {
738             # Wrap the thing in our small powershell program
739 0         0 @install_cmd = _win32_run_elevated(@install_cmd);
740             };
741              
742 11 100       69 ((@pre_cmd ? \@pre_cmd : ()), \@install_cmd);
743             }
744              
745             1;
746              
747             __END__