File Coverage

blib/lib/App/FatPacker/Simple.pm
Criterion Covered Total %
statement 41 212 19.3
branch 0 52 0.0
condition 0 6 0.0
subroutine 14 31 45.1
pod 0 12 0.0
total 55 313 17.5


line stmt bran cond sub pod time code
1             package App::FatPacker::Simple v1.0.2;
2 1     1   147981 use v5.24;
  1         5  
3 1     1   7 use warnings;
  1         2  
  1         82  
4 1     1   7 use experimental qw(lexical_subs signatures);
  1         2  
  1         7  
5              
6 1     1   994 use App::FatPacker;
  1         30440  
  1         6  
7 1     1   30 use Config;
  1         1  
  1         26  
8 1     1   3 use Cwd ();
  1         2  
  1         11  
9 1     1   529 use Distribution::Metadata;
  1         49159  
  1         30  
10 1     1   10 use File::Basename ();
  1         1  
  1         11  
11 1     1   3 use File::Find ();
  1         1  
  1         12  
12 1     1   2 use File::Spec;
  1         1  
  1         15  
13 1     1   2 use File::Spec::Unix;
  1         2  
  1         11  
14 1     1   2 use Getopt::Long ();
  1         1  
  1         9  
15 1     1   481 use Perl::Strip;
  1         244022  
  1         61  
16 1     1   575 use Pod::Usage ();
  1         64574  
  1         2490  
