File Coverage

blib/lib/CPANPLUS/Dist/Debora/Util.pm
Criterion Covered Total %
statement 123 161 76.4
branch 27 62 43.5
condition 11 19 57.8
subroutine 27 29 93.1
pod 11 11 100.0
total 199 282 70.5


line stmt bran cond sub pod time code
1             package CPANPLUS::Dist::Debora::Util;
2              
3             # SPDX-License-Identifier: Artistic-1.0-Perl OR GPL-1.0-or-later
4              
5 9     9   27763 use 5.016;
  9         60  
6 9     9   55 use warnings;
  9         18  
  9         240  
7 9     9   45 use utf8;
  9         18  
  9         46  
8              
9             our $VERSION = '0.009';
10              
11 9     9   430 use parent qw(Exporter);
  9         29  
  9         59  
12              
13             our @EXPORT_OK = qw(
14             parse_version
15             module_is_distributed_with_perl
16             decode_utf8
17             slurp_utf8
18             spew_utf8
19             can_run
20             run
21             unix_path
22             filetype
23             find_most_recent_mtime
24             find_shared_objects
25             is_testing
26             );
27              
28 9     9   912 use Carp qw(croak);
  9         20  
  9         564  
29 9     9   62 use Cwd qw(cwd);
  9         27  
  9         430  
30 9     9   1785 use Encode qw(decode);
  9         31498  
  9         553  
31 9     9   1050 use English qw(-no_match_vars);
  9         5350  
  9         77  
32 9     9   3689 use File::Spec::Functions qw(catfile splitdir splitpath);
  9         20  
  9         554  
33 9     9   59 use File::Spec::Unix qw();
  9         18  
  9         199  
34 9     9   4791 use IPC::Cmd qw(can_run);
  9         334040  
  9         589  
35 9     9   27403 use Module::CoreList 2.32;
  9         980582  
  9         85  
36 9     9   6634 use version 0.77;
  9         153  
  9         81  
37              
38 9     9   5037 use CPANPLUS::Error qw(error);
  9         69869  
  9         13536  
