diff options
Diffstat (limited to 'lib/Carton/Packer.pm')
-rw-r--r-- | lib/Carton/Packer.pm | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/lib/Carton/Packer.pm b/lib/Carton/Packer.pm new file mode 100644 index 0000000..dc1a2cf --- /dev/null +++ b/lib/Carton/Packer.pm @@ -0,0 +1,97 @@ +package Carton::Packer; +use Class::Tiny; +use warnings NONFATAL => 'all'; +use App::FatPacker; +use File::pushd (); +use Path::Tiny (); +use CPAN::Meta (); +use File::Find (); + +sub fatpack_carton { + my($self, $dir) = @_; + + my $temp = Path::Tiny->tempdir; + my $pushd = File::pushd::pushd $temp; + + my $file = $temp->child('carton.pre.pl'); + + $file->spew(<<'EOF'); +#!/usr/bin/env perl +use strict; +use 5.008001; +use Carton::CLI; +$Carton::Fatpacked = 1; +exit Carton::CLI->new->run(@ARGV); +EOF + + my $fatpacked = $self->do_fatpack($file); + + my $executable = $dir->child('carton'); + warn "Bundling $executable\n"; + + $dir->mkpath; + $executable->spew($fatpacked); + chmod 0755, $executable; +} + +sub do_fatpack { + my($self, $file) = @_; + + my $packer = App::FatPacker->new; + + my @modules = split /\r?\n/, $packer->trace(args => [$file], use => $self->required_modules); + my @packlists = $packer->packlists_containing(\@modules); + $packer->packlists_to_tree(Path::Tiny->new('fatlib')->absolute, \@packlists); + + my $fatpacked = do { + local $SIG{__WARN__} = sub {}; + $packer->fatpack_file($file); + }; + + # HACK: File::Spec bundled into arch in < 5.16, but is loadable as pure-perl + use Config; + $fatpacked =~ s/\$fatpacked{"$Config{archname}\/(Cwd|File)/\$fatpacked{"$1/g; + + $fatpacked; +} + +sub required_modules { + my($self, $packer) = @_; + + my $meta = $self->installed_meta('Carton') + or die "Couldn't find install metadata for Carton"; + + my %excludes = ( + perl => 1, + 'ExtUtils::MakeMaker' => 1, + 'Module::Build' => 1, + ); + + my @requirements = grep !$excludes{$_}, + $meta->effective_prereqs->requirements_for('runtime', 'requires')->required_modules; + + return \@requirements; +} + +sub installed_meta { + my($self, $dist) = @_; + + my @meta; + my $finder = sub { + if (m!\b$dist-.*[\\/]MYMETA.json!) { + my $meta = CPAN::Meta->load_file($_); + push @meta, $meta if $meta->name eq $dist; + } + }; + + my @meta_dirs = grep -d, map "$_/.meta", @INC; + File::Find::find({ wanted => $finder, no_chdir => 1 }, @meta_dirs) + if @meta_dirs; + + # return the latest version + @meta = sort { version->new($b->version) cmp version->new($a->version) } @meta; + + return $meta[0]; +} + +1; |