17              
18             our $IGNORE_FILE = [
19             qr/\.pod$/,
20             qr/\.packlist$/,
21             qr/MYMETA\.json$/,
22             qr/install\.json$/,
23             ];
24              
25             our $TRIAL = 0;
26              
27 0     0 0   sub new ($class, @argv) {
  0            
  0            
  0            
28 0           bless { @argv }, $class;
29             }
30              
31 0     0 0   sub parse_options ($self, @argv) {
  0            
  0            
  0            
32 0           my $parser = Getopt::Long::Parser->new(
33             config => [qw(no_auto_abbrev no_ignore_case)],
34             );
35 0     0     $parser->getoptionsfromarray(
36             \@argv,
37             "d|dir=s" => \(my $dir = 'lib,fatlib,local,extlib'),
38             "e|exclude=s" => \(my $exclude),
39 0           "h|help" => sub (@) { $self->show_help; exit 1 },
  0            
  0            
40 0     0     "o|output=s" => \(my $output),
41             "q|quiet" => \(my $quiet),
42             "s|strict" => \(my $strict),
43 0           "v|version" => sub (@) { printf "%s %s\n", __PACKAGE__, __PACKAGE__->VERSION; exit },
  0            
  0            
44 0 0         "color!" => \(my $color = 1),
45             "shebang=s" => \(my $custom_shebang),
46             "exclude-strip=s@" => \(my $exclude_strip),
47             "no-strip|no-perl-strip" => \(my $no_perl_strip),
48             "cache=s" => \(my $cache),
49             ) or exit 1;
50 0 0         $self->{script} = shift @argv or do { warn "Missing script.\n"; $self->show_help; exit 1 };
  0            
  0            
  0            
51 0           $self->{dir} = $self->build_dir($dir);
52 0           $self->{output} = $output;
53 0           $self->{quiet} = $quiet;
54 0           $self->{strict} = $strict;
55 0           $self->{color} = $color;
56 0           $self->{custom_shebang} = $custom_shebang;
57 0 0         $self->{exclude_strip} = [map { qr/$_/ } ($exclude_strip || [])->@*];
  0            
58 0           $self->{exclude} = [];
59 0 0         if (!$no_perl_strip) {
60 0 0         $self->{perl_strip} = Perl::Strip->new($cache ? (cache => $cache) : ());
61             }
62 0 0         if ($exclude) {
63 0           for my $e (split /,/, $exclude) {
64             my $dist = Distribution::Metadata->new_from_module(
65             $e, inc => $self->{dir},
66 0           );
67 0 0         if (my $files = $dist->files) {
68 0           push $self->{exclude}->@*, $files->@*;
69             } else {
70 0           $self->warning("Missing $e in $dir");
71             }
72             }
73             }
74 0           $self;
75             }
76              
77 0     0 0   sub show_help ($self) {
  0            
  0            
78 0           open my $fh, '>', \my $out;
79 0           Pod::Usage::pod2usage
80             exitval => 'noexit',
81             input => $0,
82             output => $fh,
83             sections => 'SYNOPSIS|COMMANDS|OPTIONS|EXAMPLES',
84             verbose => 99,
85             ;
86 0           $out =~ s/^[ ]{4,6}/ /mg;
87 0           $out =~ s/\n$//;
88 0           print $out;
89             }
90              
91 0     0 0   sub warning ($self, $msg) {
  0            
  0            
  0            
92 0           chomp $msg;
93             my $color = $self->{color}
94 0     0     ? sub ($text) { "\e[31m$text\e[m", "\n" }
  0            
  0            
  0            
95 0 0   0     : sub ($text) { "$text\n" };
  0            
  0            
  0            
  0            
96 0 0         if ($self->{strict}) {
    0          
97 0           die $color->("=> ERROR $msg");
98             } elsif (!$self->{quiet}) {
99 0           warn $color->("=> WARN $msg");
100             }
101             }
102              
103 0     0 0   sub debug ($self, $msg) {
  0            
  0            
  0            
104 0           chomp $msg;
105 0 0         if (!$self->{quiet}) {
106 0           warn "-> $msg\n";
107             }
108             }
109              
110 0     0 0   sub output_filename ($self) {
  0            
  0            
111 0 0         return $self->{output} if $self->{output};
112              
113 0           my $script = File::Basename::basename $self->{script};
114 0           my ($suffix, @other) = reverse split /\./, $script;
115 0 0         if (!@other) {
116 0           "$script.fatpack";
117             } else {
118 0           unshift @other, "fatpack";
119 0           join ".", reverse(@other), $suffix;
120             }
121             }
122              
123 0     0 0   sub run ($self) {
  0            
  0            
124 0           my $fatpacked = $self->fatpack_file($self->{script});
125 0           my $output_filename = $self->output_filename;
126 0 0         open my $fh, ">", $output_filename
127             or die "Cannot open '$output_filename': $!\n";
128 0           print {$fh} $fatpacked;
  0            
129 0           close $fh;
130 0           my $mode = (stat $self->{script})[2];
131 0           chmod $mode, $output_filename;
132 0           $self->debug("Successfully created $output_filename");
133             }
134              
135             # In order not to depend on App::FatPacker internals,
136             # we use only App::FatPacker::fatpack_code method.
137 0     0 0   sub fatpack_file ($self, $file) {
  0            
  0            
  0            
138 0           my ($shebang, $script) = $self->load_main_script($file);
139 0 0         $shebang = $self->{custom_shebang} if $self->{custom_shebang};
140 0           my %files;
141 0           $self->collect_files($_, \%files) for $self->{dir}->@*;
142 0           my $fatpacker = App::FatPacker->new;
143 0           return join "\n", $shebang, $fatpacker->fatpack_code(\%files), $script;
144             }
145              
146             # almost copy from App::FatPacker::load_main_script
147 0     0 0   sub load_main_script ($self, $file) {
  0            
  0            
  0            
148 0 0         open my $fh, "<", $file or die "Cannot open '$file': $!\n";
149 0           my @lines = <$fh>;
150 0           my @shebang;
151 0 0 0       if (@lines && index($lines[0], '#!') == 0) {
152 0           while (1) {
153 0           push @shebang, shift @lines;
154 0 0         last if $shebang[-1] =~ m{^\#\!.*perl};
155             }
156             }
157 0           ((join "", @shebang), (join "", @lines));
158             }
159              
160 0     0 0   sub load_file ($self, $absolute, $relative, $original) {
  0            
  0            
  0            
  0            
  0            
161              
162 0           my $content = do {
163 0 0         open my $fh, "<", $absolute or die "Cannot open '$absolute': $!\n";
164 0           local $/; <$fh>;
  0            
165             };
166              
167 0 0 0       if ($self->{perl_strip} and !grep { $original =~ $_ } $self->{exclude_strip}->@*) {
  0            
168 0           $self->debug("fatpack $relative (with perl-strip)");
169 0           return $self->{perl_strip}->strip($content);
170             } else {
171 0           $self->debug("fatpack $relative (without perl-strip)");
172 0           return $content;
173             }
174             }
175              
176 0     0 0   sub collect_files ($self, $dir, $files) {
  0            
  0            
  0            
  0            
177              
178 0           my $absolute_dir = Cwd::abs_path($dir);
179             # When $dir is not an archlib,
180             # and we are about to search $dir/archlib, skip it!
181             # because $dir/archlib itself will be searched another time.
182 0           my $skip_dir = File::Spec->catdir($absolute_dir, $Config{archname});
183 0           $skip_dir = qr/\Q$skip_dir\E/;
184              
185 0     0     my $find = sub (@) {
  0            
186 0 0         return unless -f $_;
187 0           for my $ignore ($IGNORE_FILE->@*) {
188 0 0         $_ =~ $ignore and return;
189             }
190 0           my $original = $_;
191 0           my $absolute = Cwd::abs_path($original);
192 0 0         return if $absolute =~ $skip_dir;
193 0           my $relative = File::Spec::Unix->abs2rel($absolute, $absolute_dir);
194 0           for my $exclude ($self->{exclude}->@*) {
195 0 0         if ($absolute eq $exclude) {
196 0           $self->debug("exclude $relative");
197 0           return;
198             }
199             }
200 0 0         if (!/\.(?:pm|ix|al|pl)$/) {
201 0           $self->warning("skip non perl module file $relative");
202 0           return;
203             }
204 0           $files->{$relative} = $self->load_file($absolute, $relative, $original);
205 0           };
206 0           File::Find::find({wanted => $find, no_chdir => 1}, $dir);
207             }
208              
209 0     0 0   sub build_dir ($self, $dir_string) {
  0            
  0            
  0            
210 0           my @dir;
211 0           for my $d (grep -d, split /,/, $dir_string) {
212 0           my $try = File::Spec->catdir($d, "lib/perl5");
213 0 0         if (-d $try) {
214 0           push @dir, $try, File::Spec->catdir($try, $Config{archname});
215             } else {
216 0           push @dir, $d, File::Spec->catdir($d, $Config{archname});
217             }
218             }
219 0           return [ grep -d, @dir ];
220             }
221              
222             1;
223             __END__