39              
40             # Avoid warnings from IO::Select by using IPC::Run.
41             $IPC::Cmd::USE_IPC_RUN = IPC::Cmd->can_use_ipc_run;
42              
43             my $perl_version = parse_version($PERL_VERSION);
44              
45             sub parse_version {
46 29     29 1 2005 my $string = shift;
47              
48 29         337 return version->parse($string);
49             }
50              
51             sub module_is_distributed_with_perl {
52 13     13 1 707 my ($module_name, $version) = @_;
53              
54 13         25 my $ok = 0;
55              
56             # cpan2dist is run with -w, which triggers a warning in Module::CoreList.
57 13         71 local $WARNING = 0;
58              
59 13         113 my $upper = Module::CoreList->removed_from($module_name);
60 13 100 66     18334 if (!defined $upper || $perl_version < parse_version($upper)) {
61 7         49 my $lower = Module::CoreList->first_release($module_name, $version);
62 7 100 66     3764 if (defined $lower && $perl_version >= parse_version($lower)) {
63 1         4 $ok = 1;
64             }
65             }
66              
67 13         170 return $ok;
68             }
69              
70             sub decode_utf8 {
71 7     7 1 24 my $bytes = shift;
72              
73 7         42 return decode('UTF-8', $bytes);
74             }
75              
76             sub slurp_utf8 {
77 3     3 1 12 my $filename = shift;
78              
79 3         8 my $data;
80              
81 3         172 my $ok = open my $fh, '<:encoding(UTF-8)', $filename;
82 3 50       335 if ($ok) {
83 3         29 local $RS = undef;
84 3         857 $data = <$fh>;
85 3 50       300 close $fh or $ok = 0;
86             }
87              
88 3         26 return $data;
89             }
90              
91             sub spew_utf8 {
92 1     1 1 4 my ($filename, $string) = @_;
93              
94 1     1   44 my $ok = open my $fh, '>:encoding(UTF-8)', $filename;
  1         7  
  1         39  
  1         9  
95 1 50       1301 if ($ok) {
96 1         3 $ok = print {$fh} $string;
  1         8  
97 1 50       68 close $fh or $ok = 0;
98             }
99              
100 1         12 return $ok;
101             }
102              
103             sub run {
104 0     0 1 0 my (%options) = @_;
105              
106 0         0 my $ok = 0;
107              
108 0         0 my $command = $options{command};
109 0 0       0 if (!$command) {
110 0         0 error('No command');
111 0         0 return $ok;
112             }
113              
114 0         0 my $dir = $options{dir};
115 0         0 delete $options{dir};
116              
117 0 0       0 if (!exists $options{buffer}) {
118 0         0 my $buf = q{};
119 0         0 $options{buffer} = \$buf;
120             }
121              
122             my $on_error = $options{on_error}
123 0   0 0   0 // sub { error("Could not run '$_[0]': $_[1]") };
  0         0  
124 0         0 delete $options{on_error};
125              
126 0         0 my $origdir;
127 0 0       0 if ($dir) {
128 0         0 $origdir = cwd;
129 0 0       0 if (!chdir $dir) {
130 0         0 return $ok;
131             }
132             }
133              
134 0         0 $ok = IPC::Cmd::run(%options);
135 0 0       0 if (!$ok) {
136 0         0 my $cmdline = join q{ }, @{$command};
  0         0  
137 0   0     0 my $output = ${$options{buffer}} // q{};
  0         0  
138 0         0 $on_error->($cmdline, $output);
139             }
140              
141 0 0       0 if ($origdir) {
142 0 0       0 if (!chdir $origdir) {
143 0         0 $ok = 0;
144             }
145             }
146              
147 0         0 return $ok;
148             }
149              
150             sub unix_path {
151 6     6 1 14 my $path = shift;
152              
153 6         92 (undef, $path) = splitpath($path, 1);
154 6         55 $path = File::Spec::Unix->catfile(splitdir($path));
155              
156 6         68 return $path;
157             }
158              
159             sub filetype {
160 7     7 1 24 my $filename = shift;
161              
162 7         146 my %type_for = (
163             '1' => 'text',
164             '1p' => 'text',
165             '3' => 'text',
166             '3perl' => 'text',
167             '3pm' => 'text',
168             'bat' => 'script',
169             'dll' => 'executable',
170             'dylib' => 'executable',
171             'exe' => 'executable',
172             'pl' => 'script',
173             'pm' => 'text',
174             'pod' => 'text',
175             'so' => 'executable',
176             );
177              
178 7         102 my @magic = (
179             [0, 4, '7F454C46', 'executable'], # ELF
180             [0, 4, 'FEEDFACE', 'executable'], # Mach-O
181             [0, 4, 'CEFAEDFE', 'executable'], # Mach-O
182             [0, 4, 'FEEDFACF', 'executable'], # Mach-O
183             [0, 4, 'CFFAEDFE', 'executable'], # Mach-O
184             [0, 2, '4D5A', 'executable'], # PE
185             [0, 2, '2321', 'script'], # Shebang
186             );
187              
188 7         25 my $type = 'data';
189              
190 7 50       75 if ($filename =~ m{[.]([^.]+) \z}xms) {
191 7         29 my $suffix = lc $1;
192 7 50       32 if (exists $type_for{$suffix}) {
193 7         27 $type = $type_for{$suffix};
194             }
195             }
196              
197 7 50       32 if ($type eq 'data') {
198 0 0       0 if (open my $fh, '<:raw', $filename) {
199 0 0       0 if (read $fh, my $data, 16) {
200             TYPE:
201 0         0 for (@magic) {
202 0 0       0 if (substr($data, $_->[0], $_->[1]) eq pack 'H*', $_->[2]) {
203 0         0 $type = $_->[3];
204 0         0 last TYPE;
205             }
206             }
207             }
208 0 0       0 close $fh or undef;
209             }
210             }
211              
212 7         64 return $type;
213             }
214              
215             sub find_most_recent_mtime {
216 6     6 1 75 my $sourcedir = shift;
217              
218 6         65 my $most_recent_mtime = 0;
219              
220             my $find = sub {
221 230     230   520 my $dir = shift;
222              
223 230 50       6291 opendir my $dh, $dir or croak "Could not traverse '$dir': $OS_ERROR";
224             ENTRY:
225 230         4621 while (defined(my $entry = readdir $dh)) {
226 1642 100 100     6868 next ENTRY if $entry eq q{.} || $entry eq q{..};
227              
228 1182         5397 my $path = catfile($dir, $entry);
229              
230             # Skip symbolic links.
231 1182 50       16061 next ENTRY if -l $path;
232              
233 1182 100       14542 if (-d $path) {
234 224         911 __SUB__->($path);
235             }
236             else {
237 958         11983 my @stat = stat $path;
238 958 50       2889 if (@stat) {
239 958         1640 my $mtime = $stat[9];
240 958 100       4853 if ($most_recent_mtime < $mtime) {
241 12         85 $most_recent_mtime = $mtime;
242             }
243             }
244             }
245             }
246 230         2565 closedir $dh;
247              
248 230         1776 return;
249 6         173 };
250 6         52 $find->($sourcedir);
251              
252 6         130 return $most_recent_mtime;
253             }
254              
255             sub find_shared_objects {
256 3     3 1 10 my $stagingdir = shift;
257              
258 3         7 my @shared_objects;
259              
260             my $find = sub {
261 18     18   40 my $dir = shift;
262              
263 18 50       407 opendir my $dh, $dir
264             or croak "Could not traverse '$dir': $OS_ERROR";
265             ENTRY:
266 18         279 while (defined(my $entry = readdir $dh)) {
267 57 100 100     344 next ENTRY if $entry eq q{.} || $entry eq q{..};
268              
269 21         123 my $path = catfile($dir, $entry);
270              
271             # Skip symbolic links.
272 21 50       282 next ENTRY if -l $path;
273              
274 21 100       247 if (-d $path) {
275 15         86 __SUB__->($path);
276             }
277             else {
278 6 50       32 if (filetype($path) eq 'executable') {
279 0         0 push @shared_objects, $path;
280             }
281             }
282             }
283 18         157 closedir $dh;
284              
285 18         139 return;
286 3         46 };
287 3         14 $find->($stagingdir);
288              
289 3         40 return \@shared_objects;
290             }
291              
292             sub is_testing {
293 2   33 2 1 15 return $ENV{AUTOMATED_TESTING} || $ENV{RELEASE_TESTING};
294             }
295              
296             1;
297             __END__