App-FatPacker-Simple
view release on metacpan or search on metacpan
lib/App/FatPacker/Simple.pm view on Meta::CPAN
package App::FatPacker::Simple v1.0.2;
use v5.24;
use warnings;
use experimental qw(lexical_subs signatures);
use App::FatPacker;
use Config;
use Cwd ();
use Distribution::Metadata;
use File::Basename ();
use File::Find ();
use File::Spec;
use File::Spec::Unix;
use Getopt::Long ();
use Perl::Strip;
use Pod::Usage ();
our $IGNORE_FILE = [
qr/\.pod$/,
qr/\.packlist$/,
qr/MYMETA\.json$/,
qr/install\.json$/,
];
our $TRIAL = 0;
sub new ($class, @argv) {
bless { @argv }, $class;
}
sub parse_options ($self, @argv) {
my $parser = Getopt::Long::Parser->new(
config => [qw(no_auto_abbrev no_ignore_case)],
);
$parser->getoptionsfromarray(
\@argv,
"d|dir=s" => \(my $dir = 'lib,fatlib,local,extlib'),
"e|exclude=s" => \(my $exclude),
"h|help" => sub (@) { $self->show_help; exit 1 },
"o|output=s" => \(my $output),
"q|quiet" => \(my $quiet),
"s|strict" => \(my $strict),
"v|version" => sub (@) { printf "%s %s\n", __PACKAGE__, __PACKAGE__->VERSION; exit },
"color!" => \(my $color = 1),
"shebang=s" => \(my $custom_shebang),
"exclude-strip=s@" => \(my $exclude_strip),
"no-strip|no-perl-strip" => \(my $no_perl_strip),
"cache=s" => \(my $cache),
) or exit 1;
$self->{script} = shift @argv or do { warn "Missing script.\n"; $self->show_help; exit 1 };
$self->{dir} = $self->build_dir($dir);
$self->{output} = $output;
$self->{quiet} = $quiet;
$self->{strict} = $strict;
$self->{color} = $color;
$self->{custom_shebang} = $custom_shebang;
$self->{exclude_strip} = [map { qr/$_/ } ($exclude_strip || [])->@*];
$self->{exclude} = [];
if (!$no_perl_strip) {
$self->{perl_strip} = Perl::Strip->new($cache ? (cache => $cache) : ());
}
if ($exclude) {
for my $e (split /,/, $exclude) {
my $dist = Distribution::Metadata->new_from_module(
$e, inc => $self->{dir},
);
if (my $files = $dist->files) {
push $self->{exclude}->@*, $files->@*;
} else {
$self->warning("Missing $e in $dir");
}
}
}
lib/App/FatPacker/Simple.pm view on Meta::CPAN
}
# In order not to depend on App::FatPacker internals,
# we use only App::FatPacker::fatpack_code method.
sub fatpack_file ($self, $file) {
my ($shebang, $script) = $self->load_main_script($file);
$shebang = $self->{custom_shebang} if $self->{custom_shebang};
my %files;
$self->collect_files($_, \%files) for $self->{dir}->@*;
my $fatpacker = App::FatPacker->new;
return join "\n", $shebang, $fatpacker->fatpack_code(\%files), $script;
}
# almost copy from App::FatPacker::load_main_script
sub load_main_script ($self, $file) {
open my $fh, "<", $file or die "Cannot open '$file': $!\n";
my @lines = <$fh>;
my @shebang;
if (@lines && index($lines[0], '#!') == 0) {
while (1) {
push @shebang, shift @lines;
last if $shebang[-1] =~ m{^\#\!.*perl};
}
}
((join "", @shebang), (join "", @lines));
}
sub load_file ($self, $absolute, $relative, $original) {
my $content = do {
open my $fh, "<", $absolute or die "Cannot open '$absolute': $!\n";
local $/; <$fh>;
};
if ($self->{perl_strip} and !grep { $original =~ $_ } $self->{exclude_strip}->@*) {
$self->debug("fatpack $relative (with perl-strip)");
return $self->{perl_strip}->strip($content);
} else {
$self->debug("fatpack $relative (without perl-strip)");
return $content;
}
}
sub collect_files ($self, $dir, $files) {
my $absolute_dir = Cwd::abs_path($dir);
# When $dir is not an archlib,
# and we are about to search $dir/archlib, skip it!
# because $dir/archlib itself will be searched another time.
my $skip_dir = File::Spec->catdir($absolute_dir, $Config{archname});
$skip_dir = qr/\Q$skip_dir\E/;
my $find = sub (@) {
return unless -f $_;
for my $ignore ($IGNORE_FILE->@*) {
$_ =~ $ignore and return;
}
my $original = $_;
my $absolute = Cwd::abs_path($original);
return if $absolute =~ $skip_dir;
my $relative = File::Spec::Unix->abs2rel($absolute, $absolute_dir);
for my $exclude ($self->{exclude}->@*) {
if ($absolute eq $exclude) {
$self->debug("exclude $relative");
return;
}
}
if (!/\.(?:pm|ix|al|pl)$/) {
$self->warning("skip non perl module file $relative");
return;
}
$files->{$relative} = $self->load_file($absolute, $relative, $original);
};
File::Find::find({wanted => $find, no_chdir => 1}, $dir);
}
sub build_dir ($self, $dir_string) {
my @dir;
for my $d (grep -d, split /,/, $dir_string) {
my $try = File::Spec->catdir($d, "lib/perl5");
if (-d $try) {
push @dir, $try, File::Spec->catdir($try, $Config{archname});
} else {
push @dir, $d, File::Spec->catdir($d, $Config{archname});
}
}
return [ grep -d, @dir ];
}
1;
__END__
=for stopwords fatpack fatpacks fatpacked deps
=encoding utf-8
=head1 NAME
App::FatPacker::Simple - only fatpack a script
=head1 SYNOPSIS
$ fatpack-simple script.pl
=head1 DESCRIPTION
App::FatPacker::Simple or its frontend C<fatpack-simple> helps you
fatpack a script when B<YOU> understand the whole dependencies of it.
For tutorial, please look at L<App::FatPacker::Simple::Tutorial>.
=head1 MOTIVATION
App::FatPacker::Simple is an alternative for L<App::FatPacker>'s
C<fatpack file> command.
Let me explain why I wrote this module.
L<App::FatPacker> brings more portability to Perl, that is totally awesome.
As far as I understand, App::FatPacker does 3 things:
( run in 1.551 second using v1.01-cache-2.11-cpan-64ef6c95b5d )