Add POD encoding declaration
[p5sagit/App-FatPacker.git] / lib / App / FatPacker.pm
CommitLineData
48af1939 1package App::FatPacker;
2
3use strict;
4use warnings FATAL => 'all';
f5a54fa1 5use 5.008001;
48af1939 6use Getopt::Long;
7use Cwd qw(cwd);
8use File::Find qw(find);
9use File::Spec::Functions qw(
10 catdir splitpath splitdir catpath rel2abs abs2rel
11);
cbd99f49 12use File::Spec::Unix;
48af1939 13use File::Copy qw(copy);
4d5603b7 14use File::Path qw(mkpath rmtree);
48af1939 15use B qw(perlstring);
16
2b49e0e2 17our $VERSION = '0.009009'; # 0.9.9
f5a54fa1 18
19$VERSION = eval $VERSION;
20
48af1939 21sub call_parser {
24d68aa2 22 my $self = shift;
69667cc8 23 my ($args, $options) = @_;
24d68aa2 24
25 local *ARGV = [ @{$args} ];
69667cc8 26 $self->{option_parser}->getoptions(@$options);
24d68aa2 27
28 return [ @ARGV ];
48af1939 29}
30
31sub lines_of {
32 map +(chomp,$_)[1], do { local @ARGV = ($_[0]); <> };
33}
34
35sub stripspace {
36 my ($text) = @_;
37 $text =~ /^(\s+)/ && $text =~ s/^$1//mg;
38 $text;
39}
40
48af1939 41sub import {
24d68aa2 42 $_[1] && $_[1] eq '-run_script'
48af1939 43 and return shift->new->run_script;
44}
45
24d68aa2 46sub new {
47 bless {
48 option_parser => Getopt::Long::Parser->new(
49 config => [ qw(require_order pass_through bundling no_auto_abbrev) ]
50 ),
51 }, $_[0];
52}
48af1939 53
54sub run_script {
55 my ($self, $args) = @_;
56 my @args = $args ? @$args : @ARGV;
3e4dadce 57 (my $cmd = shift @args || 'help') =~ s/-/_/g;
24d68aa2 58
48af1939 59 if (my $meth = $self->can("script_command_${cmd}")) {
60 $self->$meth(\@args);
61 } else {
62 die "No such command ${cmd}";
63 }
64}
65
3e4dadce 66sub script_command_help {
67 print "Try `perldoc fatpack` for how to use me\n";
68}
69
48af1939 70sub script_command_trace {
71 my ($self, $args) = @_;
24d68aa2 72
69667cc8 73 $args = $self->call_parser($args => [
48af1939 74 'to=s' => \my $file,
75 'to-stderr' => \my $to_stderr,
3fdf85ca 76 'use=s' => \my @additional_use
69667cc8 77 ]);
48af1939 78
79 die "Can't use to and to-stderr on same call" if $file && $to_stderr;
80
3fdf85ca 81 $file ||= 'fatpacker.trace';
69667cc8 82
3fdf85ca 83 if (!$to_stderr and -e $file) {
84 unlink $file or die "Couldn't remove old trace file: $!";
48af1939 85 }
86 my $arg = do {
3fdf85ca 87 if ($to_stderr) {
020d9b76 88 ">&STDERR"
3fdf85ca 89 } elsif ($file) {
276a30c9 90 ">>${file}"
48af1939 91 }
92 };
3fdf85ca 93
b4704b1a 94 $self->trace(
5e1de95d 95 use => \@additional_use,
96 args => $args,
7a3662c8 97 output => $arg,
b4704b1a 98 );
abd7cf01 99}
100
101sub trace {
b4704b1a 102 my ($self, %opts) = @_;
69667cc8 103
e0cd89ab 104 my $capture;
105
69667cc8 106 my $output = $opts{output} || do {
107 $capture++; '>&STDOUT'
108 };
e0cd89ab 109
69667cc8 110 my $trace_opts = join ',', $output||'>&STDOUT', @{$opts{use}||[]};
7a3662c8 111
69667cc8 112 local $ENV{PERL5OPT} = '-MApp::FatPacker::Trace='.$trace_opts;
abd7cf01 113
69667cc8 114 my @args = @{$opts{args}||[]};
e0cd89ab 115
69667cc8 116 if ($output) {
117 # user specified output target, JFDI
118 system $^X, @args;
119 return;
e0cd89ab 120 } else {
69667cc8 121 # no output target specified, slurp
122 open my $out_fh, '-|', $^X, @args;
123 return do { local $/; <$out_fh> };
124 }
48af1939 125}
126
127sub script_command_packlists_for {
128 my ($self, $args) = @_;
129 foreach my $pl ($self->packlists_containing($args)) {
130 print "${pl}\n";
131 }
132}
133
134sub packlists_containing {
135 my ($self, $targets) = @_;
136 my @targets = @$targets;
a976705b 137 foreach my $t (@targets) {
138 require $t;
139 }
48af1939 140 my @search = grep -d $_, map catdir($_, 'auto'), @INC;
141 my %pack_rev;
142 my $cwd = cwd;
143 find(sub {
144 return unless $_ eq '.packlist' && -f $_;
145 $pack_rev{$_} = $File::Find::name for lines_of $File::Find::name;
146 }, @search);
147 chdir($cwd) or die "Couldn't chdir back to ${cwd} after find: $!";
f5a54fa1 148 my %found; @found{map +($pack_rev{$INC{$_}}||()), @targets} = ();
48af1939 149 sort keys %found;
150}
151
152sub script_command_tree {
153 my ($self, $args) = @_;
154 my $base = catdir(cwd,'fatlib');
155 $self->packlists_to_tree($base, $args);
156}
157
158sub packlists_to_tree {
159 my ($self, $where, $packlists) = @_;
4d5603b7 160 rmtree $where;
161 mkpath $where;
48af1939 162 foreach my $pl (@$packlists) {
163 my ($vol, $dirs, $file) = splitpath $pl;
164 my @dir_parts = splitdir $dirs;
165 my $pack_base;
166 PART: foreach my $p (0 .. $#dir_parts) {
167 if ($dir_parts[$p] eq 'auto') {
168 # $p-2 since it's <wanted path>/$Config{archname}/auto
169 $pack_base = catpath $vol, catdir @dir_parts[0..$p-2];
170 last PART;
171 }
172 }
173 die "Couldn't figure out base path of packlist ${pl}" unless $pack_base;
174 foreach my $source (lines_of $pl) {
175 # there is presumably a better way to do "is this under this base?"
176 # but if so, it's not obvious to me in File::Spec
177 next unless substr($source,0,length $pack_base) eq $pack_base;
178 my $target = rel2abs( abs2rel($source, $pack_base), $where );
179 my $target_dir = catpath((splitpath $target)[0,1]);
4d5603b7 180 mkpath $target_dir;
48af1939 181 copy $source => $target;
182 }
183 }
184}
185
186sub script_command_file {
187 my ($self, $args) = @_;
188 my $file = shift @$args;
189 my $cwd = cwd;
190 my @dirs = map rel2abs($_, $cwd), ('lib','fatlib');
191 my %files;
192 foreach my $dir (@dirs) {
193 find(sub {
194 return unless -f $_;
195 !/\.pm$/ and warn "File ${File::Find::name} isn't a .pm file - can't pack this and if you hoped we were going to things may not be what you expected later\n" and return;
cbd99f49 196 $files{File::Spec::Unix->abs2rel($File::Find::name,$dir)} = do {
48af1939 197 local (@ARGV, $/) = ($File::Find::name); <>
198 };
0de38c11 199 close ARGV;
48af1939 200 }, $dir);
201 }
202 my $start = stripspace <<' END_START';
203 # This chunk of stuff was generated by App::FatPacker. To find the original
204 # file's code, look for the end of this BEGIN block or the string 'FATPACK'
205 BEGIN {
206 my %fatpacked;
207 END_START
208 my $end = stripspace <<' END_END';
209 s/^ //mg for values %fatpacked;
210
211 unshift @INC, sub {
212 if (my $fat = $fatpacked{$_[1]}) {
002ecfea 213 open my $fh, '<', \$fat
214 or die "FatPacker error loading $_[1] (could be a perl installation issue?)";
48af1939 215 return $fh;
216 }
217 return
218 };
219
220 } # END OF FATPACK CODE
221 END_END
222 my @segments = map {
223 (my $stub = $_) =~ s/\.pm$//;
224 my $name = uc join '_', split '/', $stub;
cc5db92a 225 my $data = $files{$_}; $data =~ s/^/ /mg; $data =~ s/(?<!\n)\z/\n/;
48af1939 226 '$fatpacked{'.perlstring($_).qq!} = <<'${name}';\n!
227 .qq!${data}${name}\n!;
228 } sort keys %files;
229 print join "\n", $start, @segments, $end;
230}
f5a54fa1 231
bb49414d 232=encoding UTF-8
233
f5a54fa1 234=head1 NAME
235
236App::FatPacker - pack your dependencies onto your script file
237
238=head1 SYNOPSIS
239
240 $ fatpack trace myscript.pl
62ceea28 241 $ fatpack packlists-for `cat fatpacker.trace` >packlists
242 $ fatpack tree `cat packlists`
f5a54fa1 243 $ (fatpack file; cat myscript.pl) >myscript.packed.pl
244
245See the documentation for the L<fatpack> script itself for more information.
246
3fdf85ca 247The programmatic API for this code is not yet fully decided, hence the 0.9
f5a54fa1 248release version. Expect that to be cleaned up for 1.0.
249
250=head1 SUPPORT
251
252Your current best avenue is to come annoy annoy mst on #toolchain on
253irc.perl.org. There should be a non-IRC means of support by 1.0.
254
255=head1 AUTHOR
256
257Matt S. Trout (mst) <mst@shadowcat.co.uk>
258
259=head2 CONTRIBUTORS
260
99b15200 261miyagawa - Tatsuhiko Miyagawa (cpan:MIYAGAWA) <miyagawa@bulknews.net>
262
263tokuhirom - MATSUNO★Tokuhiro (cpan:TOKUHIROM) <tokuhirom@gmail.com>
264
265dg - David Leadbeater (cpan:DGL) <dgl@dgl.cx>
266
267gugod - 劉康民 (cpan:GUGOD) <gugod@cpan.org>
268
269t0m - Tomas Doran (cpan:BOBTFISH) <bobtfish@bobtfish.net>
270
271sawyer - Sawyer X (cpan:XSAWYERX) <xsawyerx@cpan.org>
272
273ether - Karen Etheridge (cpan:ETHER) <ether@cpan.org>
274
cbd99f49 275Mithaldu - Christian Walde (cpan:MITHALDU) <walde.christian@googlemail.com>
276
99b15200 277Many more people are probably owed thanks for ideas. Yet
f5a54fa1 278another doc nit to fix.
279
280=head1 COPYRIGHT
281
282Copyright (c) 2010 the App::FatPacker L</AUTHOR> and L</CONTRIBUTORS>
283as listed above.
284
285=head1 LICENSE
286
287This library is free software and may be distributed under the same terms
288as perl itself.
289
290=cut
291
48af1939 2921;
24d68aa2 293