line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Sys::OsPackage |
2
|
|
|
|
|
|
|
# ABSTRACT: install OS packages and determine if CPAN modules are packaged for the OS |
3
|
|
|
|
|
|
|
# Copyright (c) 2022 by Ian Kluft |
4
|
|
|
|
|
|
|
# Open Source license Perl's Artistic License 2.0: |
5
|
|
|
|
|
|
|
# SPDX-License-Identifier: Artistic-2.0 |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
# This module is maintained for minimal dependencies so it can build systems/containers from scratch. |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
## no critic (Modules::RequireExplicitPackage) |
10
|
|
|
|
|
|
|
# This resolves conflicting Perl::Critic rules which want package and strictures each before the other |
11
|
3
|
|
|
3
|
|
2209
|
use strict; |
|
3
|
|
|
|
|
15
|
|
|
3
|
|
|
|
|
73
|
|
12
|
3
|
|
|
3
|
|
12
|
use warnings; |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
64
|
|
13
|
3
|
|
|
3
|
|
1525
|
use utf8; |
|
3
|
|
|
|
|
37
|
|
|
3
|
|
|
|
|
12
|
|
14
|
|
|
|
|
|
|
## use critic (Modules::RequireExplicitPackage) |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
package Sys::OsPackage; |
17
|
|
|
|
|
|
|
$Sys::OsPackage::VERSION = '0.1.6'; |
18
|
3
|
|
|
3
|
|
137
|
use Config; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
125
|
|
19
|
3
|
|
|
3
|
|
15
|
use Carp qw(carp croak confess); |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
202
|
|
20
|
3
|
|
|
3
|
|
1399
|
use Sys::OsRelease; |
|
3
|
|
|
|
|
6518
|
|
|
3
|
|
|
|
|
72
|
|
21
|
3
|
|
|
3
|
|
1240
|
use autodie; |
|
3
|
|
|
|
|
40242
|
|
|
3
|
|
|
|
|
21
|
|
22
|
|
|
|
|
|
|
BEGIN { |
23
|
|
|
|
|
|
|
# import methods from Sys::OsRelease to manage singleton instance |
24
|
3
|
|
|
3
|
|
19347
|
Sys::OsRelease->import_singleton(); |
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
# system configuration |
28
|
|
|
|
|
|
|
my %_sysconf = ( |
29
|
|
|
|
|
|
|
# additional common IDs to provide to Sys::OsRelease to recognize as common platforms in ID_LIKE attributes |
30
|
|
|
|
|
|
|
# this adds to recognized common platforms: |
31
|
|
|
|
|
|
|
# RHEL, SuSE, Ubuntu - common commercial platforms |
32
|
|
|
|
|
|
|
# CentOS - because we use it to recognize Rocky and Alma as needing EPEL |
33
|
|
|
|
|
|
|
common_id => [qw(centos rhel suse ubuntu)], |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# command search list & path |
36
|
|
|
|
|
|
|
search_cmds => [qw(uname curl tar cpan cpanm rpm yum repoquery dnf apt apt-cache dpkg-query apk pacman brew |
37
|
|
|
|
|
|
|
zypper)], |
38
|
|
|
|
|
|
|
search_path => [qw(/bin /usr/bin /sbin /usr/sbin /opt/bin /usr/local/bin)], |
39
|
|
|
|
|
|
|
); |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
# platform/package configuration |
42
|
|
|
|
|
|
|
# all entries in here have a second-level hash keyed on the platform |
43
|
|
|
|
|
|
|
# TODO: refactor to delegate this to packaging driver classes |
44
|
|
|
|
|
|
|
my %_platconf = ( |
45
|
|
|
|
|
|
|
# platform packaging handler class name |
46
|
|
|
|
|
|
|
packager => { |
47
|
|
|
|
|
|
|
alpine => "Sys::OsPackage::Driver::Alpine", |
48
|
|
|
|
|
|
|
arch => "Sys::OsPackage::Driver::Arch", |
49
|
|
|
|
|
|
|
centos => "Sys::OsPackage::Driver::RPM", # CentOS no longer exists; CentOS derivatives supported via ID_LIKE |
50
|
|
|
|
|
|
|
debian => "Sys::OsPackage::Driver::Debian", |
51
|
|
|
|
|
|
|
fedora => "Sys::OsPackage::Driver::RPM", |
52
|
|
|
|
|
|
|
opensuse => "Sys::OsPackage::Driver::Suse", |
53
|
|
|
|
|
|
|
rhel => "Sys::OsPackage::Driver::RPM", |
54
|
|
|
|
|
|
|
suse => "Sys::OsPackage::Driver::Suse", |
55
|
|
|
|
|
|
|
ubuntu => "Sys::OsPackage::Driver::Debian", |
56
|
|
|
|
|
|
|
}, |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
# package name override where computed name is not correct |
59
|
|
|
|
|
|
|
override => { |
60
|
|
|
|
|
|
|
debian => { |
61
|
|
|
|
|
|
|
"libapp-cpanminus-perl" => "cpanminus", |
62
|
|
|
|
|
|
|
}, |
63
|
|
|
|
|
|
|
ubuntu => { |
64
|
|
|
|
|
|
|
"libapp-cpanminus-perl" => "cpanminus", |
65
|
|
|
|
|
|
|
}, |
66
|
|
|
|
|
|
|
arch => { |
67
|
|
|
|
|
|
|
"perl-app-cpanminus" => "cpanminus", |
68
|
|
|
|
|
|
|
"tar" => "core/tar", |
69
|
|
|
|
|
|
|
"curl" => "core/curl", |
70
|
|
|
|
|
|
|
}, |
71
|
|
|
|
|
|
|
}, |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# prerequisite OS packages for CPAN |
74
|
|
|
|
|
|
|
prereq => { |
75
|
|
|
|
|
|
|
alpine => [qw(perl-utils)], |
76
|
|
|
|
|
|
|
fedora => [qw(perl-CPAN)], |
77
|
|
|
|
|
|
|
centos => [qw(epel-release perl-CPAN)], # CentOS no longer exists, still used for CentOS-derived systems |
78
|
|
|
|
|
|
|
debian => [qw(perl-modules)], |
79
|
|
|
|
|
|
|
opensuse => [qw()], |
80
|
|
|
|
|
|
|
suse => [qw()], |
81
|
|
|
|
|
|
|
ubuntu => [qw(perl-modules)], |
82
|
|
|
|
|
|
|
}, |
83
|
|
|
|
|
|
|
); |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
# Perl-related configuration (read only) |
86
|
|
|
|
|
|
|
my %_perlconf = ( |
87
|
|
|
|
|
|
|
sources => { |
88
|
|
|
|
|
|
|
"App::cpanminus" => 'https://cpan.metacpan.org/authors/id/M/MI/MIYAGAWA/App-cpanminus-1.7046.tar.gz', |
89
|
|
|
|
|
|
|
}, |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# Perl module dependencies |
92
|
|
|
|
|
|
|
# Sys::OsPackage doesn't have to declare these as dependencies because it will load them by package or CPAN before use |
93
|
|
|
|
|
|
|
# That maintains a light footprint for bootstrapping a container or system. |
94
|
|
|
|
|
|
|
module_deps => [qw(Term::ANSIColor Perl::PrereqScanner::NotQuiteLite HTTP::Tiny)], |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# OS package dependencies for CPAN |
97
|
|
|
|
|
|
|
cpan_deps => [qw(curl tar make)], |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# built-in modules/pragmas to skip processing as dependencies |
100
|
|
|
|
|
|
|
skip => { |
101
|
|
|
|
|
|
|
"strict" => 1, |
102
|
|
|
|
|
|
|
"warnings" => 1, |
103
|
|
|
|
|
|
|
"utf8" => 1, |
104
|
|
|
|
|
|
|
"feature" => 1, |
105
|
|
|
|
|
|
|
"autodie" => 1, |
106
|
|
|
|
|
|
|
}, |
107
|
|
|
|
|
|
|
); |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# |
110
|
|
|
|
|
|
|
# class data access functions |
111
|
|
|
|
|
|
|
# |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
# helper function to allow methods to get the instance ref when called via the class name |
114
|
|
|
|
|
|
|
sub class_or_obj |
115
|
|
|
|
|
|
|
{ |
116
|
421
|
|
|
421
|
0
|
462
|
my $coo = shift; |
117
|
421
|
100
|
|
|
|
756
|
return $coo if ref $coo; # return it if it's an object |
118
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
# safety net: all-stop if we received an undef |
120
|
22
|
50
|
|
|
|
35
|
if (not defined $coo) { |
121
|
0
|
|
|
|
|
0
|
confess "coo got undef from:".(join "|", caller 1); |
122
|
|
|
|
|
|
|
} |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
# return the instance |
125
|
22
|
|
|
|
|
80
|
my $inst_method = $coo->can("instance"); |
126
|
22
|
50
|
|
|
|
46
|
if (not $inst_method) { |
127
|
0
|
|
|
|
|
0
|
confess "incompatible class $coo from:".(join "|", caller 1); |
128
|
|
|
|
|
|
|
} |
129
|
22
|
|
|
|
|
43
|
return &$inst_method($coo); |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
# system configuration |
133
|
|
|
|
|
|
|
sub sysconf |
134
|
|
|
|
|
|
|
{ |
135
|
10
|
|
|
10
|
0
|
1320
|
my $key = shift; |
136
|
10
|
100
|
|
|
|
32
|
return if not exists $_sysconf{$key}; |
137
|
9
|
|
|
|
|
122
|
return $_sysconf{$key}; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
# Perl configuration |
141
|
|
|
|
|
|
|
sub perlconf |
142
|
|
|
|
|
|
|
{ |
143
|
5
|
|
|
5
|
0
|
2336
|
my $key = shift; |
144
|
5
|
100
|
|
|
|
16
|
return if not exists $_perlconf{$key}; |
145
|
4
|
|
|
|
|
12
|
return $_perlconf{$key}; |
146
|
|
|
|
|
|
|
} |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
# platform configuration |
149
|
|
|
|
|
|
|
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines) |
150
|
1
|
|
|
1
|
|
83
|
sub _platconf { return \%_platconf; } # for testing |
151
|
|
|
|
|
|
|
## use critic (Subroutines::ProhibitUnusedPrivateSubroutines) |
152
|
|
|
|
|
|
|
sub platconf |
153
|
|
|
|
|
|
|
{ |
154
|
22
|
|
|
22
|
0
|
962
|
my ($class_or_obj, $key) = @_; |
155
|
22
|
|
|
|
|
39
|
my $self = class_or_obj($class_or_obj); |
156
|
|
|
|
|
|
|
|
157
|
22
|
50
|
|
|
|
189
|
return if not defined $self->platform(); |
158
|
22
|
100
|
|
|
|
46
|
return if not exists $_platconf{$key}{$self->platform()}; |
159
|
21
|
|
|
|
|
42
|
return $_platconf{$key}{$self->platform()}; |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
# |
163
|
|
|
|
|
|
|
# initialization of the singleton instance |
164
|
|
|
|
|
|
|
# imported methods from Sys::OsRelease: init new instance defined_instance clear_instance |
165
|
|
|
|
|
|
|
# |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
# initialize a new instance |
168
|
|
|
|
|
|
|
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines) # called by imported instance() - perlcritic can't see it |
169
|
|
|
|
|
|
|
sub _new_instance |
170
|
|
|
|
|
|
|
{ |
171
|
2
|
|
|
2
|
|
2147
|
my ($class, @params) = @_; |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
# enforce class lineage |
174
|
2
|
50
|
|
|
|
16
|
if (not $class->isa(__PACKAGE__)) { |
175
|
0
|
0
|
|
|
|
0
|
croak "cannot find instance: ".(ref $class ? ref $class : $class)." is not a ".__PACKAGE__; |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# obtain parameters from array or hashref |
179
|
2
|
|
|
|
|
4
|
my %obj; |
180
|
2
|
50
|
|
|
|
16
|
if (scalar @params > 0) { |
181
|
2
|
50
|
|
|
|
8
|
if (ref $params[0] eq 'HASH') { |
182
|
0
|
|
|
|
|
0
|
$obj{_config} = $params[0]; |
183
|
|
|
|
|
|
|
} else { |
184
|
2
|
|
|
|
|
6
|
$obj{_config} = {@params}; |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# bless instance |
189
|
2
|
|
|
|
|
5
|
my $obj_ref = bless \%obj, $class; |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
# initialization |
192
|
2
|
50
|
|
|
|
13
|
if (exists $obj_ref->{_config}{debug}) { |
|
|
50
|
|
|
|
|
|
193
|
0
|
|
|
|
|
0
|
$obj_ref->{debug} = $obj_ref->{_config}{debug}; |
194
|
|
|
|
|
|
|
} elsif (exists $ENV{SYS_OSPACKAGE_DEBUG}) { |
195
|
0
|
|
|
|
|
0
|
$obj_ref->{debug} = deftrue($ENV{SYS_OSPACKAGE_DEBUG}); |
196
|
|
|
|
|
|
|
} |
197
|
2
|
50
|
|
|
|
7
|
if (deftrue($obj_ref->{debug})) { |
198
|
0
|
|
|
|
|
0
|
print STDERR "_new_instance($class, ".join(", ", @params).")\n"; |
199
|
|
|
|
|
|
|
} |
200
|
2
|
|
|
|
|
7
|
$obj_ref->{sysenv} = {}; |
201
|
2
|
|
|
|
|
5
|
$obj_ref->{module_installed} = {}; |
202
|
2
|
|
|
|
|
9
|
$obj_ref->collect_sysenv(); |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
# instantiate object |
205
|
2
|
|
|
|
|
26
|
return $obj_ref; |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
## use critic (Subroutines::ProhibitUnusedPrivateSubroutines) |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
# utility: test if a value is defined and is true |
210
|
|
|
|
|
|
|
sub deftrue |
211
|
|
|
|
|
|
|
{ |
212
|
12
|
|
|
12
|
0
|
78
|
my $value = shift; |
213
|
12
|
100
|
66
|
|
|
125
|
return ((defined $value) and $value) ? 1 : 0; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
# |
217
|
|
|
|
|
|
|
# functions that query instance data |
218
|
|
|
|
|
|
|
# |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
# read/write accessor for debug flag |
221
|
|
|
|
|
|
|
sub debug |
222
|
|
|
|
|
|
|
{ |
223
|
14
|
|
|
14
|
0
|
27
|
my ($class_or_obj, $value) = @_; |
224
|
14
|
|
|
|
|
20
|
my $self = class_or_obj($class_or_obj); |
225
|
|
|
|
|
|
|
|
226
|
14
|
50
|
|
|
|
30
|
if (defined $value) { |
227
|
0
|
|
|
|
|
0
|
$self->{debug} = $value; |
228
|
|
|
|
|
|
|
} |
229
|
14
|
|
|
|
|
46
|
return $self->{debug}; |
230
|
|
|
|
|
|
|
} |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
# read-only accessor for quiet flag |
233
|
|
|
|
|
|
|
sub quiet |
234
|
|
|
|
|
|
|
{ |
235
|
3
|
|
|
3
|
0
|
1438
|
my ($class_or_obj) = @_; |
236
|
3
|
|
|
|
|
7
|
my $self = class_or_obj($class_or_obj); |
237
|
|
|
|
|
|
|
|
238
|
3
|
|
|
|
|
8
|
return deftrue($self->{_config}{quiet}); |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
# read/write accessor for system environment data |
242
|
|
|
|
|
|
|
# sysenv is the data collected about the system and commands |
243
|
|
|
|
|
|
|
sub sysenv |
244
|
|
|
|
|
|
|
{ |
245
|
241
|
|
|
241
|
0
|
23152
|
my ($class_or_obj, $key, $value) = @_; |
246
|
241
|
|
|
|
|
344
|
my $self = class_or_obj($class_or_obj); |
247
|
|
|
|
|
|
|
|
248
|
241
|
100
|
|
|
|
400
|
if (defined $value) { |
249
|
40
|
|
|
|
|
123
|
$self->{sysenv}{$key} = $value; |
250
|
|
|
|
|
|
|
} |
251
|
241
|
|
|
|
|
1235
|
return $self->{sysenv}{$key}; |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
# return system platform type |
255
|
|
|
|
|
|
|
sub platform |
256
|
|
|
|
|
|
|
{ |
257
|
71
|
|
|
71
|
0
|
101
|
my ($class_or_obj) = @_; |
258
|
71
|
|
|
|
|
86
|
my $self = class_or_obj($class_or_obj); |
259
|
|
|
|
|
|
|
|
260
|
71
|
|
|
|
|
117
|
return $self->sysenv("platform"); |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
# return system packager type, or undef if not determined |
264
|
|
|
|
|
|
|
sub packager |
265
|
|
|
|
|
|
|
{ |
266
|
22
|
|
|
22
|
0
|
69
|
my ($class_or_obj) = @_; |
267
|
22
|
|
|
|
|
61
|
my $self = class_or_obj($class_or_obj); |
268
|
|
|
|
|
|
|
|
269
|
22
|
|
|
|
|
64
|
return $self->sysenv("packager"); # undef intentionally returned if it doesn't exist |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
# look up known exceptions for the platform's package naming pattern |
273
|
|
|
|
|
|
|
sub pkg_override |
274
|
|
|
|
|
|
|
{ |
275
|
0
|
|
|
0
|
0
|
0
|
my ($class_or_obj, $pkg) = @_; |
276
|
0
|
|
|
|
|
0
|
my $self = class_or_obj($class_or_obj); |
277
|
|
|
|
|
|
|
|
278
|
0
|
|
|
|
|
0
|
my $override = $self->platconf("override"); |
279
|
0
|
0
|
0
|
|
|
0
|
return if ((not defined $override) or (ref $override ne "HASH")); |
280
|
0
|
|
|
|
|
0
|
return $override->{$pkg}; |
281
|
|
|
|
|
|
|
} |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
# check if a package name is actually a pragma and may as well be skipped because it's built in to Perl |
284
|
|
|
|
|
|
|
sub mod_is_pragma |
285
|
|
|
|
|
|
|
{ |
286
|
0
|
|
|
0
|
0
|
0
|
my ($class_or_obj, $module) = @_; |
287
|
0
|
|
|
|
|
0
|
my $self = class_or_obj($class_or_obj); |
288
|
|
|
|
|
|
|
|
289
|
0
|
|
|
|
|
0
|
my $perl_skip = perlconf("skip"); |
290
|
0
|
0
|
0
|
|
|
0
|
return if ((not defined $perl_skip) or (ref $perl_skip ne "HASH")); |
291
|
0
|
0
|
|
|
|
0
|
return (deftrue($perl_skip->{$module}) ? 1 : 0); |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
# find platform-specific prerequisite packages for installation of CPAN |
295
|
|
|
|
|
|
|
sub cpan_prereqs |
296
|
|
|
|
|
|
|
{ |
297
|
0
|
|
|
0
|
0
|
0
|
my ($class_or_obj) = @_; |
298
|
0
|
|
|
|
|
0
|
my $self = class_or_obj($class_or_obj); |
299
|
|
|
|
|
|
|
|
300
|
0
|
|
|
|
|
0
|
my @prereqs = @{perlconf("cpan_deps")}; |
|
0
|
|
|
|
|
0
|
|
301
|
0
|
|
|
|
|
0
|
my $plat_prereq = $self->platconf("prereq"); |
302
|
0
|
0
|
0
|
|
|
0
|
if ((defined $plat_prereq) |
303
|
|
|
|
|
|
|
and (ref $plat_prereq eq "ARRAY")) |
304
|
|
|
|
|
|
|
{ |
305
|
0
|
|
|
|
|
0
|
push @prereqs, @{$plat_prereq}; |
|
0
|
|
|
|
|
0
|
|
306
|
|
|
|
|
|
|
} |
307
|
0
|
|
|
|
|
0
|
return @prereqs; |
308
|
|
|
|
|
|
|
} |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
# determine if a Perl module is installed, or if a value is provided act as a write accessor for the module's flag |
311
|
|
|
|
|
|
|
sub module_installed |
312
|
|
|
|
|
|
|
{ |
313
|
0
|
|
|
0
|
0
|
0
|
my ($class_or_obj, $name, $value) = @_; |
314
|
0
|
|
|
|
|
0
|
my $self = class_or_obj($class_or_obj); |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
# if a value is provided then act as a write accessor to the module_installed flag for the module |
317
|
0
|
0
|
|
|
|
0
|
if (defined $value) { |
318
|
0
|
0
|
|
|
|
0
|
my $flag = $value ? 1 : 0; |
319
|
0
|
|
|
|
|
0
|
$self->{module_installed}{$name} = $flag; |
320
|
0
|
|
|
|
|
0
|
return $flag; |
321
|
|
|
|
|
|
|
} |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
# short-circuit the search if we installed the module or already found it installed |
324
|
0
|
0
|
|
|
|
0
|
return 1 if deftrue($self->{module_installed}{$name}); |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
# check each path element for the module |
327
|
0
|
|
|
|
|
0
|
my $modfile = join("/", split(/::/x, $name)); |
328
|
0
|
|
|
|
|
0
|
foreach my $element (@INC) { |
329
|
0
|
|
|
|
|
0
|
my $filepath = "$element/$modfile.pm"; |
330
|
0
|
0
|
|
|
|
0
|
if (-f $filepath) { |
331
|
0
|
|
|
|
|
0
|
$self->{module_installed}{$name} = 1; |
332
|
0
|
|
|
|
|
0
|
return 1; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
} |
335
|
0
|
|
|
|
|
0
|
return 0; |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
# run an external command and capture its standard output |
339
|
|
|
|
|
|
|
# optional \%args in first parameter |
340
|
|
|
|
|
|
|
# carp_errors - carp full details in case of errors |
341
|
|
|
|
|
|
|
# list - return an array of result lines |
342
|
|
|
|
|
|
|
sub capture_cmd |
343
|
|
|
|
|
|
|
{ |
344
|
7
|
|
|
7
|
0
|
535
|
my ($class_or_obj, @cmd) = @_; |
345
|
7
|
|
|
|
|
33
|
my $self = class_or_obj($class_or_obj); |
346
|
7
|
50
|
|
|
|
24
|
$self->debug() and print STDERR "debug(capture_cmd): ".join(" ", @cmd)."\n"; |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
# get optional arguments if first element of @cmd is a hashref |
349
|
7
|
|
|
|
|
10
|
my %args; |
350
|
7
|
50
|
|
|
|
18
|
if (ref $cmd[0] eq "HASH") { |
351
|
0
|
|
|
|
|
0
|
%args = %{shift @cmd}; |
|
0
|
|
|
|
|
0
|
|
352
|
|
|
|
|
|
|
} |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
# capture output |
355
|
7
|
|
|
|
|
8
|
my @output; |
356
|
7
|
|
|
|
|
18
|
my $cmd = join( " ", @cmd); |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
# @cmd is concatenated into $cmd - any args which need quotes should have them included |
359
|
|
|
|
|
|
|
{ |
360
|
3
|
|
|
3
|
|
5035
|
no autodie; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
12
|
|
|
7
|
|
|
|
|
9
|
|
361
|
7
|
50
|
|
|
|
10125
|
open my $fh, "-|", $cmd |
362
|
|
|
|
|
|
|
or croak "failed to run pipe command '$cmd': $!"; |
363
|
7
|
|
|
|
|
2714
|
while (<$fh>) { |
364
|
7
|
|
|
|
|
64
|
chomp; |
365
|
7
|
|
|
|
|
149
|
push @output, $_; |
366
|
|
|
|
|
|
|
} |
367
|
7
|
50
|
|
|
|
365
|
if(not close $fh) { |
368
|
0
|
0
|
|
|
|
0
|
if (deftrue($args{carp_errors})) { |
369
|
0
|
|
|
|
|
0
|
carp "failed to close pipe for command '$cmd': $!";; |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
# detect and handle errors |
375
|
7
|
50
|
|
|
|
90
|
if ($? != 0) { |
376
|
|
|
|
|
|
|
# for some commands displaying errors are unnecessary - carp errors if requested |
377
|
0
|
0
|
|
|
|
0
|
if (deftrue($args{carp_errors})) { |
378
|
0
|
|
|
|
|
0
|
carp "exit status $? from command '$cmd'"; |
379
|
|
|
|
|
|
|
} |
380
|
0
|
|
|
|
|
0
|
return; |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
# return results |
384
|
7
|
50
|
|
|
|
164
|
if (deftrue($args{list})) { |
385
|
|
|
|
|
|
|
# return an array if list option set |
386
|
0
|
|
|
|
|
0
|
return @output; |
387
|
|
|
|
|
|
|
} |
388
|
7
|
100
|
|
|
|
300
|
return wantarray ? @output : join("\n", @output); |
389
|
|
|
|
|
|
|
} |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
# get working directory (with minimal library prerequisites) |
392
|
|
|
|
|
|
|
sub pwd |
393
|
|
|
|
|
|
|
{ |
394
|
0
|
|
|
0
|
0
|
0
|
my ($class_or_obj) = @_; |
395
|
0
|
|
|
|
|
0
|
my $self = class_or_obj($class_or_obj); |
396
|
|
|
|
|
|
|
|
397
|
0
|
|
|
|
|
0
|
my $pwd = $self->capture_cmd('pwd'); |
398
|
0
|
0
|
|
|
|
0
|
$self->debug() and print STDERR "debug: pwd = $pwd\n"; |
399
|
0
|
|
|
|
|
0
|
return $pwd; |
400
|
|
|
|
|
|
|
} |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
# find executable files in the $PATH and standard places |
403
|
|
|
|
|
|
|
sub cmd_path |
404
|
|
|
|
|
|
|
{ |
405
|
32
|
|
|
32
|
0
|
66
|
my ($class_or_obj, $name) = @_; |
406
|
32
|
|
|
|
|
41
|
my $self = class_or_obj($class_or_obj); |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
# collect and cache path info |
409
|
32
|
100
|
66
|
|
|
54
|
if (not defined $self->sysenv("path_list") or not defined $self->sysenv("path_flag")) { |
410
|
2
|
|
|
|
|
36
|
$self->sysenv("path_list", [split /:/x, $ENV{PATH}]); |
411
|
2
|
|
|
|
|
5
|
$self->sysenv("path_flag", {map { ($_ => 1) } @{$self->sysenv("path_list")}}); |
|
18
|
|
|
|
|
56
|
|
|
2
|
|
|
|
|
4
|
|
412
|
2
|
|
|
|
|
8
|
my $path_flag = $self->sysenv("path_flag"); |
413
|
2
|
|
|
|
|
3
|
foreach my $dir (@{sysconf("search_path")}) { |
|
2
|
|
|
|
|
4
|
|
414
|
12
|
100
|
|
|
|
151
|
-d $dir or next; |
415
|
10
|
50
|
|
|
|
36
|
if (not exists $path_flag->{$dir}) { |
416
|
0
|
|
|
|
|
0
|
push @{$self->sysenv("path_list")}, $dir; |
|
0
|
|
|
|
|
0
|
|
417
|
0
|
|
|
|
|
0
|
$path_flag->{$dir} = 1; |
418
|
|
|
|
|
|
|
} |
419
|
|
|
|
|
|
|
} |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
# check each path element for the file |
423
|
32
|
|
|
|
|
39
|
foreach my $element (@{$self->sysenv("path_list")}) { |
|
32
|
|
|
|
|
43
|
|
424
|
246
|
|
|
|
|
516
|
my $filepath = "$element/$name"; |
425
|
246
|
100
|
|
|
|
2271
|
if (-x $filepath) { |
426
|
14
|
|
|
|
|
63
|
return $filepath; |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
} |
429
|
18
|
|
|
|
|
59
|
return; |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
# de-duplicate a colon-delimited path |
433
|
|
|
|
|
|
|
# utility function |
434
|
|
|
|
|
|
|
sub _dedup_path |
435
|
|
|
|
|
|
|
{ |
436
|
0
|
|
|
0
|
|
0
|
my ($class_or_obj, @in_paths) = @_; |
437
|
0
|
|
|
|
|
0
|
my $self = class_or_obj($class_or_obj); |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
# construct path lists and deduplicate |
440
|
0
|
|
|
|
|
0
|
my @out_path; |
441
|
|
|
|
|
|
|
my %path_seen; |
442
|
0
|
|
|
|
|
0
|
foreach my $dir (map {split /:/x, $_} @in_paths) { |
|
0
|
|
|
|
|
0
|
|
443
|
0
|
0
|
|
|
|
0
|
$self->debug() and print STDERR "debug: found $dir\n"; |
444
|
0
|
0
|
|
|
|
0
|
if ($dir eq "." ) { |
445
|
|
|
|
|
|
|
# omit "." for good security practice |
446
|
0
|
|
|
|
|
0
|
next; |
447
|
|
|
|
|
|
|
} |
448
|
|
|
|
|
|
|
# add the path if it hasn't already been seen, and it exists |
449
|
0
|
0
|
0
|
|
|
0
|
if (not exists $path_seen{$dir} and -d $dir) { |
450
|
0
|
|
|
|
|
0
|
push @out_path, $dir; |
451
|
0
|
0
|
|
|
|
0
|
$self->debug() and print STDERR "debug: pushed $dir\n"; |
452
|
|
|
|
|
|
|
} |
453
|
0
|
|
|
|
|
0
|
$path_seen{$dir} = 1; |
454
|
|
|
|
|
|
|
} |
455
|
0
|
|
|
|
|
0
|
return join ":", @out_path; |
456
|
|
|
|
|
|
|
} |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
# save library hints where user's local Perl modules go, observed in search/cleanup of paths |
459
|
|
|
|
|
|
|
sub _save_hint |
460
|
|
|
|
|
|
|
{ |
461
|
0
|
|
|
0
|
|
0
|
my ($item, $lib_hints_ref, $hints_seen_ref) = @_; |
462
|
0
|
0
|
|
|
|
0
|
if (not exists $hints_seen_ref->{$item}) { |
463
|
0
|
|
|
|
|
0
|
push @{$lib_hints_ref}, $item; |
|
0
|
|
|
|
|
0
|
|
464
|
0
|
|
|
|
|
0
|
$hints_seen_ref->{$item} = 1; |
465
|
|
|
|
|
|
|
} |
466
|
0
|
|
|
|
|
0
|
return; |
467
|
|
|
|
|
|
|
} |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
# more exhaustive search for user's local perl library directory |
470
|
|
|
|
|
|
|
sub user_perldir_search_loop |
471
|
|
|
|
|
|
|
{ |
472
|
0
|
|
|
0
|
0
|
0
|
my ($class_or_obj) = @_; |
473
|
0
|
|
|
|
|
0
|
my $self = class_or_obj($class_or_obj); |
474
|
|
|
|
|
|
|
|
475
|
0
|
0
|
|
|
|
0
|
if (not defined $self->sysenv("perlbase")) { |
476
|
0
|
|
|
|
|
0
|
DIRLOOP: foreach my $dirpath ($self->sysenv("home"), $self->sysenv("home")."/lib", |
477
|
|
|
|
|
|
|
$self->sysenv("home")."/.local") |
478
|
|
|
|
|
|
|
{ |
479
|
0
|
|
|
|
|
0
|
foreach my $perlname (qw(perl perl5)) { |
480
|
0
|
0
|
0
|
|
|
0
|
if (-d "$dirpath/$perlname" and -w "$dirpath/$perlname") { |
481
|
0
|
|
|
|
|
0
|
$self->sysenv("perlbase", $dirpath."/".$perlname); |
482
|
0
|
|
|
|
|
0
|
last DIRLOOP; |
483
|
|
|
|
|
|
|
} |
484
|
|
|
|
|
|
|
} |
485
|
|
|
|
|
|
|
} |
486
|
|
|
|
|
|
|
} |
487
|
0
|
|
|
|
|
0
|
return; |
488
|
|
|
|
|
|
|
} |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
# make sure directory path exists |
491
|
|
|
|
|
|
|
sub build_path |
492
|
|
|
|
|
|
|
{ |
493
|
0
|
|
|
0
|
0
|
0
|
my @path_parts = @_; |
494
|
0
|
|
|
|
|
0
|
my $need_path; |
495
|
0
|
|
|
|
|
0
|
foreach my $need_dir (@path_parts) { |
496
|
0
|
0
|
|
|
|
0
|
$need_path = (defined $need_path) ? "$need_path/$need_dir" : $need_dir; |
497
|
0
|
0
|
|
|
|
0
|
if (not -d $need_path) { |
498
|
3
|
|
|
3
|
|
11237
|
no autodie; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
12
|
|
499
|
0
|
0
|
|
|
|
0
|
mkdir $need_path, 0755 |
500
|
|
|
|
|
|
|
or return 0; # give up if we can't create the directory |
501
|
|
|
|
|
|
|
} |
502
|
|
|
|
|
|
|
} |
503
|
0
|
|
|
|
|
0
|
return 1; |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
# if the user's local perl library doesn't exist, see if we can create it |
507
|
|
|
|
|
|
|
sub user_perldir_create |
508
|
|
|
|
|
|
|
{ |
509
|
0
|
|
|
0
|
0
|
0
|
my ($class_or_obj) = @_; |
510
|
0
|
|
|
|
|
0
|
my $self = class_or_obj($class_or_obj); |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
# bail out on Win32 because XDG directory standard only applies to Unix-like systems |
513
|
0
|
0
|
0
|
|
|
0
|
if ($self->sysenv("os") eq "MSWin32" or $self->sysenv("os") eq "Win32") { |
514
|
0
|
|
|
|
|
0
|
return 0; |
515
|
|
|
|
|
|
|
} |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
# try to create an XDG-compatible perl library directory under .local |
518
|
0
|
0
|
|
|
|
0
|
if (not defined $self->sysenv("perlbase")) { |
519
|
|
|
|
|
|
|
# use a default that complies with XDG directory structure |
520
|
0
|
0
|
|
|
|
0
|
if (build_path($self->sysenv("home"), ".local", "perl")) { |
521
|
0
|
|
|
|
|
0
|
$self->sysenv("perlbase", $self->sysenv("home")."/.local/perl"); |
522
|
|
|
|
|
|
|
} |
523
|
|
|
|
|
|
|
} |
524
|
0
|
|
|
|
|
0
|
build_path($self->sysenv("perlbase"), "lib", "perl5"); |
525
|
0
|
|
|
|
|
0
|
return; |
526
|
|
|
|
|
|
|
} |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
# find or create user's local Perl directory |
529
|
|
|
|
|
|
|
sub user_perldir_search |
530
|
|
|
|
|
|
|
{ |
531
|
0
|
|
|
0
|
0
|
0
|
my ($class_or_obj) = @_; |
532
|
0
|
|
|
|
|
0
|
my $self = class_or_obj($class_or_obj); |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
# use environment variables to look for user's Perl library |
535
|
0
|
|
|
|
|
0
|
my @lib_hints; |
536
|
|
|
|
|
|
|
my %hints_seen; |
537
|
0
|
|
|
|
|
0
|
my $home = $self->sysenv("home"); |
538
|
0
|
0
|
|
|
|
0
|
if (exists $ENV{PERL_LOCAL_LIB_ROOT}) { |
539
|
0
|
|
|
|
|
0
|
foreach my $item (split /:/x, $ENV{PERL_LOCAL_LIB_ROOT}) { |
540
|
0
|
0
|
|
|
|
0
|
if ($item =~ qr(^$home/)x) { |
541
|
0
|
|
|
|
|
0
|
$item =~ s=/$==x; # remove trailing slash if present |
542
|
0
|
|
|
|
|
0
|
_save_hint($item, \@lib_hints, \%hints_seen); |
543
|
|
|
|
|
|
|
} |
544
|
|
|
|
|
|
|
} |
545
|
|
|
|
|
|
|
} |
546
|
0
|
0
|
|
|
|
0
|
if (exists $ENV{PERL5LIB}) { |
547
|
0
|
|
|
|
|
0
|
foreach my $item (split /:/x, $ENV{PERL5LIB}) { |
548
|
0
|
0
|
|
|
|
0
|
if ($item =~ qr(^$home/)x) { |
549
|
0
|
|
|
|
|
0
|
$item =~ s=/$==x; # remove trailing slash if present |
550
|
0
|
|
|
|
|
0
|
$item =~ s=/[^/]+$==x; # remove last directory from path |
551
|
0
|
|
|
|
|
0
|
_save_hint($item, \@lib_hints, \%hints_seen); |
552
|
|
|
|
|
|
|
} |
553
|
|
|
|
|
|
|
} |
554
|
|
|
|
|
|
|
} |
555
|
0
|
0
|
|
|
|
0
|
if (exists $ENV{PATH}) { |
556
|
0
|
|
|
|
|
0
|
foreach my $item (split /:/x, $ENV{PATH}) { |
557
|
0
|
0
|
0
|
|
|
0
|
if ($item =~ qr(^$home/)x and $item =~ qr(/perl[5]?/)x) { |
558
|
0
|
|
|
|
|
0
|
$item =~ s=/$==x; # remove trailing slash if present |
559
|
0
|
|
|
|
|
0
|
$item =~ s=/[^/]+$==x; # remove last directory from path |
560
|
0
|
|
|
|
|
0
|
_save_hint($item, \@lib_hints, \%hints_seen); |
561
|
|
|
|
|
|
|
} |
562
|
|
|
|
|
|
|
} |
563
|
|
|
|
|
|
|
} |
564
|
0
|
|
|
|
|
0
|
foreach my $dirpath (@lib_hints) { |
565
|
0
|
0
|
0
|
|
|
0
|
if (-d $dirpath and -w $dirpath) { |
566
|
0
|
|
|
|
|
0
|
$self->sysenv("perlbase", $dirpath); |
567
|
0
|
|
|
|
|
0
|
last; |
568
|
|
|
|
|
|
|
} |
569
|
|
|
|
|
|
|
} |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
# more exhaustive search for user's local perl library directory |
572
|
0
|
|
|
|
|
0
|
$self->user_perldir_search_loop(); |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
# if the user's local perl library doesn't exist, create it |
575
|
0
|
|
|
|
|
0
|
$self->user_perldir_create(); |
576
|
0
|
|
|
|
|
0
|
return; |
577
|
|
|
|
|
|
|
} |
578
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
# set up user library and environment variables |
580
|
|
|
|
|
|
|
# this is called for non-root users |
581
|
|
|
|
|
|
|
sub set_user_env |
582
|
|
|
|
|
|
|
{ |
583
|
0
|
|
|
0
|
0
|
0
|
my ($class_or_obj) = @_; |
584
|
0
|
|
|
|
|
0
|
my $self = class_or_obj($class_or_obj); |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
# find or create library under home directory |
587
|
0
|
0
|
|
|
|
0
|
if (exists $ENV{HOME}) { |
588
|
0
|
|
|
|
|
0
|
$self->sysenv("home", $ENV{HOME}); |
589
|
|
|
|
|
|
|
} |
590
|
0
|
|
|
|
|
0
|
$self->user_perldir_search(); |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
# |
593
|
|
|
|
|
|
|
# set user environment variables similar to local::lib |
594
|
|
|
|
|
|
|
# |
595
|
|
|
|
|
|
|
{ |
596
|
|
|
|
|
|
|
# allow environment variables to be set without "local" in this block - this updates them for child processes |
597
|
|
|
|
|
|
|
## no critic (Variables::RequireLocalizedPunctuationVars) |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
# update PATH |
600
|
0
|
0
|
|
|
|
0
|
if (exists $ENV{PATH}) { |
|
0
|
|
|
|
|
0
|
|
601
|
0
|
|
|
|
|
0
|
$ENV{PATH} = $self->_dedup_path($ENV{PATH}, $self->sysenv("perlbase")."/bin"); |
602
|
|
|
|
|
|
|
} else { |
603
|
0
|
|
|
|
|
0
|
$ENV{PATH} = $self->_dedup_path("/usr/bin:/bin", $self->sysenv("perlbase")."/bin", "/usr/local/bin"); |
604
|
|
|
|
|
|
|
} |
605
|
|
|
|
|
|
|
|
606
|
|
|
|
|
|
|
# because we modified PATH: remove path cache/flags and force them to be regenerated |
607
|
0
|
|
|
|
|
0
|
delete $self->{sysenv}{path_list}; |
608
|
0
|
|
|
|
|
0
|
delete $self->{sysenv}{path_flag}; |
609
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
# update PERL5LIB |
611
|
0
|
0
|
|
|
|
0
|
if (exists $ENV{PERL5LIB}) { |
612
|
0
|
|
|
|
|
0
|
$ENV{PERL5LIB} = $self->_dedup_path($ENV{PERL5LIB}, $self->sysenv("perlbase")."/lib/perl5"); |
613
|
|
|
|
|
|
|
} else { |
614
|
0
|
|
|
|
|
0
|
$ENV{PERL5LIB} = $self->_dedup_path(@INC, $self->sysenv("perlbase")."/lib/perl5"); |
615
|
|
|
|
|
|
|
} |
616
|
|
|
|
|
|
|
|
617
|
|
|
|
|
|
|
# update PERL_LOCAL_LIB_ROOT/PERL_MB_OPT/PERL_MM_OPT for local::lib |
618
|
0
|
0
|
|
|
|
0
|
if (exists $ENV{PERL_LOCAL_LIB_ROOT}) { |
619
|
0
|
|
|
|
|
0
|
$ENV{PERL_LOCAL_LIB_ROOT} = $self->_dedup_path($ENV{PERL_LOCAL_LIB_ROOT}, $self->sysenv("perlbase")); |
620
|
|
|
|
|
|
|
} else { |
621
|
0
|
|
|
|
|
0
|
$ENV{PERL_LOCAL_LIB_ROOT} = $self->sysenv("perlbase"); |
622
|
|
|
|
|
|
|
} |
623
|
|
|
|
|
|
|
{ |
624
|
|
|
|
|
|
|
## no critic (Variables::RequireLocalizedPunctuationVars) |
625
|
0
|
|
|
|
|
0
|
$ENV{PERL_MB_OPT} = '--install_base "'.$self->sysenv("perlbase").'"'; |
|
0
|
|
|
|
|
0
|
|
626
|
0
|
|
|
|
|
0
|
$ENV{PERL_MM_OPT} = 'INSTALL_BASE='.$self->sysenv("perlbase"); |
627
|
|
|
|
|
|
|
} |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
# update MANPATH |
630
|
0
|
0
|
|
|
|
0
|
if (exists $ENV{MANPATH}) { |
631
|
0
|
|
|
|
|
0
|
$ENV{MANPATH} = $self->_dedup_path($ENV{MANPATH}, $self->sysenv("perlbase")."/man"); |
632
|
|
|
|
|
|
|
} else { |
633
|
0
|
|
|
|
|
0
|
$ENV{MANPATH} = $self->_dedup_path("usr/share/man", $self->sysenv("perlbase")."/man", "/usr/local/share/man"); |
634
|
|
|
|
|
|
|
} |
635
|
|
|
|
|
|
|
} |
636
|
|
|
|
|
|
|
|
637
|
|
|
|
|
|
|
# display updated environment variables |
638
|
0
|
0
|
|
|
|
0
|
if (not $self->quiet()) { |
639
|
0
|
|
|
|
|
0
|
print "using environment settings: (add these to login shell rc script if needed)\n"; |
640
|
0
|
|
|
|
|
0
|
print "".('-' x 75)."\n"; |
641
|
0
|
|
|
|
|
0
|
foreach my $varname (qw(PATH PERL5LIB PERL_LOCAL_LIB_ROOT PERL_MB_OPT PERL_MM_OPT MANPATH)) { |
642
|
0
|
|
|
|
|
0
|
print "export $varname=$ENV{$varname}\n"; |
643
|
|
|
|
|
|
|
} |
644
|
0
|
|
|
|
|
0
|
print "".('-' x 75)."\n"; |
645
|
0
|
|
|
|
|
0
|
print "\n"; |
646
|
|
|
|
|
|
|
} |
647
|
0
|
|
|
|
|
0
|
return; |
648
|
|
|
|
|
|
|
} |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
# collect info and deduce platform type |
651
|
|
|
|
|
|
|
sub resolve_platform |
652
|
|
|
|
|
|
|
{ |
653
|
2
|
|
|
2
|
0
|
6
|
my ($class_or_obj) = @_; |
654
|
2
|
|
|
|
|
4
|
my $self = class_or_obj($class_or_obj); |
655
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
# collect uname info |
657
|
2
|
|
|
|
|
14
|
my $uname = $self->sysenv("uname"); |
658
|
2
|
50
|
|
|
|
8
|
if (defined $uname) { |
659
|
|
|
|
|
|
|
# Unix-like systems all have uname |
660
|
2
|
|
|
|
|
6
|
$self->sysenv("os", $self->capture_cmd($uname, "-s")); |
661
|
2
|
|
|
|
|
23
|
$self->sysenv("kernel", $self->capture_cmd($uname, "-r")); |
662
|
2
|
|
|
|
|
25
|
$self->sysenv("machine", $self->capture_cmd($uname, "-m")); |
663
|
|
|
|
|
|
|
} else { |
664
|
|
|
|
|
|
|
# if the platform doesn't have uname (mainly Windows), get what we can from the Perl configuration |
665
|
0
|
|
|
|
|
0
|
$self->sysenv("os", $Config{osname}); |
666
|
0
|
|
|
|
|
0
|
$self->sysenv("machine", $Config{archname}); |
667
|
|
|
|
|
|
|
} |
668
|
|
|
|
|
|
|
|
669
|
|
|
|
|
|
|
# initialize Sys::OsRelease and set platform type |
670
|
2
|
|
|
|
|
26
|
my $osrelease = Sys::OsRelease->instance(common_id => sysconf("common_id")); |
671
|
2
|
|
|
|
|
3053
|
$self->sysenv("platform", $osrelease->platform()); |
672
|
|
|
|
|
|
|
|
673
|
|
|
|
|
|
|
# determine system's packager if possible |
674
|
2
|
|
|
|
|
16
|
my $plat_packager = $self->platconf("packager"); |
675
|
2
|
50
|
|
|
|
12
|
if (defined $plat_packager) { |
676
|
2
|
|
|
|
|
5
|
$self->sysenv("packager", $plat_packager); |
677
|
|
|
|
|
|
|
} |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
# display system info |
680
|
2
|
|
|
|
|
10
|
my $detected; |
681
|
2
|
50
|
|
|
|
9
|
if (defined $osrelease->osrelease_path()) { |
682
|
2
|
50
|
|
|
|
37
|
if ($self->platform() eq $osrelease->id()) { |
683
|
0
|
|
|
|
|
0
|
$detected = $self->platform(); |
684
|
|
|
|
|
|
|
} else { |
685
|
2
|
|
|
|
|
27
|
$detected = $osrelease->id()." -> ".$self->platform(); |
686
|
|
|
|
|
|
|
} |
687
|
2
|
50
|
|
|
|
6
|
if (defined $self->sysenv("packager")) { |
688
|
2
|
|
|
|
|
5
|
$detected .= " handled by ".$self->sysenv("packager"); |
689
|
|
|
|
|
|
|
} |
690
|
|
|
|
|
|
|
|
691
|
|
|
|
|
|
|
} else { |
692
|
0
|
|
|
|
|
0
|
$detected = $self->platform()." (no os-release data)"; |
693
|
|
|
|
|
|
|
} |
694
|
2
|
50
|
|
|
|
9
|
if (not $self->quiet()) { |
695
|
0
|
|
|
|
|
0
|
print $self->text_green()."system detected: $detected".$self->text_color_reset()."\n"; |
696
|
|
|
|
|
|
|
} |
697
|
2
|
|
|
|
|
16
|
return; |
698
|
|
|
|
|
|
|
} |
699
|
|
|
|
|
|
|
|
700
|
|
|
|
|
|
|
# collect system environment info |
701
|
|
|
|
|
|
|
sub collect_sysenv |
702
|
|
|
|
|
|
|
{ |
703
|
2
|
|
|
2
|
0
|
5
|
my ($class_or_obj) = @_; |
704
|
2
|
|
|
|
|
6
|
my $self = class_or_obj($class_or_obj); |
705
|
2
|
|
|
|
|
6
|
my $sysenv = $self->{sysenv}; |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
# find command locations |
708
|
2
|
|
|
|
|
18
|
foreach my $cmd (@{sysconf("search_cmds")}) { |
|
2
|
|
|
|
|
5
|
|
709
|
32
|
100
|
|
|
|
62
|
if (my $filepath = $self->cmd_path($cmd)) { |
710
|
14
|
|
|
|
|
47
|
$sysenv->{$cmd} = $filepath; |
711
|
|
|
|
|
|
|
} |
712
|
|
|
|
|
|
|
} |
713
|
2
|
|
|
|
|
164
|
$sysenv->{perl} = $Config{perlpath}; |
714
|
|
|
|
|
|
|
|
715
|
|
|
|
|
|
|
# collect info and deduce platform type |
716
|
2
|
|
|
|
|
13
|
$self->resolve_platform(); |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
# check if user is root |
719
|
2
|
50
|
|
|
|
40
|
if ($> == 0) { |
720
|
|
|
|
|
|
|
# set the flag to indicate they are root |
721
|
2
|
|
|
|
|
38
|
$sysenv->{root} = 1; |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
# on Alpine, refresh the package data |
724
|
2
|
50
|
|
|
|
11
|
if (exists $sysenv->{apk}) { |
725
|
0
|
|
|
|
|
0
|
$self->run_cmd($sysenv->{apk}, "update"); |
726
|
|
|
|
|
|
|
} |
727
|
|
|
|
|
|
|
} else { |
728
|
|
|
|
|
|
|
# set user environment variables as necessary (similar to local::lib but without that as a dependency) |
729
|
0
|
|
|
|
|
0
|
$self->set_user_env(); |
730
|
|
|
|
|
|
|
} |
731
|
|
|
|
|
|
|
|
732
|
|
|
|
|
|
|
# debug dump |
733
|
2
|
50
|
|
|
|
12
|
if ($self->debug()) { |
734
|
0
|
|
|
|
|
0
|
print STDERR "debug: sysenv:\n"; |
735
|
0
|
|
|
|
|
0
|
foreach my $key (sort keys %$sysenv) { |
736
|
0
|
0
|
|
|
|
0
|
if (ref $sysenv->{$key} eq "ARRAY") { |
737
|
0
|
|
|
|
|
0
|
print STDERR " $key => [".join(" ", @{$sysenv->{$key}})."]\n"; |
|
0
|
|
|
|
|
0
|
|
738
|
|
|
|
|
|
|
} else { |
739
|
0
|
0
|
|
|
|
0
|
print STDERR " $key => ".(exists $sysenv->{$key} ? $sysenv->{$key} : "(undef)")."\n"; |
740
|
|
|
|
|
|
|
} |
741
|
|
|
|
|
|
|
} |
742
|
|
|
|
|
|
|
} |
743
|
2
|
|
|
|
|
11
|
return; |
744
|
|
|
|
|
|
|
} |
745
|
|
|
|
|
|
|
|
746
|
|
|
|
|
|
|
# run an external command |
747
|
|
|
|
|
|
|
sub run_cmd |
748
|
|
|
|
|
|
|
{ |
749
|
0
|
|
|
0
|
0
|
0
|
my ($class_or_obj, @cmd) = @_; |
750
|
0
|
|
|
|
|
0
|
my $self = class_or_obj($class_or_obj); |
751
|
|
|
|
|
|
|
|
752
|
0
|
0
|
|
|
|
0
|
$self->debug() and print STDERR "debug(run_cmd): ".join(" ", @cmd)."\n"; |
753
|
|
|
|
|
|
|
{ |
754
|
3
|
|
|
3
|
|
12378
|
no autodie; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
13
|
|
|
0
|
|
|
|
|
0
|
|
755
|
0
|
|
|
|
|
0
|
system @cmd; |
756
|
|
|
|
|
|
|
} |
757
|
0
|
0
|
|
|
|
0
|
if ($? == -1) { |
|
|
0
|
|
|
|
|
|
758
|
0
|
|
|
|
|
0
|
print STDERR "failed to execute '".(join " ", @cmd)."': $!\n"; |
759
|
0
|
|
|
|
|
0
|
exit 1; |
760
|
|
|
|
|
|
|
} elsif ($? & 127) { |
761
|
0
|
0
|
|
|
|
0
|
printf STDERR "child '".(join " ", @cmd)."' died with signal %d, %s coredump\n", |
762
|
|
|
|
|
|
|
($? & 127), ($? & 128) ? 'with' : 'without'; |
763
|
0
|
|
|
|
|
0
|
exit 1; |
764
|
|
|
|
|
|
|
} else { |
765
|
0
|
|
|
|
|
0
|
my $retval = $? >> 8; |
766
|
0
|
0
|
|
|
|
0
|
if ($retval != 0) { |
767
|
0
|
|
|
|
|
0
|
printf STDERR "child '".(join " ", @cmd)."' exited with value %d\n", $? >> 8; |
768
|
0
|
|
|
|
|
0
|
return 0; |
769
|
|
|
|
|
|
|
} |
770
|
|
|
|
|
|
|
} |
771
|
|
|
|
|
|
|
|
772
|
|
|
|
|
|
|
# it gets here if it succeeded |
773
|
0
|
|
|
|
|
0
|
return 1; |
774
|
|
|
|
|
|
|
} |
775
|
|
|
|
|
|
|
|
776
|
|
|
|
|
|
|
# check if the user is root - if so, return true |
777
|
|
|
|
|
|
|
sub is_root |
778
|
|
|
|
|
|
|
{ |
779
|
0
|
|
|
0
|
0
|
0
|
my ($class_or_obj) = @_; |
780
|
0
|
|
|
|
|
0
|
my $self = class_or_obj($class_or_obj); |
781
|
|
|
|
|
|
|
|
782
|
0
|
|
|
|
|
0
|
return ($self->sysenv("root") != 0); |
783
|
|
|
|
|
|
|
} |
784
|
|
|
|
|
|
|
|
785
|
|
|
|
|
|
|
# handle various systems' packagers |
786
|
|
|
|
|
|
|
# op parameter is a string: |
787
|
|
|
|
|
|
|
# implemented: 1 if packager implemented for this system, otherwise undef |
788
|
|
|
|
|
|
|
# pkgcmd: 1 if packager command found, 0 if not found |
789
|
|
|
|
|
|
|
# modpkg(module): find name of package for Perl module |
790
|
|
|
|
|
|
|
# find(pkg): 1 if named package exists, 0 if not |
791
|
|
|
|
|
|
|
# install(pkg): 0 = failure, 1 = success |
792
|
|
|
|
|
|
|
# returns undef if not implemented |
793
|
|
|
|
|
|
|
# for ops which return a numeric status: 0 = failure, 1 = success |
794
|
|
|
|
|
|
|
# some ops return a value such as query results |
795
|
|
|
|
|
|
|
sub call_pkg_driver |
796
|
|
|
|
|
|
|
{ |
797
|
5
|
|
|
5
|
0
|
43
|
my ($class_or_obj, %args) = @_; |
798
|
5
|
|
|
|
|
11
|
my $self = class_or_obj($class_or_obj); |
799
|
|
|
|
|
|
|
|
800
|
5
|
50
|
|
|
|
27
|
if (not exists $args{op}) { |
801
|
0
|
|
|
|
|
0
|
croak "call_pkg_driver() requires op parameter"; |
802
|
|
|
|
|
|
|
} |
803
|
|
|
|
|
|
|
|
804
|
|
|
|
|
|
|
# check if packager is implemented for currently-running system |
805
|
5
|
50
|
|
|
|
24
|
if ($args{op} eq "implemented") { |
806
|
0
|
0
|
|
|
|
0
|
if ($self->sysenv("os") eq "Linux") { |
807
|
0
|
0
|
|
|
|
0
|
if (not defined $self->platform()) { |
808
|
|
|
|
|
|
|
# for Linux packagers, we need ID to tell distros apart - all modern distros should provide one |
809
|
0
|
|
|
|
|
0
|
return; |
810
|
|
|
|
|
|
|
} |
811
|
0
|
0
|
|
|
|
0
|
if (not defined $self->packager()) { |
812
|
|
|
|
|
|
|
# it gets here on Linux distros which we don't have a packager implementation |
813
|
0
|
|
|
|
|
0
|
return; |
814
|
|
|
|
|
|
|
} |
815
|
|
|
|
|
|
|
} else { |
816
|
|
|
|
|
|
|
# add handlers for more packagers as they are implemented |
817
|
0
|
|
|
|
|
0
|
return; |
818
|
|
|
|
|
|
|
} |
819
|
0
|
|
|
|
|
0
|
return 1; |
820
|
|
|
|
|
|
|
} |
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
# if a pkg parameter is present, apply package name override if one is configured |
823
|
5
|
50
|
33
|
|
|
27
|
if (exists $args{pkg} and $self->pkg_override($args{pkg})) { |
824
|
0
|
|
|
|
|
0
|
$args{pkg} = $self->pkg_override($args{pkg}); |
825
|
|
|
|
|
|
|
} |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
# if a module parameter is present, add mod_parts parameter |
828
|
5
|
50
|
|
|
|
13
|
if (exists $args{module}) { |
829
|
0
|
|
|
|
|
0
|
$args{mod_parts} = [split /::/x, $args{module}]; |
830
|
|
|
|
|
|
|
} |
831
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
# look up function which implements op for package type |
833
|
|
|
|
|
|
|
## no critic (BuiltinFunctions::ProhibitStringyEval) # need stringy eval to load a class from a string |
834
|
5
|
50
|
|
|
|
14
|
if (not eval "require ".$self->packager()) { |
835
|
0
|
|
|
|
|
0
|
croak "failed to load driver class ".$self->packager(); |
836
|
|
|
|
|
|
|
} |
837
|
|
|
|
|
|
|
## use critic (BuiltinFunctions::ProhibitStringyEval) |
838
|
5
|
|
|
|
|
34
|
my $funcname = $self->packager()."::".$args{op}; |
839
|
5
|
50
|
|
|
|
17
|
$self->debug() and print STDERR "debug: $funcname(".join(" ", map {$_."=".$args{$_}} sort keys %args).")\n"; |
|
0
|
|
|
|
|
0
|
|
840
|
5
|
|
|
|
|
10
|
my $funcref = $self->packager()->can($args{op}); |
841
|
5
|
50
|
|
|
|
14
|
if (not defined $funcref) { |
842
|
|
|
|
|
|
|
# not implemented - subroutine name not found in driver class |
843
|
0
|
0
|
|
|
|
0
|
$self->debug() and print STDERR "debug: $funcname not implemented\n"; |
844
|
0
|
|
|
|
|
0
|
return; |
845
|
|
|
|
|
|
|
} |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
# call the function with parameters: driver class (class method call), Sys::OsPackage instance, arguments |
848
|
5
|
|
|
|
|
10
|
return $funcref->($self->packager(), $self, \%args); |
849
|
|
|
|
|
|
|
} |
850
|
|
|
|
|
|
|
|
851
|
|
|
|
|
|
|
# return string to turn text green |
852
|
|
|
|
|
|
|
sub text_green |
853
|
|
|
|
|
|
|
{ |
854
|
0
|
|
|
0
|
0
|
|
my ($class_or_obj) = @_; |
855
|
0
|
|
|
|
|
|
my $self = class_or_obj($class_or_obj); |
856
|
|
|
|
|
|
|
|
857
|
0
|
0
|
|
|
|
|
$self->module_installed('Term::ANSIColor') or return ""; |
858
|
0
|
|
|
|
|
|
require Term::ANSIColor; |
859
|
0
|
|
|
|
|
|
return Term::ANSIColor::color('green'); |
860
|
|
|
|
|
|
|
} |
861
|
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
# return string to turn text back to normal |
863
|
|
|
|
|
|
|
sub text_color_reset |
864
|
|
|
|
|
|
|
{ |
865
|
0
|
|
|
0
|
0
|
|
my ($class_or_obj) = @_; |
866
|
0
|
|
|
|
|
|
my $self = class_or_obj($class_or_obj); |
867
|
|
|
|
|
|
|
|
868
|
0
|
0
|
|
|
|
|
$self->module_installed('Term::ANSIColor') or return ""; |
869
|
0
|
|
|
|
|
|
require Term::ANSIColor; |
870
|
0
|
|
|
|
|
|
return Term::ANSIColor::color('reset'); |
871
|
|
|
|
|
|
|
} |
872
|
|
|
|
|
|
|
|
873
|
|
|
|
|
|
|
# install a Perl module as an OS package |
874
|
|
|
|
|
|
|
sub module_package |
875
|
|
|
|
|
|
|
{ |
876
|
0
|
|
|
0
|
0
|
|
my ($class_or_obj, $module) = @_; |
877
|
0
|
|
|
|
|
|
my $self = class_or_obj($class_or_obj); |
878
|
|
|
|
|
|
|
|
879
|
|
|
|
|
|
|
# check if we can install a package |
880
|
0
|
0
|
|
|
|
|
if (not $self->is_root()) { |
881
|
|
|
|
|
|
|
# must be root to install an OS package |
882
|
0
|
|
|
|
|
|
return 0; |
883
|
|
|
|
|
|
|
} |
884
|
0
|
0
|
|
|
|
|
if (not $self->call_pkg_driver(op => "implemented")) { |
885
|
0
|
|
|
|
|
|
return 0; |
886
|
|
|
|
|
|
|
} |
887
|
|
|
|
|
|
|
|
888
|
|
|
|
|
|
|
# handle various package managers |
889
|
0
|
|
|
|
|
|
my $pkgname = $self->call_pkg_driver(op => "modpkg", module => $module); |
890
|
0
|
0
|
0
|
|
|
|
return 0 if (not defined $pkgname) or length($pkgname) == 0; |
891
|
0
|
0
|
|
|
|
|
if (not $self->quiet()) { |
892
|
0
|
|
|
|
|
|
print "\n"; |
893
|
0
|
|
|
|
|
|
print $self->text_green()."install $pkgname for $module using ".$self->sysenv("packager") |
894
|
|
|
|
|
|
|
.$self->text_color_reset()."\n"; |
895
|
|
|
|
|
|
|
} |
896
|
|
|
|
|
|
|
|
897
|
0
|
|
|
|
|
|
return $self->call_pkg_driver(op => "install", pkg => $pkgname); |
898
|
|
|
|
|
|
|
} |
899
|
|
|
|
|
|
|
|
900
|
|
|
|
|
|
|
# check if OS package is installed |
901
|
|
|
|
|
|
|
sub pkg_installed |
902
|
|
|
|
|
|
|
{ |
903
|
0
|
|
|
0
|
0
|
|
my ($class_or_obj, $pkgname) = @_; |
904
|
0
|
|
|
|
|
|
my $self = class_or_obj($class_or_obj); |
905
|
|
|
|
|
|
|
|
906
|
0
|
0
|
0
|
|
|
|
return 0 if (not defined $pkgname) or length($pkgname) == 0; |
907
|
0
|
|
|
|
|
|
return $self->call_pkg_driver(op => "is_installed", pkg => $pkgname); |
908
|
|
|
|
|
|
|
} |
909
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
# check if module is installed, and install it if not present |
911
|
|
|
|
|
|
|
sub install_module |
912
|
|
|
|
|
|
|
{ |
913
|
0
|
|
|
0
|
0
|
|
my ($class_or_obj, $name) = @_; |
914
|
0
|
|
|
|
|
|
my $self = class_or_obj($class_or_obj); |
915
|
|
|
|
|
|
|
|
916
|
|
|
|
|
|
|
# check if module is installed |
917
|
0
|
0
|
|
|
|
|
if (not $self->module_installed($name)) { |
918
|
|
|
|
|
|
|
# print header for module installation |
919
|
0
|
0
|
|
|
|
|
if (not $self->quiet()) { |
920
|
0
|
|
|
|
|
|
print $self->text_green().('-' x 75)."\n"; |
921
|
0
|
|
|
|
|
|
print "install $name".$self->text_color_reset()."\n"; |
922
|
|
|
|
|
|
|
} |
923
|
|
|
|
|
|
|
|
924
|
|
|
|
|
|
|
# try first to install it with an OS package (root required) |
925
|
0
|
|
|
|
|
|
my $done=0; |
926
|
0
|
0
|
|
|
|
|
if ($self->is_root()) { |
927
|
0
|
0
|
|
|
|
|
if ($self->module_package($name)) { |
928
|
0
|
|
|
|
|
|
$self->module_installed($name, 1); |
929
|
0
|
|
|
|
|
|
$done=1; |
930
|
|
|
|
|
|
|
} |
931
|
|
|
|
|
|
|
} |
932
|
|
|
|
|
|
|
|
933
|
|
|
|
|
|
|
# try again with CPAN or CPANMinus if it wasn't installed by a package |
934
|
0
|
0
|
|
|
|
|
if (not $done) { |
935
|
0
|
0
|
|
|
|
|
my $cmd = (defined $self->sysenv("cpan") ? $self->sysenv("cpan") : $self->sysenv("cpanm")); |
936
|
0
|
0
|
|
|
|
|
$self->run_cmd($cmd, $name) |
937
|
|
|
|
|
|
|
or croak "failed to install $name module"; |
938
|
0
|
|
|
|
|
|
$self->module_installed($name, 1); |
939
|
|
|
|
|
|
|
} |
940
|
|
|
|
|
|
|
} |
941
|
0
|
|
|
|
|
|
return; |
942
|
|
|
|
|
|
|
} |
943
|
|
|
|
|
|
|
|
944
|
|
|
|
|
|
|
# bootstrap CPAN-Minus in a subdirectory of the current directory |
945
|
|
|
|
|
|
|
sub bootstrap_cpanm |
946
|
|
|
|
|
|
|
{ |
947
|
0
|
|
|
0
|
0
|
|
my ($class_or_obj) = @_; |
948
|
0
|
|
|
|
|
|
my $self = class_or_obj($class_or_obj); |
949
|
|
|
|
|
|
|
|
950
|
|
|
|
|
|
|
# save current directory |
951
|
0
|
|
|
|
|
|
my $old_pwd = $self->pwd(); |
952
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
# make build directory and change into it |
954
|
0
|
0
|
|
|
|
|
if (not -d "build") { |
955
|
3
|
|
|
3
|
|
10780
|
no autodie; |
|
3
|
|
|
|
|
13
|
|
|
3
|
|
|
|
|
11
|
|
956
|
0
|
0
|
|
|
|
|
mkdir "build", 0755 |
957
|
|
|
|
|
|
|
or croak "can't make build directory in current directory: $!"; |
958
|
|
|
|
|
|
|
} |
959
|
0
|
|
|
|
|
|
chdir "build"; |
960
|
|
|
|
|
|
|
|
961
|
|
|
|
|
|
|
# verify required commands are present |
962
|
0
|
|
|
|
|
|
my @missing; |
963
|
0
|
|
|
|
|
|
foreach my $cmd (@{perlconf("cpan_deps")}) { |
|
0
|
|
|
|
|
|
|
964
|
0
|
0
|
|
|
|
|
if (not defined $self->sysenv("$cmd")) { |
965
|
0
|
|
|
|
|
|
push @missing, $cmd; |
966
|
|
|
|
|
|
|
} |
967
|
|
|
|
|
|
|
} |
968
|
0
|
0
|
|
|
|
|
if (scalar @missing > 0) { |
969
|
0
|
|
|
|
|
|
croak "missing ".(join ", ", @missing)." command - can't bootstrap cpanm"; |
970
|
|
|
|
|
|
|
} |
971
|
|
|
|
|
|
|
|
972
|
|
|
|
|
|
|
# download cpanm |
973
|
0
|
|
|
|
|
|
my $perl_sources = perlconf("sources"); |
974
|
|
|
|
|
|
|
$self->run_cmd($self->sysenv("curl"), "-L", "--output", "app-cpanminus.tar.gz", |
975
|
0
|
0
|
|
|
|
|
$perl_sources->{"App::cpanminus"} |
976
|
|
|
|
|
|
|
) |
977
|
|
|
|
|
|
|
or croak "download failed for App::cpanminus"; |
978
|
0
|
|
|
|
|
|
my @cpanm_path = grep {qr(/bin/cpanm$)x} ($self->capture_cmd({list=>1}, $self->sysenv("tar"), |
|
0
|
|
|
|
|
|
|
979
|
|
|
|
|
|
|
qw(-tf app-cpanminus.tar.gz))); |
980
|
0
|
|
|
|
|
|
my $cpanm_path = pop @cpanm_path; |
981
|
0
|
|
|
|
|
|
$self->run_cmd($self->sysenv("tar"), "-xf", "app-cpanminus.tar.gz", $cpanm_path); |
982
|
|
|
|
|
|
|
{ |
983
|
3
|
|
|
3
|
|
7675
|
no autodie; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
9
|
|
|
0
|
|
|
|
|
|
|
984
|
0
|
0
|
|
|
|
|
chmod 0755, $cpanm_path |
985
|
|
|
|
|
|
|
or croak "failed to chmod $cpanm_path:$!"; |
986
|
|
|
|
|
|
|
} |
987
|
0
|
|
|
|
|
|
$self->sysenv("cpanm", $self->pwd()."/".$cpanm_path); |
988
|
|
|
|
|
|
|
|
989
|
|
|
|
|
|
|
# change back up to previous directory |
990
|
0
|
|
|
|
|
|
chdir $old_pwd; |
991
|
0
|
|
|
|
|
|
return; |
992
|
|
|
|
|
|
|
} |
993
|
|
|
|
|
|
|
|
994
|
|
|
|
|
|
|
# establish CPAN if not already present |
995
|
|
|
|
|
|
|
sub establish_cpan |
996
|
|
|
|
|
|
|
{ |
997
|
0
|
|
|
0
|
0
|
|
my ($class_or_obj) = @_; |
998
|
0
|
|
|
|
|
|
my $self = class_or_obj($class_or_obj); |
999
|
|
|
|
|
|
|
|
1000
|
|
|
|
|
|
|
# first get package dependencies for CPAN (and CPAN too if available via OS package) |
1001
|
0
|
0
|
|
|
|
|
if ($self->is_root()) { |
1002
|
|
|
|
|
|
|
# package dependencies for CPAN (i.e. make, or oddly-named OS package that contains CPAN) |
1003
|
0
|
|
|
|
|
|
my @deps = $self->cpan_prereqs(); |
1004
|
0
|
|
|
|
|
|
$self->call_pkg_driver(op => "install", pkg => \@deps); |
1005
|
|
|
|
|
|
|
|
1006
|
|
|
|
|
|
|
# check for commands which were installed by their package name, and specifically look for cpan by any package |
1007
|
0
|
|
|
|
|
|
foreach my $dep (@deps, "cpan") { |
1008
|
0
|
0
|
|
|
|
|
if (my $filepath = $self->cmd_path($dep)) { |
1009
|
0
|
|
|
|
|
|
$self->sysenv($dep, $filepath); |
1010
|
|
|
|
|
|
|
} |
1011
|
|
|
|
|
|
|
} |
1012
|
|
|
|
|
|
|
} |
1013
|
|
|
|
|
|
|
|
1014
|
|
|
|
|
|
|
# install CPAN-Minus if neither CPAN nor CPAN-Minus exist |
1015
|
0
|
0
|
0
|
|
|
|
if (not defined $self->sysenv("cpan") and not defined $self->sysenv("cpanm")) { |
1016
|
|
|
|
|
|
|
# try to install CPAN-Minus as an OS package |
1017
|
0
|
0
|
|
|
|
|
if ($self->is_root()) { |
1018
|
0
|
0
|
|
|
|
|
if ($self->module_package("App::cpanminus")) { |
1019
|
0
|
|
|
|
|
|
$self->sysenv("cpanm", $self->cmd_path("cpanm")); |
1020
|
|
|
|
|
|
|
} |
1021
|
|
|
|
|
|
|
} |
1022
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
# try again if it wasn't installed by a package |
1024
|
0
|
0
|
|
|
|
|
if (not defined $self->sysenv("cpanm")) { |
1025
|
0
|
|
|
|
|
|
$self->bootstrap_cpanm(); |
1026
|
|
|
|
|
|
|
} |
1027
|
|
|
|
|
|
|
} |
1028
|
|
|
|
|
|
|
|
1029
|
|
|
|
|
|
|
# install CPAN if it doesn't exist |
1030
|
0
|
0
|
|
|
|
|
if (not defined $self->sysenv("cpan")) { |
1031
|
|
|
|
|
|
|
# try to install CPAN as an OS package |
1032
|
0
|
0
|
|
|
|
|
if ($self->is_root()) { |
1033
|
0
|
0
|
|
|
|
|
if ($self->module_package("CPAN")) { |
1034
|
0
|
|
|
|
|
|
$self->sysenv("cpan", $self->cmd_path("cpan")); |
1035
|
|
|
|
|
|
|
} |
1036
|
|
|
|
|
|
|
} |
1037
|
|
|
|
|
|
|
|
1038
|
|
|
|
|
|
|
# try again with cpanminus if it wasn't installed by a package |
1039
|
0
|
0
|
|
|
|
|
if (not defined $self->sysenv("cpan")) { |
1040
|
0
|
0
|
|
|
|
|
if ($self->run_cmd($self->sysenv("perl"), $self->sysenv("cpanm"), "CPAN")) { |
1041
|
0
|
|
|
|
|
|
$self->sysenv("cpan", $self->cmd_path("cpan")); |
1042
|
|
|
|
|
|
|
} |
1043
|
|
|
|
|
|
|
} |
1044
|
|
|
|
|
|
|
} |
1045
|
|
|
|
|
|
|
|
1046
|
|
|
|
|
|
|
# install dependencies for this tool |
1047
|
0
|
|
|
|
|
|
foreach my $dep (@{perlconf("module_deps")}) { |
|
0
|
|
|
|
|
|
|
1048
|
0
|
|
|
|
|
|
$self->install_module($dep); |
1049
|
|
|
|
|
|
|
} |
1050
|
0
|
|
|
|
|
|
return; |
1051
|
|
|
|
|
|
|
} |
1052
|
|
|
|
|
|
|
|
1053
|
|
|
|
|
|
|
1; |
1054
|
|
|
|
|
|
|
|
1055
|
|
|
|
|
|
|
=pod |
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
=encoding UTF-8 |
1058
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
=head1 NAME |
1060
|
|
|
|
|
|
|
|
1061
|
|
|
|
|
|
|
Sys::OsPackage - install OS packages and determine if CPAN modules are packaged for the OS |
1062
|
|
|
|
|
|
|
|
1063
|
|
|
|
|
|
|
=head1 VERSION |
1064
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
version 0.1.6 |
1066
|
|
|
|
|
|
|
|
1067
|
|
|
|
|
|
|
=head1 SYNOPSIS |
1068
|
|
|
|
|
|
|
|
1069
|
|
|
|
|
|
|
use Sys::OsPackage; |
1070
|
|
|
|
|
|
|
my $ospackage = Sys::OsPackage->instance(); |
1071
|
|
|
|
|
|
|
foreach my $module ( qw(module-name ...)) { |
1072
|
|
|
|
|
|
|
$ospackage->install_module($module); |
1073
|
|
|
|
|
|
|
} |
1074
|
|
|
|
|
|
|
|
1075
|
|
|
|
|
|
|
=head1 DESCRIPTION |
1076
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
I is used for installing Perl module dependencies. |
1078
|
|
|
|
|
|
|
It can look up whether a Perl module is available under some operating systems' packages. |
1079
|
|
|
|
|
|
|
If the module is available as an OS package, it installs it via the packaging system of the OS. |
1080
|
|
|
|
|
|
|
Otherwise it runs CPAN to install the module. |
1081
|
|
|
|
|
|
|
|
1082
|
|
|
|
|
|
|
The use cases of I include setting up systems or containers with Perl modules using OS packages |
1083
|
|
|
|
|
|
|
as often as possible. It can also be used fvor installing dependencies for a Perl script on an existing system. |
1084
|
|
|
|
|
|
|
|
1085
|
|
|
|
|
|
|
OS packaging systems currently supported by I are the Linux distributions Alpine, Arch, Debian, |
1086
|
|
|
|
|
|
|
Fedora and OpenSuse. |
1087
|
|
|
|
|
|
|
Using L it's able to detect operating systems derived from a supported platform use the correct driver. |
1088
|
|
|
|
|
|
|
|
1089
|
|
|
|
|
|
|
RHEL and CentOS are supported by the Fedora driver. |
1090
|
|
|
|
|
|
|
CentOS-derived systems Rocky and Alma are supported by recognizing them as derivatives. |
1091
|
|
|
|
|
|
|
Ubuntu is supported by the Debian driver. |
1092
|
|
|
|
|
|
|
|
1093
|
|
|
|
|
|
|
Other packaging systems for Unix-like operating systems should be feasible to add by writing a driver module. |
1094
|
|
|
|
|
|
|
|
1095
|
|
|
|
|
|
|
=head1 SEE ALSO |
1096
|
|
|
|
|
|
|
|
1097
|
|
|
|
|
|
|
L comes with I to provide a command-line interface. |
1098
|
|
|
|
|
|
|
|
1099
|
|
|
|
|
|
|
L |
1100
|
|
|
|
|
|
|
|
1101
|
|
|
|
|
|
|
"pacman/Rosetta" at Arch Linux Wiki compares commands of 5 Linux packaging systems L |
1102
|
|
|
|
|
|
|
|
1103
|
|
|
|
|
|
|
GitHub repository for Sys::OsPackage: L |
1104
|
|
|
|
|
|
|
|
1105
|
|
|
|
|
|
|
=head1 BUGS AND LIMITATIONS |
1106
|
|
|
|
|
|
|
|
1107
|
|
|
|
|
|
|
Please report bugs via GitHub at L |
1108
|
|
|
|
|
|
|
|
1109
|
|
|
|
|
|
|
Patches and enhancements may be submitted via a pull request at L |
1110
|
|
|
|
|
|
|
|
1111
|
|
|
|
|
|
|
=head1 LICENSE INFORMATION |
1112
|
|
|
|
|
|
|
|
1113
|
|
|
|
|
|
|
Copyright (c) 2022 by Ian Kluft |
1114
|
|
|
|
|
|
|
|
1115
|
|
|
|
|
|
|
This module is distributed in the hope that it will be useful, but it is provided “as is” and without any express or implied warranties. For details, see the full text of the license in the file LICENSE or at L. |
1116
|
|
|
|
|
|
|
|
1117
|
|
|
|
|
|
|
=head1 AUTHOR |
1118
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
Ian Kluft |
1120
|
|
|
|
|
|
|
|
1121
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
1122
|
|
|
|
|
|
|
|
1123
|
|
|
|
|
|
|
This software is Copyright (c) 2022 by Ian Kluft. |
1124
|
|
|
|
|
|
|
|
1125
|
|
|
|
|
|
|
This is free software, licensed under: |
1126
|
|
|
|
|
|
|
|
1127
|
|
|
|
|
|
|
The Artistic License 2.0 (GPL Compatible) |
1128
|
|
|
|
|
|
|
|
1129
|
|
|
|
|
|
|
=cut |
1130
|
|
|
|
|
|
|
|
1131
|
|
|
|
|
|
|
__END__ |