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   23187 use 5.016;
  9         50  
6 9     9   48 use warnings;
  9         15  
  9         219  
7 9     9   39 use utf8;
  9         17  
  9         46  
8              
9             our $VERSION = '0.010';
10              
11 9     9   385 use parent qw(Exporter);
  9         19  
  9         46  
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   803 use Carp qw(croak);
  9         15  
  9         469  
29 9     9   49 use Cwd qw(cwd);
  9         15  
  9         366  
30 9     9   2861 use Encode qw(decode);
  9         26992  
  9         462  
31 9     9   904 use English qw(-no_match_vars);
  9         4812  
  9         67  
32 9     9   2883 use File::Spec::Functions qw(catfile splitdir splitpath);
  9         18  
  9         507  
33 9     9   50 use File::Spec::Unix qw();
  9         13  
  9         151  
34 9     9   4116 use IPC::Cmd qw(can_run);
  9         291777  
  9         515  
35 9     9   23972 use Module::CoreList 2.32;
  9         816318  
  9         76  
36 9     9   5635 use version 0.77;
  9         129  
  9         106  
37              
38 9     9   5137 use CPANPLUS::Error qw(error);
  9         60793  
  9         11989  
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 1673 my $string = shift;
47              
48 29         357 return version->parse($string);
49             }
50              
51             sub module_is_distributed_with_perl {
52 13     13 1 555 my ($module_name, $version) = @_;
53              
54 13         22 my $ok = 0;
55              
56             # cpan2dist is run with -w, which triggers a warning in Module::CoreList.
57 13         57 local $WARNING = 0;
58              
59 13         93 my $upper = Module::CoreList->removed_from($module_name);
60 13 100 66     15340 if (!defined $upper || $perl_version < parse_version($upper)) {
61 7         40 my $lower = Module::CoreList->first_release($module_name, $version);
62 7 100 66     3019 if (defined $lower && $perl_version >= parse_version($lower)) {
63 1         2 $ok = 1;
64             }
65             }
66              
67 13         138 return $ok;
68             }
69              
70             sub decode_utf8 {
71 7     7 1 18 my $bytes = shift;
72              
73 7         36 return decode('UTF-8', $bytes);
74             }
75              
76             sub slurp_utf8 {
77 3     3 1 10 my $filename = shift;
78              
79 3         4 my $data;
80              
81 3         102 my $ok = open my $fh, '<:encoding(UTF-8)', $filename;
82 3 50       191 if ($ok) {
83 3         25 local $RS = undef;
84 3         1177 $data = <$fh>;
85 3 50       230 close $fh or $ok = 0;
86             }
87              
88 3         23 return $data;
89             }
90              
91             sub spew_utf8 {
92 1     1 1 3 my ($filename, $string) = @_;
93              
94 1     1   32 my $ok = open my $fh, '>:encoding(UTF-8)', $filename;
  1         8  
  1         2  
  1         4  
95 1 50       1007 if ($ok) {
96 1         1 $ok = print {$fh} $string;
  1         6  
97 1 50       55 close $fh or $ok = 0;
98             }
99              
100 1         8 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 9 my $path = shift;
152              
153 6         18 (undef, $path) = splitpath($path, 1);
154 6         39 $path = File::Spec::Unix->catfile(splitdir($path));
155              
156 6         52 return $path;
157             }
158              
159             sub filetype {
160 7     7 1 17 my $filename = shift;
161              
162 7         157 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         76 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         18 my $type = 'data';
189              
190 7 50       69 if ($filename =~ m{[.]([^.]+) \z}xms) {
191 7         32 my $suffix = lc $1;
192 7 50       21 if (exists $type_for{$suffix}) {
193 7         14 $type = $type_for{$suffix};
194             }
195             }
196              
197 7 50       21 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         60 return $type;
213             }
214              
215             sub find_most_recent_mtime {
216 6     6 1 69 my $sourcedir = shift;
217              
218 6         65 my $most_recent_mtime = 0;
219              
220             my $find = sub {
221 230     230   420 my $dir = shift;
222              
223 230 50       5766 opendir my $dh, $dir or croak "Could not traverse '$dir': $OS_ERROR";
224             ENTRY:
225 230         5838 while (defined(my $entry = readdir $dh)) {
226 1642 100 100     5944 next ENTRY if $entry eq q{.} || $entry eq q{..};
227              
228 1182         4820 my $path = catfile($dir, $entry);
229              
230             # Skip symbolic links.
231 1182 50       15853 next ENTRY if -l $path;
232              
233 1182 100       12002 if (-d $path) {
234 224         782 __SUB__->($path);
235             }
236             else {
237 958         9994 my @stat = stat $path;
238 958 50       2514 if (@stat) {
239 958         1381 my $mtime = $stat[9];
240 958 100       4036 if ($most_recent_mtime < $mtime) {
241 12         55 $most_recent_mtime = $mtime;
242             }
243             }
244             }
245             }
246 230         2170 closedir $dh;
247              
248 230         1527 return;
249 6         154 };
250 6         48 $find->($sourcedir);
251              
252 6         108 return $most_recent_mtime;
253             }
254              
255             sub find_shared_objects {
256 3     3 1 15 my $stagingdir = shift;
257              
258 3         10 my @shared_objects;
259              
260             my $find = sub {
261 18     18   41 my $dir = shift;
262              
263 18 50       335 opendir my $dh, $dir
264             or croak "Could not traverse '$dir': $OS_ERROR";
265             ENTRY:
266 18         223 while (defined(my $entry = readdir $dh)) {
267 57 100 100     286 next ENTRY if $entry eq q{.} || $entry eq q{..};
268              
269 21         108 my $path = catfile($dir, $entry);
270              
271             # Skip symbolic links.
272 21 50       211 next ENTRY if -l $path;
273              
274 21 100       209 if (-d $path) {
275 15         57 __SUB__->($path);
276             }
277             else {
278 6 50       39 if (filetype($path) eq 'executable') {
279 0         0 push @shared_objects, $path;
280             }
281             }
282             }
283 18         125 closedir $dh;
284              
285 18         117 return;
286 3         32 };
287 3         12 $find->($stagingdir);
288              
289 3         46 return \@shared_objects;
290             }
291              
292             sub is_testing {
293 2   33 2 1 14 return $ENV{AUTOMATED_TESTING} || $ENV{RELEASE_TESTING};
294             }
295              
296             1;
297             __END__