1 package App::FatPacker;
4 use warnings FATAL => 'all';
8 use File::Find qw(find);
9 use File::Spec::Functions qw(
10 catdir splitpath splitdir catpath rel2abs abs2rel
12 use File::Copy qw(copy);
13 use File::Path qw(mkpath rmtree);
16 our $VERSION = '0.009006'; # 0.9.6
18 $VERSION = eval $VERSION;
22 my ( $args, $options ) = @_;
24 local *ARGV = [ @{$args} ];
25 $self->{'option_parser'}->getoptions( @{$options} );
31 map +(chomp,$_)[1], do { local @ARGV = ($_[0]); <> };
36 $text =~ /^(\s+)/ && $text =~ s/^$1//mg;
41 $_[1] && $_[1] eq '-run_script'
42 and return shift->new->run_script;
47 option_parser => Getopt::Long::Parser->new(
48 config => [ qw(require_order pass_through bundling no_auto_abbrev) ]
54 my ($self, $args) = @_;
55 my @args = $args ? @$args : @ARGV;
56 (my $cmd = shift @args || 'help') =~ s/-/_/g;
58 if (my $meth = $self->can("script_command_${cmd}")) {
61 die "No such command ${cmd}";
65 sub script_command_help {
66 print "Try `perldoc fatpack` for how to use me\n";
69 sub script_command_trace {
70 my ($self, $args) = @_;
72 $args = $self->call_parser( $args => [
74 'to-stderr' => \my $to_stderr,
75 'use=s' => \my @additional_use
78 die "Can't use to and to-stderr on same call" if $file && $to_stderr;
80 $file ||= 'fatpacker.trace';
81 if (!$to_stderr and -e $file) {
82 unlink $file or die "Couldn't remove old trace file: $!";
93 use => \@additional_use,
100 my ($self, %opts) = @_;
101 my $use = defined $opts{'use'} ? $opts{'use'} : [];
102 my $args = defined $opts{'args'} ? $opts{'args'} : [];
103 my $output = $opts{'output'};
106 $output .= "," . join ",", @$use;
110 local $ENV{PERL5OPT} = '-MApp::FatPacker::Trace='.$output;
115 sub script_command_packlists_for {
116 my ($self, $args) = @_;
117 foreach my $pl ($self->packlists_containing($args)) {
122 sub packlists_containing {
123 my ($self, $targets) = @_;
124 my @targets = @$targets;
125 require $_ for @targets;
126 my @search = grep -d $_, map catdir($_, 'auto'), @INC;
130 return unless $_ eq '.packlist' && -f $_;
131 $pack_rev{$_} = $File::Find::name for lines_of $File::Find::name;
133 chdir($cwd) or die "Couldn't chdir back to ${cwd} after find: $!";
134 my %found; @found{map +($pack_rev{$INC{$_}}||()), @targets} = ();
138 sub script_command_tree {
139 my ($self, $args) = @_;
140 my $base = catdir(cwd,'fatlib');
141 $self->packlists_to_tree($base, $args);
144 sub packlists_to_tree {
145 my ($self, $where, $packlists) = @_;
148 foreach my $pl (@$packlists) {
149 my ($vol, $dirs, $file) = splitpath $pl;
150 my @dir_parts = splitdir $dirs;
152 PART: foreach my $p (0 .. $#dir_parts) {
153 if ($dir_parts[$p] eq 'auto') {
154 # $p-2 since it's <wanted path>/$Config{archname}/auto
155 $pack_base = catpath $vol, catdir @dir_parts[0..$p-2];
159 die "Couldn't figure out base path of packlist ${pl}" unless $pack_base;
160 foreach my $source (lines_of $pl) {
161 # there is presumably a better way to do "is this under this base?"
162 # but if so, it's not obvious to me in File::Spec
163 next unless substr($source,0,length $pack_base) eq $pack_base;
164 my $target = rel2abs( abs2rel($source, $pack_base), $where );
165 my $target_dir = catpath((splitpath $target)[0,1]);
167 copy $source => $target;
172 sub script_command_file {
173 my ($self, $args) = @_;
174 my $file = shift @$args;
176 my @dirs = map rel2abs($_, $cwd), ('lib','fatlib');
178 foreach my $dir (@dirs) {
181 !/\.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;
182 $files{abs2rel($File::Find::name,$dir)} = do {
183 local (@ARGV, $/) = ($File::Find::name); <>
187 my $start = stripspace <<' END_START';
188 # This chunk of stuff was generated by App::FatPacker. To find the original
189 # file's code, look for the end of this BEGIN block or the string 'FATPACK'
193 my $end = stripspace <<' END_END';
194 s/^ //mg for values %fatpacked;
197 if (my $fat = $fatpacked{$_[1]}) {
198 open my $fh, '<', \$fat
199 or die "FatPacker error loading $_[1] (could be a perl installation issue?)";
205 } # END OF FATPACK CODE
208 (my $stub = $_) =~ s/\.pm$//;
209 my $name = uc join '_', split '/', $stub;
210 my $data = $files{$_}; $data =~ s/^/ /mg; $data =~ s/(?<!\n)\z/\n/;
211 '$fatpacked{'.perlstring($_).qq!} = <<'${name}';\n!
212 .qq!${data}${name}\n!;
214 print join "\n", $start, @segments, $end;
219 App::FatPacker - pack your dependencies onto your script file
223 $ fatpack trace myscript.pl
224 $ fatpack packlists-for `cat fatpacker.trace` >packlists
225 $ fatpack tree `cat packlists`
226 $ (fatpack file; cat myscript.pl) >myscript.packed.pl
228 See the documentation for the L<fatpack> script itself for more information.
230 The programmatic API for this code is not yet fully decided, hence the 0.9
231 release version. Expect that to be cleaned up for 1.0.
235 Your current best avenue is to come annoy annoy mst on #toolchain on
236 irc.perl.org. There should be a non-IRC means of support by 1.0.
240 Matt S. Trout (mst) <mst@shadowcat.co.uk>
244 None as yet, though I probably owe lots of people thanks for ideas. Yet
245 another doc nit to fix.
249 Copyright (c) 2010 the App::FatPacker L</AUTHOR> and L</CONTRIBUTORS>
254 This library is free software and may be distributed under the same terms