├── dist.ini ├── xt ├── mirror │ ├── modules │ │ ├── 02packages.details.txt.gz │ │ └── 02packages.details.txt │ └── authors │ │ └── id │ │ └── M │ │ └── MI │ │ └── MIYAGAWA │ │ └── Hash-MultiValue-0.08.tar.gz ├── cli │ ├── version.t │ ├── no_cpanfile.t │ ├── bundle.t │ ├── perl.t │ ├── tree.t │ ├── json_pp.t │ ├── freeze.t │ ├── mismatch.t │ ├── deps_phase.t │ ├── help.t │ ├── subdir.t │ ├── deployment.t │ ├── mirror.t │ ├── cpanfile.t │ ├── install.t │ ├── snapshot.t │ ├── without.t │ ├── update.t │ ├── check.t │ └── exec.t └── CLI.pm ├── .gitignore ├── lib ├── Carton │ ├── Doc │ │ ├── Version.pod │ │ ├── Show.pod │ │ ├── Tree.pod │ │ ├── List.pod │ │ ├── Bundle.pod │ │ ├── Exec.pod │ │ ├── Check.pod │ │ ├── Upgrading.pod │ │ ├── Update.pod │ │ ├── Install.pod │ │ └── FAQ.pod │ ├── Package.pm │ ├── Dependency.pm │ ├── Dist │ │ └── Core.pm │ ├── Mirror.pm │ ├── Util.pm │ ├── Error.pm │ ├── Dist.pm │ ├── Snapshot │ │ ├── Emitter.pm │ │ └── Parser.pm │ ├── CPANfile.pm │ ├── Index.pm │ ├── Tree.pm │ ├── Packer.pm │ ├── Environment.pm │ ├── Builder.pm │ ├── Snapshot.pm │ └── CLI.pm └── Carton.pm ├── .travis.yml ├── script └── carton ├── cpanfile ├── Makefile.PL ├── META.json ├── README.md ├── Changes └── LICENSE /dist.ini: -------------------------------------------------------------------------------- 1 | name = Carton 2 | [@Milla] 3 | installer = MakeMaker 4 | -------------------------------------------------------------------------------- /xt/mirror/modules/02packages.details.txt.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hachi/carton/master/xt/mirror/modules/02packages.details.txt.gz -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | MYMETA.* 2 | META.yml 3 | !META.json 4 | .carton/ 5 | local/ 6 | cpanfile.snapshot 7 | /carton-* 8 | /.build 9 | /vendor 10 | /blib 11 | -------------------------------------------------------------------------------- /xt/mirror/authors/id/M/MI/MIYAGAWA/Hash-MultiValue-0.08.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hachi/carton/master/xt/mirror/authors/id/M/MI/MIYAGAWA/Hash-MultiValue-0.08.tar.gz -------------------------------------------------------------------------------- /xt/cli/version.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | 4 | use xt::CLI; 5 | 6 | my $app = cli(); 7 | $app->run("version"); 8 | 9 | like $app->stdout, qr/carton $Carton::VERSION/; 10 | 11 | done_testing; 12 | 13 | -------------------------------------------------------------------------------- /lib/Carton/Doc/Version.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | Carton::Doc::Version - Display version 4 | 5 | =head1 SYNOPSIS 6 | 7 | carton version 8 | 9 | =head1 DESCRIPTION 10 | 11 | This command displays the current version number of carton. 12 | -------------------------------------------------------------------------------- /lib/Carton/Doc/Show.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | Carton::Doc::Show - Show the module information 4 | 5 | =head1 SYNOPSIS 6 | 7 | carton show Module 8 | 9 | =head1 DESCRIPTION 10 | 11 | Displays the information about modules, distribution and its versions. 12 | 13 | -------------------------------------------------------------------------------- /xt/cli/no_cpanfile.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | { 6 | my $app = cli(); 7 | $app->run("install"); 8 | like $app->stderr, qr/Can't locate cpanfile/; 9 | is $app->exit_code, 255; 10 | } 11 | 12 | done_testing; 13 | 14 | -------------------------------------------------------------------------------- /lib/Carton/Doc/Tree.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | Carton::Doc::Tree - Show the tree of dependency graph 4 | 5 | =head1 SYNOPSIS 6 | 7 | carton tree 8 | 9 | =head1 DESCRIPTION 10 | 11 | Displays the tree representation of dependency graph for your application. 12 | 13 | 14 | -------------------------------------------------------------------------------- /lib/Carton/Package.pm: -------------------------------------------------------------------------------- 1 | package Carton::Package; 2 | use strict; 3 | use Class::Tiny qw( name version pathname ); 4 | 5 | sub BUILDARGS { 6 | my($class, @args) = @_; 7 | return { name => $args[0], version => $args[1], pathname => $args[2] }; 8 | } 9 | 10 | 1; 11 | 12 | 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: perl 2 | perl: 3 | - '5.20' 4 | - 5.18 5 | - 5.16 6 | - 5.14 7 | - 5.12 8 | - '5.10' 9 | - 5.8 10 | before_install: 11 | - perlbrew install-cpanm -f 12 | install: 13 | - (cpanm --installdeps --with-develop -nq .) || cat ~/.cpanm/build.log 14 | script: 15 | - prove -lr xt -j9 16 | sudo: false 17 | -------------------------------------------------------------------------------- /xt/cli/bundle.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | { 6 | my $app = cli(); 7 | 8 | $app->write_cpanfile(<run("install"); 13 | $app->run("bundle"); 14 | 15 | ok -f ($app->dir . "/vendor/cache/authors/id/D/DO/DOY/Try-Tiny-0.12.tar.gz"); 16 | } 17 | 18 | done_testing; 19 | 20 | -------------------------------------------------------------------------------- /xt/cli/perl.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | { 6 | my $app = cli(); 7 | 8 | $app->write_cpanfile(<run("install"); 14 | like $app->stdout, qr/Complete/; 15 | 16 | $app->run("list"); 17 | like $app->stdout, qr/Hash-MultiValue-/; 18 | } 19 | 20 | done_testing; 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /lib/Carton/Dependency.pm: -------------------------------------------------------------------------------- 1 | package Carton::Dependency; 2 | use strict; 3 | use Class::Tiny { 4 | module => undef, 5 | requirement => undef, 6 | dist => undef, 7 | }; 8 | 9 | sub requirements { shift->dist->requirements(@_) } 10 | 11 | sub distname { 12 | my $self = shift; 13 | $self->dist->name; 14 | } 15 | 16 | sub version { 17 | my $self = shift; 18 | $self->dist->version_for($self->module); 19 | } 20 | 21 | 1; 22 | -------------------------------------------------------------------------------- /lib/Carton/Dist/Core.pm: -------------------------------------------------------------------------------- 1 | package Carton::Dist::Core; 2 | use strict; 3 | use parent 'Carton::Dist'; 4 | 5 | use Class::Tiny qw( module_version ); 6 | 7 | sub BUILDARGS { 8 | my($class, %args) = @_; 9 | 10 | # TODO represent dual-life 11 | $args{name} =~ s/::/-/g; 12 | 13 | \%args; 14 | } 15 | 16 | sub is_core { 1 } 17 | 18 | sub version_for { 19 | my($self, $module) = @_; 20 | $self->module_version; 21 | } 22 | 23 | 1; 24 | -------------------------------------------------------------------------------- /xt/cli/tree.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | { 6 | my $app = cli(); 7 | 8 | $app->write_cpanfile(<run("install"); 13 | $app->run("tree"); 14 | 15 | is $app->exit_code, 0; 16 | like $app->stdout, qr/^HTML::Parser \(HTML-Parser-/m; 17 | like $app->stdout, qr/^ HTML::Tagset \(HTML-Tagset-/m; 18 | } 19 | 20 | done_testing; 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /lib/Carton/Mirror.pm: -------------------------------------------------------------------------------- 1 | package Carton::Mirror; 2 | use strict; 3 | use Class::Tiny qw( url ); 4 | 5 | our $DefaultMirror = 'http://cpan.metacpan.org/'; 6 | 7 | sub BUILDARGS { 8 | my($class, $url) = @_; 9 | return { url => $url }; 10 | } 11 | 12 | sub default { 13 | my $class = shift; 14 | $class->new($DefaultMirror); 15 | } 16 | 17 | sub is_default { 18 | my $self = shift; 19 | $self->url eq $DefaultMirror; 20 | } 21 | 22 | 1; 23 | 24 | -------------------------------------------------------------------------------- /xt/cli/json_pp.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | plan skip_all => "perl <= 5.14" if $] >= 5.015; 6 | 7 | { 8 | my $app = cli(); 9 | 10 | $app->write_cpanfile(<run("install"); 16 | $app->clean_local; 17 | 18 | $app->run("install", "--deployment"); 19 | unlike $app->stderr, qr/JSON::PP is not in range/; 20 | } 21 | 22 | done_testing; 23 | 24 | -------------------------------------------------------------------------------- /xt/cli/freeze.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | { 6 | my $app = cli(); 7 | 8 | $app->write_cpanfile(<run("install"); 13 | $app->run("list"); 14 | like $app->stdout, qr/Try-Tiny-0\.11/; 15 | 16 | $app->clean_local; 17 | 18 | $app->run("install"); 19 | $app->run("list"); 20 | like $app->stdout, qr/Try-Tiny-0\.11/; 21 | } 22 | 23 | done_testing; 24 | 25 | -------------------------------------------------------------------------------- /script/carton: -------------------------------------------------------------------------------- 1 | #!perl 2 | use strict; 3 | use 5.008001; 4 | use Carton::CLI; 5 | 6 | exit Carton::CLI->new->run(@ARGV); 7 | 8 | __END__ 9 | 10 | =head1 NAME 11 | 12 | carton - Perl module dependency manager 13 | 14 | =head1 SYNOPSIS 15 | 16 | > carton install 17 | > carton exec ./myscript 18 | 19 | =head1 DESCRIPTION 20 | 21 | For more documentation, refer to L by running C or C. 22 | 23 | =head1 SEE ALSO 24 | 25 | L 26 | 27 | =cut 28 | -------------------------------------------------------------------------------- /xt/cli/mismatch.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | { 6 | my $app = cli(); 7 | 8 | $app->write_cpanfile(< '== 2.139'; 10 | requires 'Test::Differences' => '== 0.61'; 11 | EOF 12 | 13 | $app->run("install"); 14 | $app->run("list"); 15 | 16 | like $app->stdout, qr/Data-Dumper-2\.139/; 17 | like $app->stdout, qr/Test-Differences-0\.61/; 18 | 19 | $app->run("check"); 20 | like $app->stdout, qr/are satisfied/; 21 | } 22 | 23 | done_testing; 24 | 25 | -------------------------------------------------------------------------------- /xt/cli/deps_phase.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | { 6 | my $app = cli(); 7 | 8 | $app->write_cpanfile(< sub { 10 | requires 'Test::NoWarnings'; 11 | recommends 'Test::Pretty'; 12 | }; 13 | on develop => sub { 14 | requires 'Path::Tiny'; 15 | }; 16 | EOF 17 | 18 | $app->run("install"); 19 | 20 | $app->run("list"); 21 | like $app->stdout, qr/Test-NoWarnings/; 22 | like $app->stdout, qr/Path-Tiny/; 23 | unlike $app->stdout, qr/Test-Pretty/; 24 | } 25 | 26 | done_testing; 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /lib/Carton/Doc/List.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | Carton::Doc::List - List dependencies tracked in the cpanfile.snapshot file 4 | 5 | =head1 SYNOPSIS 6 | 7 | carton list 8 | 9 | =head1 DESCRIPTION 10 | 11 | List the dependencies and version information tracked in the 12 | I file. This command by default displays the name of the 13 | distribution (e.g. I) in a flat list. 14 | 15 | =head1 OPTIONS 16 | 17 | =over 4 18 | 19 | =item --distfile 20 | 21 | Displays the list of distributions in a distfile format (i.e. C) 22 | 23 | =back 24 | -------------------------------------------------------------------------------- /xt/cli/help.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | { 6 | my $app = cli(); 7 | $app->run("help"); 8 | like $app->stdout, qr/Carton - Perl module/; 9 | 10 | $app->run("-h"); 11 | like $app->stdout, qr/Carton - Perl module/; 12 | 13 | $app->run("help", "install"); 14 | like $app->stdout, qr/Install the dependencies/; 15 | 16 | $app->run("install", "-h"); 17 | like $app->stdout, qr/Install the dependencies/; 18 | 19 | $app->run("help", "foobarbaz"); 20 | is $app->stdout, ''; 21 | like $app->stderr, qr/No documentation found/; 22 | } 23 | 24 | done_testing; 25 | 26 | -------------------------------------------------------------------------------- /xt/cli/subdir.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | subtest 'carton exec in subdir', sub { 6 | my $app = cli(); 7 | $app->write_cpanfile(<run('install'); 11 | 12 | $app->dir->child('x')->mkpath; 13 | 14 | $app->run_in_dir('x' => 'list'); 15 | like $app->stdout, qr/Try-Tiny/; 16 | 17 | $app->run_in_dir('x' => 'check'); 18 | like $app->stdout, qr/are satisfied/; 19 | 20 | $app->run_in_dir('x' => 'install'); 21 | like $app->stdout, qr/Complete/; 22 | unlike $app->stderr, qr/failed/; 23 | }; 24 | 25 | done_testing; 26 | -------------------------------------------------------------------------------- /lib/Carton/Util.pm: -------------------------------------------------------------------------------- 1 | package Carton::Util; 2 | use strict; 3 | use warnings; 4 | 5 | sub load_json { 6 | my $file = shift; 7 | 8 | open my $fh, "<", $file or die "$file: $!"; 9 | from_json(join '', <$fh>); 10 | } 11 | 12 | sub dump_json { 13 | my($data, $file) = @_; 14 | 15 | open my $fh, ">", $file or die "$file: $!"; 16 | binmode $fh; 17 | print $fh to_json($data); 18 | } 19 | 20 | sub from_json { 21 | require JSON; 22 | JSON::decode_json(@_); 23 | } 24 | 25 | sub to_json { 26 | my($data) = @_; 27 | require JSON; 28 | JSON->new->utf8->pretty->canonical->encode($data); 29 | } 30 | 31 | 1; 32 | -------------------------------------------------------------------------------- /xt/cli/deployment.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | { 6 | my $app = cli(); 7 | $app->write_cpanfile(<run("install", "--deployment"); 12 | like $app->stderr, qr/deployment requires cpanfile\.snapshot/; 13 | 14 | $app->run("install"); 15 | $app->clean_local; 16 | 17 | $app->run("install", "--deployment"); 18 | $app->run("list"); 19 | like $app->stdout, qr/Try-Tiny-0\.11/; 20 | 21 | $app->run("exec", "perl", "-e", "use Try::Tiny 2;"); 22 | like $app->stderr, qr/Try::Tiny.* version 0\.11/; 23 | } 24 | 25 | done_testing; 26 | 27 | -------------------------------------------------------------------------------- /lib/Carton/Doc/Bundle.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | Carton::Doc::Bundle - Bundle cached tarballs in vendor/cache 4 | 5 | =head1 SYNOPSIS 6 | 7 | carton bundle 8 | 9 | =head1 DESCRIPTION 10 | 11 | This command bundles cached tarballs into C 12 | directory. These tarballs have been cached in C while 13 | resolving dependencies in the snapshot file.snapshot. 14 | 15 | Bundled modules can be committed to a version control system, or 16 | transferred to another host with scp/rsync etc. to use with C. 18 | 19 | =head1 OPTIONS 20 | 21 | =over 4 22 | 23 | =item --no-fatpack 24 | 25 | Skip generating fatpacked C executable in C. 26 | 27 | =back 28 | -------------------------------------------------------------------------------- /lib/Carton/Doc/Exec.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | Carton::Doc::Exec - execute your script in a carton local environment 4 | 5 | =head1 SYNOPSIS 6 | 7 | carton exec perl myscript.pl 8 | 9 | =head1 DESCRIPTION 10 | 11 | This command allows you to run your script in an isolated carton local 12 | environment, which means the perl 5 library path C<@INC> are the only 13 | ones from perl's core library path, carton's library path 14 | (i.e. C) and the current directory. 15 | 16 | This is useful to make sure your scripts and application use the exact 17 | same versions of the modules in your library path, and are not using 18 | any of the modules you accidentally installed into your system perl or 19 | perlbrew's site library path. 20 | -------------------------------------------------------------------------------- /lib/Carton/Doc/Check.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | Carton::Doc::Check - Check if your cpanfile and local environment are in sync 4 | 5 | =head1 SYNOPSIS 6 | 7 | carton check 8 | 9 | =head1 DESCRIPTION 10 | 11 | This command checks the consistency between your C, 12 | C and the local environment. 13 | 14 | =head2 MISSING MODULES 15 | 16 | If one or more of the modules specified in your I are not 17 | found in your snapshot, C will warn you about this: 18 | 19 | $ carton check 20 | Following dependencies are not satisfied. 21 | JSON has version 2.51. Needs 2.52 22 | Run `carton install` to install them. 23 | 24 | You can run C again to reinstall these missing dependencies. 25 | -------------------------------------------------------------------------------- /xt/cli/mirror.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | my $cwd = Path::Tiny->cwd; 6 | 7 | { 8 | my $app = cli(); 9 | 10 | $app->write_cpanfile(<run("install"); 16 | 17 | $app->run("list"); 18 | like $app->stdout, qr/^Hash-MultiValue-0.08/m; 19 | } 20 | 21 | { 22 | # fallback to CPAN 23 | my $app = cli(); 24 | $app->write_cpanfile(<run("install"); 30 | 31 | $app->run("list"); 32 | like $app->stdout, qr/^PSGI-/m; 33 | } 34 | 35 | done_testing; 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /lib/Carton/Error.pm: -------------------------------------------------------------------------------- 1 | package Carton::Error; 2 | use strict; 3 | use overload '""' => sub { $_[0]->error }; 4 | use Carp; 5 | 6 | sub throw { 7 | my($class, @args) = @_; 8 | die $class->new(@args); 9 | } 10 | 11 | sub rethrow { 12 | die $_[0]; 13 | } 14 | 15 | sub new { 16 | my($class, %args) = @_; 17 | bless \%args, $class; 18 | } 19 | 20 | sub error { 21 | $_[0]->{error} || ref $_[0]; 22 | } 23 | 24 | package Carton::Error::CommandNotFound; 25 | use parent 'Carton::Error'; 26 | 27 | package Carton::Error::CommandExit; 28 | use parent 'Carton::Error'; 29 | sub code { $_[0]->{code} } 30 | 31 | package Carton::Error::CPANfileNotFound; 32 | use parent 'Carton::Error'; 33 | 34 | package Carton::Error::SnapshotParseError; 35 | use parent 'Carton::Error'; 36 | sub path { $_[0]->{path} } 37 | 38 | package Carton::Error::SnapshotNotFound; 39 | use parent 'Carton::Error'; 40 | sub path { $_[0]->{path} } 41 | 42 | 1; 43 | -------------------------------------------------------------------------------- /lib/Carton/Dist.pm: -------------------------------------------------------------------------------- 1 | package Carton::Dist; 2 | use strict; 3 | use Class::Tiny { 4 | name => undef, 5 | pathname => undef, 6 | provides => sub { +{} }, 7 | requirements => sub { $_[0]->_build_requirements }, 8 | }; 9 | 10 | use CPAN::Meta; 11 | 12 | sub add_string_requirement { shift->requirements->add_string_requirement(@_) } 13 | sub required_modules { shift->requirements->required_modules(@_) } 14 | sub requirements_for_module { shift->requirements->requirements_for_module(@_) } 15 | 16 | sub is_core { 0 } 17 | 18 | sub distfile { 19 | my $self = shift; 20 | $self->pathname; 21 | } 22 | 23 | sub _build_requirements { 24 | CPAN::Meta::Requirements->new; 25 | } 26 | 27 | sub provides_module { 28 | my($self, $module) = @_; 29 | exists $self->provides->{$module}; 30 | } 31 | 32 | sub version_for { 33 | my($self, $module) = @_; 34 | $self->provides->{$module}{version}; 35 | } 36 | 37 | 1; 38 | -------------------------------------------------------------------------------- /cpanfile: -------------------------------------------------------------------------------- 1 | on configure => sub { 2 | requires 'version', 0.77; 3 | }; 4 | 5 | requires 'perl', '5.8.5'; 6 | 7 | requires 'JSON', 2.53; 8 | requires 'Module::Metadata', 1.000003; 9 | requires 'Module::CPANfile', 0.9031; 10 | 11 | requires 'Try::Tiny', 0.09; 12 | requires 'parent', 0.223; 13 | requires 'Getopt::Long', 2.39; 14 | requires 'Class::Tiny', 1.001; 15 | requires 'Path::Tiny', 0.033; 16 | 17 | # MYMETA support 18 | requires 'App::cpanminus', 1.7030; 19 | requires 'ExtUtils::MakeMaker', 6.64; 20 | requires 'Module::Build', 0.4004; 21 | 22 | requires 'CPAN::Meta', 2.120921; 23 | requires 'CPAN::Meta::Requirements', 2.121; 24 | requires 'Module::CoreList'; 25 | 26 | # for fatpack 27 | recommends 'Module::Reader', 0.002; 28 | recommends 'File::pushd'; 29 | recommends 'App::FatPacker', 0.009018; 30 | 31 | on develop => sub { 32 | requires 'Test::More', 0.90; 33 | requires 'Test::Requires'; 34 | requires 'Capture::Tiny'; 35 | }; 36 | -------------------------------------------------------------------------------- /lib/Carton/Snapshot/Emitter.pm: -------------------------------------------------------------------------------- 1 | package Carton::Snapshot::Emitter; 2 | use Class::Tiny; 3 | use warnings NONFATAL => 'all'; 4 | 5 | sub emit { 6 | my($self, $snapshot) = @_; 7 | 8 | my $data = ''; 9 | $data .= "# carton snapshot format: version @{[$snapshot->version]}\n"; 10 | $data .= "DISTRIBUTIONS\n"; 11 | 12 | for my $dist (sort { $a->name cmp $b->name } $snapshot->distributions) { 13 | $data .= " @{[$dist->name]}\n"; 14 | $data .= " pathname: @{[$dist->pathname]}\n"; 15 | 16 | $data .= " provides:\n"; 17 | for my $package (sort keys %{$dist->provides}) { 18 | $data .= " $package @{[$dist->provides->{$package}{version} || 'undef' ]}\n"; 19 | } 20 | 21 | $data .= " requirements:\n"; 22 | for my $module (sort $dist->required_modules) { 23 | $data .= " $module @{[ $dist->requirements_for_module($module) || '0' ]}\n"; 24 | } 25 | } 26 | 27 | $data; 28 | } 29 | 30 | 1; 31 | -------------------------------------------------------------------------------- /lib/Carton/CPANfile.pm: -------------------------------------------------------------------------------- 1 | package Carton::CPANfile; 2 | use Path::Tiny (); 3 | use Module::CPANfile; 4 | 5 | use overload q{""} => sub { $_[0]->stringify }, fallback => 1; 6 | 7 | use subs 'path'; 8 | 9 | use Class::Tiny { 10 | path => undef, 11 | _cpanfile => undef, 12 | requirements => sub { $_[0]->_build_requirements }, 13 | }; 14 | 15 | sub stringify { shift->path->stringify(@_) } 16 | sub dirname { shift->path->dirname(@_) } 17 | sub prereqs { shift->_cpanfile->prereqs(@_) } 18 | sub required_modules { shift->requirements->required_modules(@_) } 19 | sub requirements_for_module { shift->requirements->requirements_for_module(@_) } 20 | 21 | sub path { 22 | my $self = shift; 23 | if (@_) { 24 | $self->{path} = Path::Tiny->new($_[0]); 25 | } else { 26 | $self->{path}; 27 | } 28 | } 29 | 30 | sub load { 31 | my $self = shift; 32 | $self->_cpanfile( Module::CPANfile->load($self->path) ); 33 | } 34 | 35 | sub _build_requirements { 36 | my $self = shift; 37 | my $reqs = CPAN::Meta::Requirements->new; 38 | $reqs->add_requirements($self->prereqs->requirements_for($_, 'requires')) 39 | for qw( configure build runtime test develop ); 40 | $reqs->clear_requirement('perl'); 41 | $reqs; 42 | } 43 | 44 | 1; 45 | -------------------------------------------------------------------------------- /xt/cli/cpanfile.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | subtest 'carton install --cpanfile' => sub { 6 | my $app = cli(); 7 | $app->write_file('cpanfile.foo', <run("install", "--cpanfile", "cpanfile.foo"); 11 | $app->run("check", "--cpanfile", "cpanfile.foo"); 12 | 13 | ok !$app->dir->child('cpanfile.snapshot')->exists; 14 | ok $app->dir->child('cpanfile.foo.snapshot')->exists; 15 | 16 | like $app->stdout, qr/are satisfied/; 17 | 18 | local $ENV{PERL_CARTON_CPANFILE} = $app->dir->child('cpanfile.foo')->absolute; 19 | 20 | $app->run("list"); 21 | like $app->stdout, qr/Try-Tiny-0\.11/; 22 | 23 | $app->run("exec", "perl", "-e", "use Try::Tiny\ 1"); 24 | like $app->stderr, qr/Try::Tiny .* 0\.11/; 25 | }; 26 | 27 | subtest 'PERL_CARTON_CPANFILE' => sub { 28 | my $app = cli(); 29 | 30 | local $ENV{PERL_CARTON_CPANFILE} = $app->dir->child('cpanfile.foo')->absolute; 31 | 32 | $app->write_file('cpanfile.foo', <run("install"); 37 | $app->run("list"); 38 | 39 | like $app->stdout, qr/Try-Tiny-0\.11/; 40 | ok $app->dir->child('cpanfile.foo.snapshot')->exists; 41 | }; 42 | 43 | done_testing; 44 | 45 | -------------------------------------------------------------------------------- /lib/Carton/Doc/Upgrading.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | Carton::Doc::Upgrading - Upgrading document 4 | 5 | =head1 UPGRADING 6 | 7 | Carton adds, changes and deprecates some features between major 8 | releases in backward incompatible ways. Here's the list of major 9 | changes between versions. See C file for more details. 10 | 11 | =head2 v0.9 to v1.0 12 | 13 | =over 4 14 | 15 | =item * 16 | 17 | C is deprecated. You must pass the optional include 18 | path to perl interpreter in the normal way, like: 19 | 20 | carton exec perl -Ilib myscript 21 | 22 | Or make your script to take its own C<-I> option, like many command line 23 | launcher does (i.e. plackup, prove) 24 | 25 | carton exec plackup -Ilib myapp.psgi 26 | 27 | =item * 28 | 29 | C is now C. Its name and file format 30 | has been changed. There's no automatic migration, but you can do: 31 | 32 | # run with Carton v0.9.64 33 | > carton install 34 | 35 | # upgrade to Carton v1.0 36 | > cpanm Carton 37 | > carton install 38 | > git add cpanfile.snapshot 39 | > git rm carton.lock 40 | 41 | This process will most likely preserve modules in your local library. 42 | 43 | =item * 44 | 45 | cpanfile is now a requirement, and extracting requirements from build 46 | files (C, C) is not supported. 47 | 48 | =back 49 | -------------------------------------------------------------------------------- /lib/Carton/Doc/Update.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | Carton::Doc::Update - Update the dependencies 4 | 5 | =head1 SYNOPSIS 6 | 7 | carton update [module] 8 | 9 | =head1 DESCRIPTION 10 | 11 | Update the dependencies version for your application. 12 | 13 | Carton is designed to update your dependency in a conservative way, 14 | meaning that it doesn't update modules that aren't explicitly required 15 | to. 16 | 17 | C is a command to explicitly update one or all of 18 | modules in your cpanfile to the latest available that satisfies the 19 | requirements in cpanfile. 20 | 21 | =head1 EXAMPLE 22 | 23 | Suppose you have a cpanfile with: 24 | 25 | requires 'DBI', '1.600'; 26 | requires 'Plack', '== 1.0011'; 27 | 28 | and then run C to get DBI 1.610 (the latest at that 29 | time) and Plack 1.0011 (as specified in the requirement). 30 | 31 | A few weeks later, DBI and Plack have been updated a couple of 32 | times. Running C I update the versions, because 33 | the installed versions satisfy the requirements in C. 34 | 35 | Running C will update DBI to the latest version, say 36 | 1.611, because the version still satisfies the requirement. However, 37 | it won't update Plack's version, since whatever latest version on CPAN 38 | will not satisfy the Plack's requirement C<== 1.0011> because it wants 39 | an exact version. 40 | 41 | -------------------------------------------------------------------------------- /xt/cli/install.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | subtest 'carton install with version range' => sub { 6 | my $app = cli(); 7 | $app->write_cpanfile(<run("install"); 12 | $app->run("tree"); 13 | like $app->stdout, qr/Try::Tiny/; 14 | unlike $app->stderr, qr/Could not parse snapshot file/; 15 | }; 16 | 17 | subtest 'meta info for ancient modules' => sub { 18 | my $app = cli(); 19 | $app->write_cpanfile(<run("install"); 24 | $app->run("list"); 25 | 26 | like $app->stdout, qr/Algorithm-Diff/; 27 | }; 28 | 29 | subtest 'meta info for modules with version->declare' => sub { 30 | my $app = cli(); 31 | $app->write_cpanfile(<run("install"); 36 | $app->run("check"); 37 | 38 | like $app->stdout, qr/are satisfied/; 39 | unlike $app->stderr, qr/is not installed/; 40 | }; 41 | 42 | subtest 'meta info for modules with qv()' => sub { 43 | my $app = cli(); 44 | $app->write_cpanfile(<run("install"); 49 | $app->run("check"); 50 | 51 | like $app->stdout, qr/are satisfied/; 52 | unlike $app->stderr, qr/is not installed/; 53 | }; 54 | 55 | done_testing; 56 | 57 | -------------------------------------------------------------------------------- /xt/CLI.pm: -------------------------------------------------------------------------------- 1 | package xt::CLI; 2 | use strict; 3 | use base qw(Exporter); 4 | our @EXPORT = qw(run cli); 5 | 6 | use Test::Requires qw( Capture::Tiny File::pushd ); 7 | 8 | sub cli { 9 | my $cli = Carton::CLI::Tested->new; 10 | $cli->dir( Path::Tiny->tempdir(CLEANUP => !$ENV{NO_CLEANUP}) ); 11 | warn "Temp directory: ", $cli->dir, "\n" if $ENV{NO_CLEANUP}; 12 | $cli; 13 | } 14 | 15 | package Carton::CLI::Tested; 16 | use Carton::CLI; 17 | use Capture::Tiny qw(capture); 18 | use File::pushd (); 19 | use Path::Tiny; 20 | 21 | $Carton::CLI::UseSystem = 1; 22 | 23 | use Class::Tiny qw( dir stdout stderr exit_code ); 24 | 25 | sub write_file { 26 | my($self, $file, @args) = @_; 27 | $self->dir->child($file)->spew(@args); 28 | } 29 | 30 | sub write_cpanfile { 31 | my($self, @args) = @_; 32 | $self->write_file(cpanfile => @args); 33 | } 34 | 35 | sub run_in_dir { 36 | my($self, $dir, @args) = @_; 37 | local $self->{dir} = $self->dir->child($dir); 38 | $self->run(@args); 39 | } 40 | 41 | sub run { 42 | my($self, @args) = @_; 43 | 44 | my $pushd = File::pushd::pushd $self->dir; 45 | 46 | my @capture = capture { 47 | my $code = eval { Carton::CLI->new->run(@args) }; 48 | $self->exit_code($@ ? 255 : $code); 49 | }; 50 | 51 | $self->stdout($capture[0]); 52 | $self->stderr($capture[1]); 53 | } 54 | 55 | sub clean_local { 56 | my $self = shift; 57 | $self->dir->child("local")->remove_tree({ safe => 0 }); 58 | } 59 | 60 | 1; 61 | 62 | -------------------------------------------------------------------------------- /xt/mirror/modules/02packages.details.txt: -------------------------------------------------------------------------------- 1 | File: 02packages.details.txt 2 | URL: http://www.perl.com/CPAN/modules/02packages.details.txt 3 | Description: Package names found in carton.lock 4 | Columns: package name, version, path 5 | Intended-For: Automated fetch routines, namespace documentation. 6 | Written-By: Carton v0.9.0 7 | Line-Count: 16 8 | Last-Updated: Wed Jun 29 22:54:55 2011 9 | 10 | CGI 3.55 M/MA/MARKSTOS/CGI.pm-3.55.tar.gz 11 | CGI::Apache 1.01 M/MA/MARKSTOS/CGI.pm-3.55.tar.gz 12 | CGI::Carp 3.51 M/MA/MARKSTOS/CGI.pm-3.55.tar.gz 13 | CGI::Cookie 1.30 M/MA/MARKSTOS/CGI.pm-3.55.tar.gz 14 | CGI::Fast 1.08 M/MA/MARKSTOS/CGI.pm-3.55.tar.gz 15 | CGI::Pretty 3.46 M/MA/MARKSTOS/CGI.pm-3.55.tar.gz 16 | CGI::Push 1.05 M/MA/MARKSTOS/CGI.pm-3.55.tar.gz 17 | CGI::Switch 1.01 M/MA/MARKSTOS/CGI.pm-3.55.tar.gz 18 | CGI::Util 3.53 M/MA/MARKSTOS/CGI.pm-3.55.tar.gz 19 | CGITempFile undef M/MA/MARKSTOS/CGI.pm-3.55.tar.gz 20 | FCGI 0.73 F/FL/FLORA/FCGI-0.73.tar.gz 21 | FCGI::Stream undef F/FL/FLORA/FCGI-0.73.tar.gz 22 | Fh undef M/MA/MARKSTOS/CGI.pm-3.55.tar.gz 23 | Hash::MultiValue 0.08 M/MI/MIYAGAWA/Hash-MultiValue-0.08.tar.gz 24 | MultipartBuffer undef M/MA/MARKSTOS/CGI.pm-3.55.tar.gz 25 | Try::Tiny 0.09 D/DO/DOY/Try-Tiny-0.09.tar.gz 26 | -------------------------------------------------------------------------------- /xt/cli/snapshot.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | subtest 'snapshot file has canonical representation' => sub { 6 | my $app = cli(); 7 | $app->write_cpanfile(<run("install"); 13 | 14 | my $content = $app->dir->child('cpanfile.snapshot')->slurp; 15 | for (1..3) { 16 | $app->dir->child('cpanfile.snapshot')->remove; 17 | $app->run("install"); 18 | is $content, $app->dir->child('cpanfile.snapshot')->slurp; 19 | } 20 | }; 21 | 22 | subtest 'Bad snapshot version' => sub { 23 | my $app = cli(); 24 | $app->write_cpanfile(''); 25 | $app->write_file('cpanfile.snapshot', <run("install"); 30 | like $app->stderr, qr/Could not parse/; 31 | }; 32 | 33 | subtest 'Bad snapshot file' => sub { 34 | my $app = cli(); 35 | $app->write_cpanfile(''); 36 | $app->write_file('cpanfile.snapshot', <run("install"); 44 | like $app->stderr, qr/Could not parse/; 45 | }; 46 | 47 | subtest 'snapshot file support separate CRLF' => sub { 48 | my $app = cli(); 49 | $app->write_cpanfile(<run("install"); 55 | 56 | my $content = $app->dir->child('cpanfile.snapshot')->slurp; 57 | $content =~ s/\n/\r\n/g; 58 | $app->write_file('cpanfile.snapshot', $content); 59 | 60 | $app->run("install"); 61 | ok !$app->stderr; 62 | }; 63 | 64 | done_testing; 65 | 66 | -------------------------------------------------------------------------------- /lib/Carton/Index.pm: -------------------------------------------------------------------------------- 1 | package Carton::Index; 2 | use strict; 3 | use Class::Tiny { 4 | _packages => sub { +{} }, 5 | generator => sub { require Carton; "Carton $Carton::VERSION" }, 6 | }; 7 | 8 | sub add_package { 9 | my($self, $package) = @_; 10 | $self->_packages->{$package->name} = $package; # XXX ||= 11 | } 12 | 13 | sub count { 14 | my $self = shift; 15 | scalar keys %{$self->_packages}; 16 | } 17 | 18 | sub packages { 19 | my $self = shift; 20 | sort { $a->name cmp $b->name } values %{$self->_packages}; 21 | } 22 | 23 | sub write { 24 | my($self, $fh) = @_; 25 | 26 | print $fh <generator ]} 33 | Line-Count: @{[ $self->count ]} 34 | Last-Updated: @{[ scalar localtime ]} 35 | 36 | EOF 37 | for my $p ($self->packages) { 38 | print $fh $self->_format_line($p->name, $p->version || 'undef', $p->pathname); 39 | } 40 | } 41 | 42 | sub _format_line { 43 | my($self, @row) = @_; 44 | 45 | # from PAUSE::mldistwatch::rewrite02 46 | my $one = 30; 47 | my $two = 8; 48 | 49 | if (length $row[0] > $one) { 50 | $one += 8 - length $row[1]; 51 | $two = length $row[1]; 52 | } 53 | 54 | sprintf "%-${one}s %${two}s %s\n", @row; 55 | } 56 | 57 | sub pad { 58 | my($str, $len, $left) = @_; 59 | 60 | my $howmany = $len - length($str); 61 | return $str if $howmany <= 0; 62 | 63 | my $pad = " " x $howmany; 64 | return $left ? "$pad$str" : "$str$pad"; 65 | } 66 | 67 | 68 | 1; 69 | -------------------------------------------------------------------------------- /xt/cli/without.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | subtest 'carton install --without develop' => sub { 6 | my $app = cli(); 7 | $app->write_cpanfile(< sub { 11 | requires 'Hash::MultiValue', '== 0.14'; 12 | }; 13 | EOF 14 | 15 | $app->run("install"); 16 | $app->run("list"); 17 | like $app->stdout, qr/Try-Tiny-/; 18 | like $app->stdout, qr/Hash-MultiValue-0\.14/; 19 | 20 | $app->run("exec", "perl", "-e", "use Hash::MultiValue\ 1"); 21 | like $app->stderr, qr/Hash::MultiValue .* version 0.14/; 22 | 23 | $app->clean_local; 24 | 25 | $app->run("install", "--without", "develop"); 26 | $app->run("list"); 27 | like $app->stdout, qr/Try-Tiny-/; 28 | 29 | TODO: { 30 | local $TODO = "--without is not remembered for list"; 31 | unlike $app->stdout, qr/Hash-MultiValue-/; 32 | } 33 | 34 | $app->run("exec", "perl", "-e", "use Hash::MultiValue\ 1"); 35 | unlike $app->stderr, qr/Hash::MultiValue .* version 0.14/; 36 | }; 37 | 38 | subtest 'without features' => sub { 39 | my $app = cli(); 40 | $app->write_cpanfile(< sub { 44 | requires 'Stream::Buffered', '== 0.01'; 45 | }; 46 | EOF 47 | 48 | $app->run("install"); 49 | $app->run("list"); 50 | like $app->stdout, qr/Stream-Buffered-0\.01/; 51 | 52 | $app->clean_local; 53 | 54 | $app->run("install", "--deployment"); 55 | $app->run("exec", "perl", "-e", "use Stream::Buffered 1"); 56 | like $app->stderr, qr/Stream::Buffered .* version 0\.01/; 57 | 58 | $app->clean_local; 59 | 60 | $app->run("install", "--without", "stream"); 61 | 62 | $app->run("exec", "perl", "-e", "use Stream::Buffered 1"); 63 | unlike $app->stderr, qr/Stream::Buffered .* version 0\.01/; 64 | }; 65 | 66 | done_testing; 67 | 68 | -------------------------------------------------------------------------------- /lib/Carton/Tree.pm: -------------------------------------------------------------------------------- 1 | package Carton::Tree; 2 | use strict; 3 | use Carton::Dependency; 4 | 5 | use Class::Tiny qw( cpanfile snapshot ); 6 | 7 | use constant STOP => -1; 8 | 9 | sub walk_down { 10 | my($self, $cb) = @_; 11 | 12 | my $dumper; $dumper = sub { 13 | my($dependency, $reqs, $level, $parent) = @_; 14 | 15 | my $ret = $cb->($dependency, $reqs, $level); 16 | return if $ret && $ret == STOP; 17 | 18 | local $parent->{$dependency->distname} = 1 if $dependency; 19 | 20 | for my $module (sort $reqs->required_modules) { 21 | my $dependency = $self->dependency_for($module, $reqs); 22 | if ($dependency->dist) { 23 | next if $parent->{$dependency->distname}; 24 | $dumper->($dependency, $dependency->requirements, $level + 1, $parent); 25 | } else { 26 | # no dist found in lock 27 | } 28 | } 29 | }; 30 | 31 | $dumper->(undef, $self->cpanfile->requirements, 0, {}); 32 | undef $dumper; 33 | } 34 | 35 | sub dependency_for { 36 | my($self, $module, $reqs) = @_; 37 | 38 | my $requirement = $reqs->requirements_for_module($module); 39 | 40 | my $dep = Carton::Dependency->new; 41 | $dep->module($module); 42 | $dep->requirement($requirement); 43 | 44 | if (my $dist = $self->snapshot->find_or_core($module)) { 45 | $dep->dist($dist); 46 | } 47 | 48 | return $dep; 49 | } 50 | 51 | sub merged_requirements { 52 | my $self = shift; 53 | 54 | my $merged_reqs = CPAN::Meta::Requirements->new; 55 | 56 | my %seen; 57 | $self->walk_down(sub { 58 | my($dependency, $reqs, $level) = @_; 59 | return Carton::Tree::STOP if $dependency && $seen{$dependency->distname}++; 60 | $merged_reqs->add_requirements($reqs); 61 | }); 62 | 63 | $merged_reqs->clear_requirement('perl'); 64 | $merged_reqs->finalize; 65 | 66 | $merged_reqs; 67 | } 68 | 69 | 1; 70 | -------------------------------------------------------------------------------- /xt/cli/update.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | subtest 'carton update NonExistentModule' => sub { 6 | my $app = cli(); 7 | 8 | $app->write_cpanfile(<run("install"); 13 | $app->run("update", "XYZ"); 14 | like $app->stderr, qr/Could not find module XYZ/; 15 | }; 16 | 17 | subtest 'carton update upgrades a dist' => sub { 18 | my $app = cli(); 19 | 20 | $app->write_cpanfile(<run("install"); 25 | $app->run("list"); 26 | like $app->stdout, qr/Try-Tiny-0\.09/; 27 | 28 | $app->write_cpanfile(<= 0.09, <= 0.12'; 30 | EOF 31 | 32 | $app->run("install"); 33 | $app->run("check"); 34 | like $app->stdout, qr/are satisfied/; 35 | 36 | $app->run("list"); 37 | like $app->stdout, qr/Try-Tiny-0\.09/; 38 | 39 | $app->run("update", "Try::Tiny"); 40 | like $app->stdout, qr/installed Try-Tiny-0\.12.*upgraded from 0\.09/; 41 | 42 | $app->run("check"); 43 | like $app->stdout, qr/are satisfied/; 44 | 45 | $app->run("list"); 46 | like $app->stdout, qr/Try-Tiny-0\.12/; 47 | }; 48 | 49 | subtest 'downgrade a distribution' => sub { 50 | my $app = cli(); 51 | 52 | $app->write_cpanfile(<run("install"); 56 | $app->run("list"); 57 | like $app->stdout, qr/Try-Tiny-0\.\d\d/; 58 | 59 | $app->write_cpanfile(<run("update"); 63 | $app->run("list"); 64 | like $app->stdout, qr/Try-Tiny-0\.09/; 65 | 66 | TODO: { 67 | local $TODO = 'collecting wrong install info'; 68 | $app->write_cpanfile(<run("install"); 72 | $app->run("list"); 73 | like $app->stdout, qr/Try-Tiny-0\.09/; 74 | } 75 | }; 76 | 77 | done_testing; 78 | 79 | -------------------------------------------------------------------------------- /Makefile.PL: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v5.035. 2 | use strict; 3 | use warnings; 4 | 5 | use 5.008005; 6 | 7 | use ExtUtils::MakeMaker; 8 | 9 | my %WriteMakefileArgs = ( 10 | "ABSTRACT" => "Perl module dependency manager (aka Bundler for Perl)", 11 | "AUTHOR" => "Tatsuhiko Miyagawa", 12 | "CONFIGURE_REQUIRES" => { 13 | "ExtUtils::MakeMaker" => 0, 14 | "version" => "0.77" 15 | }, 16 | "DISTNAME" => "Carton", 17 | "EXE_FILES" => [ 18 | "script/carton" 19 | ], 20 | "LICENSE" => "perl", 21 | "MIN_PERL_VERSION" => "5.008005", 22 | "NAME" => "Carton", 23 | "PREREQ_PM" => { 24 | "App::cpanminus" => "1.703", 25 | "CPAN::Meta" => "2.120921", 26 | "CPAN::Meta::Requirements" => "2.121", 27 | "Class::Tiny" => "1.001", 28 | "ExtUtils::MakeMaker" => "6.64", 29 | "Getopt::Long" => "2.39", 30 | "JSON" => "2.53", 31 | "Module::Build" => "0.4004", 32 | "Module::CPANfile" => "0.9031", 33 | "Module::CoreList" => 0, 34 | "Module::Metadata" => "1.000003", 35 | "Path::Tiny" => "0.033", 36 | "Try::Tiny" => "0.09", 37 | "parent" => "0.223" 38 | }, 39 | "VERSION" => "v1.0.16", 40 | "test" => { 41 | "TESTS" => "t/*.t" 42 | } 43 | ); 44 | 45 | 46 | my %FallbackPrereqs = ( 47 | "App::cpanminus" => "1.703", 48 | "CPAN::Meta" => "2.120921", 49 | "CPAN::Meta::Requirements" => "2.121", 50 | "Class::Tiny" => "1.001", 51 | "ExtUtils::MakeMaker" => "6.64", 52 | "Getopt::Long" => "2.39", 53 | "JSON" => "2.53", 54 | "Module::Build" => "0.4004", 55 | "Module::CPANfile" => "0.9031", 56 | "Module::CoreList" => 0, 57 | "Module::Metadata" => "1.000003", 58 | "Path::Tiny" => "0.033", 59 | "Try::Tiny" => "0.09", 60 | "parent" => "0.223", 61 | "version" => "0.77" 62 | ); 63 | 64 | 65 | unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { 66 | delete $WriteMakefileArgs{TEST_REQUIRES}; 67 | delete $WriteMakefileArgs{BUILD_REQUIRES}; 68 | $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; 69 | } 70 | 71 | delete $WriteMakefileArgs{CONFIGURE_REQUIRES} 72 | unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; 73 | 74 | WriteMakefile(%WriteMakefileArgs); 75 | -------------------------------------------------------------------------------- /xt/cli/check.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | subtest 'carton check fails when there is no lock' => sub { 6 | my $app = cli(); 7 | $app->write_cpanfile(<run("check"); 12 | like $app->stderr, qr/find cpanfile\.snapshot/; 13 | }; 14 | 15 | subtest 'carton install and check' => sub { 16 | my $app = cli(); 17 | $app->write_cpanfile(<run("install"); 22 | 23 | $app->run("check"); 24 | like $app->stdout, qr/are satisfied/; 25 | 26 | $app->run("list"); 27 | like $app->stdout, qr/Try-Tiny-0\.11/; 28 | 29 | $app->write_cpanfile(<run("check"); 34 | like $app->stdout, qr/not satisfied/; 35 | 36 | TODO: { 37 | local $TODO = 'exec does not verify lock'; 38 | $app->run("exec", "perl", "use Try::Tiny"); 39 | like $app->stderr, qr/\.snapshot/; 40 | } 41 | 42 | $app->run("install"); 43 | 44 | $app->run("check"); 45 | like $app->stdout, qr/are satisfied/; 46 | 47 | $app->run("list"); 48 | like $app->stdout, qr/Try-Tiny-0\.\d\d/; 49 | 50 | $app->write_cpanfile(<run("check"); 55 | like $app->stdout, qr/not satisfied/; 56 | 57 | $app->run("install"); 58 | like $app->stderr, qr/failed/; 59 | 60 | $app->run("check"); 61 | like $app->stdout, qr/not satisfied/; 62 | }; 63 | 64 | subtest 'detect unused modules' => sub { 65 | my $app = cli; 66 | $app->write_cpanfile("requires 'Try::Tiny';"); 67 | 68 | $app->run("install"); 69 | $app->write_cpanfile(""); 70 | 71 | 72 | TODO: { 73 | local $TODO = "Can't detect superflous modules"; 74 | $app->run("install"); 75 | $app->run("list"); 76 | is $app->stdout, ""; 77 | 78 | $app->run("check"); 79 | like $app->stdout, qr/unused/; 80 | } 81 | }; 82 | 83 | subtest 'detect downgrade' => sub { 84 | my $app = cli; 85 | $app->write_cpanfile("requires 'URI';"); 86 | $app->run("install"); 87 | 88 | $app->write_cpanfile("requires 'URI', '== 1.59';"); 89 | $app->run("check"); 90 | 91 | like $app->stdout, qr/not satisfied/; 92 | like $app->stdout, qr/URI has version .* Needs == 1\.59/; 93 | }; 94 | 95 | done_testing; 96 | 97 | -------------------------------------------------------------------------------- /xt/cli/exec.t: -------------------------------------------------------------------------------- 1 | use strict; 2 | use Test::More; 3 | use xt::CLI; 4 | 5 | subtest 'carton exec without a command', sub { 6 | my $app = cli(); 7 | $app->write_cpanfile(''); 8 | $app->run("install"); 9 | $app->run("exec"); 10 | like $app->stderr, qr/carton exec needs a command/; 11 | is $app->exit_code, 255; 12 | }; 13 | 14 | subtest 'exec without cpanfile', sub { 15 | my $app = cli(); 16 | $app->run("exec", "perl", "-e", 1); 17 | like $app->stderr, qr/Can't locate cpanfile/; 18 | is $app->exit_code, 255; 19 | }; 20 | 21 | subtest 'exec without a snapshot', sub { 22 | my $app = cli(); 23 | $app->write_cpanfile(); 24 | $app->run("exec", "perl", "-e", 1); 25 | like $app->stderr, qr/cpanfile\.snapshot/; 26 | is $app->exit_code, 255; 27 | }; 28 | 29 | subtest 'carton exec', sub { 30 | my $app = cli(); 31 | $app->write_cpanfile(''); 32 | $app->run("install"); 33 | 34 | TODO: { 35 | local $TODO = "exec now does not strip site_perl"; 36 | $app->run("exec", "perl", "-e", "use Try::Tiny"); 37 | like $app->stderr, qr/Can't locate Try\/Tiny.pm/; 38 | } 39 | 40 | $app->write_cpanfile(<run("install"); 45 | 46 | $app->run("exec", "--", "perl", "-e", 'use Try::Tiny; print $Try::Tiny::VERSION, "\n"'); 47 | like $app->stdout, qr/0\.11/; 48 | 49 | $app->run("exec", "perl", "-e", 'use Try::Tiny; print $Try::Tiny::VERSION, "\n"'); 50 | like $app->stdout, qr/0\.11/, "No need for -- as well"; 51 | 52 | $app->run("exec", "perl", "-MTry::Tiny", "-e", 'print $Try::Tiny::VERSION, "\n"'); 53 | like $app->stdout, qr/0\.11/; 54 | 55 | $app->write_cpanfile(<run("install"); 61 | $app->run("exec", "--", "ack", "--version"); 62 | 63 | like $app->stdout, qr/ack 2\.02/; 64 | }; 65 | 66 | subtest 'carton exec perl -Ilib', sub { 67 | my $app = cli(); 68 | $app->write_cpanfile(''); 69 | $app->run("install"); 70 | 71 | $app->dir->child("lib")->mkpath; 72 | $app->dir->child("lib/FooBarBaz.pm")->spew("package FooBarBaz; 1"); 73 | 74 | $app->run("exec", "perl", "-Ilib", "-e", 'use FooBarBaz; print "foo"'); 75 | like $app->stdout, qr/foo/; 76 | unlike $app->stderr, qr/exec -Ilib is deprecated/; 77 | 78 | $app->run("exec", "-Ilib", "perl", "-e", 'print "foo"'); 79 | like $app->stdout, qr/foo/; 80 | like $app->stderr, qr/exec -Ilib is deprecated/; 81 | }; 82 | 83 | done_testing; 84 | 85 | -------------------------------------------------------------------------------- /lib/Carton/Packer.pm: -------------------------------------------------------------------------------- 1 | package Carton::Packer; 2 | use Class::Tiny; 3 | use warnings NONFATAL => 'all'; 4 | use App::FatPacker; 5 | use File::pushd (); 6 | use Path::Tiny (); 7 | use CPAN::Meta (); 8 | use File::Find (); 9 | 10 | sub fatpack_carton { 11 | my($self, $dir) = @_; 12 | 13 | my $temp = Path::Tiny->tempdir; 14 | my $pushd = File::pushd::pushd $temp; 15 | 16 | my $file = $temp->child('carton.pre.pl'); 17 | 18 | $file->spew(<<'EOF'); 19 | #!/usr/bin/env perl 20 | use strict; 21 | use 5.008001; 22 | use Carton::CLI; 23 | $Carton::Fatpacked = 1; 24 | exit Carton::CLI->new->run(@ARGV); 25 | EOF 26 | 27 | my $fatpacked = $self->do_fatpack($file); 28 | 29 | my $executable = $dir->child('carton'); 30 | warn "Bundling $executable\n"; 31 | 32 | $dir->mkpath; 33 | $executable->spew($fatpacked); 34 | chmod 0755, $executable; 35 | } 36 | 37 | sub do_fatpack { 38 | my($self, $file) = @_; 39 | 40 | my $packer = App::FatPacker->new; 41 | 42 | my @modules = split /\r?\n/, $packer->trace(args => [$file], use => $self->required_modules); 43 | my @packlists = $packer->packlists_containing(\@modules); 44 | $packer->packlists_to_tree(Path::Tiny->new('fatlib')->absolute, \@packlists); 45 | 46 | my $fatpacked = do { 47 | local $SIG{__WARN__} = sub {}; 48 | $packer->fatpack_file($file); 49 | }; 50 | 51 | # HACK: File::Spec bundled into arch in < 5.16, but is loadable as pure-perl 52 | use Config; 53 | $fatpacked =~ s/\$fatpacked{"$Config{archname}\/(Cwd|File)/\$fatpacked{"$1/g; 54 | 55 | $fatpacked; 56 | } 57 | 58 | sub required_modules { 59 | my($self, $packer) = @_; 60 | 61 | my $meta = $self->installed_meta('Carton') 62 | or die "Couldn't find install metadata for Carton"; 63 | 64 | my %excludes = ( 65 | perl => 1, 66 | 'ExtUtils::MakeMaker' => 1, 67 | 'Module::Build' => 1, 68 | ); 69 | 70 | my @requirements = grep !$excludes{$_}, 71 | $meta->effective_prereqs->requirements_for('runtime', 'requires')->required_modules; 72 | 73 | return \@requirements; 74 | } 75 | 76 | sub installed_meta { 77 | my($self, $dist) = @_; 78 | 79 | my @meta; 80 | my $finder = sub { 81 | if (m!\b$dist-.*[\\/]MYMETA.json!) { 82 | my $meta = CPAN::Meta->load_file($_); 83 | push @meta, $meta if $meta->name eq $dist; 84 | } 85 | }; 86 | 87 | File::Find::find({ wanted => $finder, no_chdir => 1 }, grep -d, map "$_/.meta", @INC); 88 | 89 | # return the latest version 90 | @meta = sort { version->new($b->version) cmp version->new($a->version) } @meta; 91 | 92 | return $meta[0]; 93 | } 94 | 95 | 1; 96 | -------------------------------------------------------------------------------- /lib/Carton/Environment.pm: -------------------------------------------------------------------------------- 1 | package Carton::Environment; 2 | use strict; 3 | use Carton::CPANfile; 4 | use Carton::Snapshot; 5 | use Carton::Error; 6 | use Carton::Tree; 7 | use Path::Tiny; 8 | 9 | use Class::Tiny { 10 | cpanfile => undef, 11 | snapshot => sub { $_[0]->_build_snapshot }, 12 | install_path => sub { $_[0]->_build_install_path }, 13 | vendor_cache => sub { $_[0]->_build_vendor_cache }, 14 | tree => sub { $_[0]->_build_tree }, 15 | }; 16 | 17 | sub _build_snapshot { 18 | my $self = shift; 19 | Carton::Snapshot->new(path => $self->cpanfile . ".snapshot"); 20 | } 21 | 22 | sub _build_install_path { 23 | my $self = shift; 24 | if ($ENV{PERL_CARTON_PATH}) { 25 | return Path::Tiny->new($ENV{PERL_CARTON_PATH}); 26 | } else { 27 | return $self->cpanfile->path->parent->child("local"); 28 | } 29 | } 30 | 31 | sub _build_vendor_cache { 32 | my $self = shift; 33 | Path::Tiny->new($self->install_path->dirname . "/vendor/cache"); 34 | } 35 | 36 | sub _build_tree { 37 | my $self = shift; 38 | Carton::Tree->new(cpanfile => $self->cpanfile, snapshot => $self->snapshot); 39 | } 40 | 41 | sub vendor_bin { 42 | my $self = shift; 43 | $self->vendor_cache->parent->child('bin'); 44 | } 45 | 46 | sub build_with { 47 | my($class, $cpanfile) = @_; 48 | 49 | $cpanfile = Path::Tiny->new($cpanfile)->absolute; 50 | if ($cpanfile->is_file) { 51 | return $class->new(cpanfile => Carton::CPANfile->new(path => $cpanfile)); 52 | } else { 53 | Carton::Error::CPANfileNotFound->throw(error => "Can't locate cpanfile: $cpanfile"); 54 | } 55 | } 56 | 57 | sub build { 58 | my($class, $cpanfile_path, $install_path) = @_; 59 | 60 | my $self = $class->new; 61 | 62 | $cpanfile_path &&= Path::Tiny->new($cpanfile_path)->absolute; 63 | 64 | my $cpanfile = $self->locate_cpanfile($cpanfile_path || $ENV{PERL_CARTON_CPANFILE}); 65 | if ($cpanfile && $cpanfile->is_file) { 66 | $self->cpanfile( Carton::CPANfile->new(path => $cpanfile) ); 67 | } else { 68 | Carton::Error::CPANfileNotFound->throw(error => "Can't locate cpanfile: (@{[ $cpanfile_path || 'cpanfile' ]})"); 69 | } 70 | 71 | $self->install_path( Path::Tiny->new($install_path)->absolute ) if $install_path; 72 | 73 | $self; 74 | } 75 | 76 | sub locate_cpanfile { 77 | my($self, $path) = @_; 78 | 79 | if ($path) { 80 | return Path::Tiny->new($path)->absolute; 81 | } 82 | 83 | my $current = Path::Tiny->cwd; 84 | my $previous = ''; 85 | 86 | until ($current eq '/' or $current eq $previous) { 87 | # TODO support PERL_CARTON_CPANFILE 88 | my $try = $current->child('cpanfile'); 89 | if ($try->is_file) { 90 | return $try->absolute; 91 | } 92 | 93 | ($previous, $current) = ($current, $current->parent); 94 | } 95 | 96 | return; 97 | } 98 | 99 | 1; 100 | 101 | -------------------------------------------------------------------------------- /META.json: -------------------------------------------------------------------------------- 1 | { 2 | "abstract" : "Perl module dependency manager (aka Bundler for Perl)", 3 | "author" : [ 4 | "Tatsuhiko Miyagawa" 5 | ], 6 | "dynamic_config" : 0, 7 | "generated_by" : "Dist::Zilla version 5.035, Dist::Milla version v1.0.15, CPAN::Meta::Converter version 2.150001", 8 | "license" : [ 9 | "perl_5" 10 | ], 11 | "meta-spec" : { 12 | "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", 13 | "version" : 2 14 | }, 15 | "name" : "Carton", 16 | "no_index" : { 17 | "directory" : [ 18 | "t", 19 | "xt", 20 | "inc", 21 | "share", 22 | "eg", 23 | "examples" 24 | ] 25 | }, 26 | "prereqs" : { 27 | "configure" : { 28 | "requires" : { 29 | "ExtUtils::MakeMaker" : "0", 30 | "version" : "0.77" 31 | } 32 | }, 33 | "develop" : { 34 | "requires" : { 35 | "Capture::Tiny" : "0", 36 | "Dist::Milla" : "v1.0.15", 37 | "Test::More" : "0.9", 38 | "Test::Pod" : "1.41", 39 | "Test::Requires" : "0" 40 | } 41 | }, 42 | "runtime" : { 43 | "recommends" : { 44 | "App::FatPacker" : "0.009018", 45 | "File::pushd" : "0", 46 | "Module::Reader" : "0.002" 47 | }, 48 | "requires" : { 49 | "App::cpanminus" : "1.703", 50 | "CPAN::Meta" : "2.120921", 51 | "CPAN::Meta::Requirements" : "2.121", 52 | "Class::Tiny" : "1.001", 53 | "ExtUtils::MakeMaker" : "6.64", 54 | "Getopt::Long" : "2.39", 55 | "JSON" : "2.53", 56 | "Module::Build" : "0.4004", 57 | "Module::CPANfile" : "0.9031", 58 | "Module::CoreList" : "0", 59 | "Module::Metadata" : "1.000003", 60 | "Path::Tiny" : "0.033", 61 | "Try::Tiny" : "0.09", 62 | "parent" : "0.223", 63 | "perl" : "v5.8.5" 64 | } 65 | } 66 | }, 67 | "release_status" : "stable", 68 | "resources" : { 69 | "bugtracker" : { 70 | "web" : "https://github.com/perl-carton/carton/issues" 71 | }, 72 | "homepage" : "https://github.com/perl-carton/carton", 73 | "repository" : { 74 | "type" : "git", 75 | "url" : "https://github.com/perl-carton/carton.git", 76 | "web" : "https://github.com/perl-carton/carton" 77 | } 78 | }, 79 | "version" : "v1.0.16", 80 | "x_contributors" : [ 81 | "Christian Walde ", 82 | "David Golden ", 83 | "David Steinbrunner ", 84 | "ikasam_a ", 85 | "Kan Fushihara ", 86 | "Masahiro Chiba ", 87 | "NAKAGAWA Masaki ", 88 | "Olaf Alders ", 89 | "Pedro Figueiredo ", 90 | "shiba_yu36 ", 91 | "Tatsuhiko Miyagawa ", 92 | "Tatsuhiko Miyagawa ", 93 | "Yanick Champoux " 94 | ] 95 | } 96 | 97 | -------------------------------------------------------------------------------- /lib/Carton/Doc/Install.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | Carton::Doc::Install - Install the dependencies 4 | 5 | =head1 SYNOPSIS 6 | 7 | carton install [--deployment] [--cached] [--path=PATH] [--without develop] 8 | 9 | =head1 DESCRIPTION 10 | 11 | Install the dependencies for your application. This command has two 12 | modes and the behavior is slightly different. 13 | 14 | =head2 DEVELOPMENT MODE 15 | 16 | =over 4 17 | 18 | =item carton install 19 | 20 | If you run C without any arguments and if I 21 | exists, carton will scan dependencies from I and install 22 | the modules. 23 | 24 | =back 25 | 26 | If you run C for the first time 27 | (i.e. I does not exist), carton will fetch all the 28 | modules specified, resolve dependencies and install all required 29 | modules from CPAN. 30 | 31 | If I file does exist, carton will still try to install 32 | modules specified or updated in I, but uses I 33 | for the dependency resolution, and then cascades to CPAN. 34 | 35 | carton will analyze all the dependencies and their version 36 | information, and it is saved into I file. It is important 37 | to add I file into a version controlled repository and 38 | commit the changes as you update your dependencies. 39 | 40 | =head2 DEPLOYMENT MODE 41 | 42 | If you specify the C<--deployment> command line option or the 43 | I exists, carton will only use the dependencies 44 | specified in the I instead of resolving 45 | dependencies. 46 | 47 | =head1 OPTIONS 48 | 49 | =over 4 50 | 51 | =item --deployment 52 | 53 | Force the deployment mode. See L above. 54 | 55 | =item --cached 56 | 57 | Locate distribution tarballs in C rather than fetching 58 | them from CPAN mirrors. This requires you to run C 59 | prior to the deployment and commit or sync the content of C 60 | directory to the other host you run C on. 61 | 62 | =item --cpanfile 63 | 64 | Specify the alternate path for cpanfile. By default, C 65 | will look for the file C in the current directory, then 66 | upwards till the root directory, in case the command runs from a sub 67 | directory. 68 | 69 | Carton assumes the directory where your cpanfile (or altenate path) 70 | exists as a project root directory, and will look for the snapshot file as 71 | well as install directory (C) and C relative to it. 72 | 73 | =item --path 74 | 75 | Specify the path to install modules to. Defaults to I in the 76 | directory relative to where C is. 77 | 78 | B: this option, as of version 1.0, is not preserved across 79 | multiple runs of C or other commands such as C or C. You can choose to set the path in 81 | C envrionment variable to persist it across 82 | commands. 83 | 84 | =item --without 85 | 86 | By default, C will install all the phases for 87 | dependencies, including C. You can specify phases or features 88 | to exclude, in the comma separated list. 89 | 90 | carton install --deployment --without develop 91 | 92 | B: C<--without> for the initial installation (without 93 | cpanfile.snapshot) is not supported at this moment. 94 | 95 | =back 96 | -------------------------------------------------------------------------------- /lib/Carton/Builder.pm: -------------------------------------------------------------------------------- 1 | package Carton::Builder; 2 | use strict; 3 | use Class::Tiny { 4 | mirror => undef, 5 | index => undef, 6 | cascade => sub { 1 }, 7 | without => sub { [] }, 8 | cpanfile => undef, 9 | fatscript => sub { $_[0]->_build_fatscript }, 10 | }; 11 | 12 | sub effective_mirrors { 13 | my $self = shift; 14 | 15 | # push default CPAN mirror always, as a fallback 16 | # TODO don't pass fallback if --cached is set? 17 | 18 | my @mirrors = ($self->mirror); 19 | push @mirrors, Carton::Mirror->default if $self->custom_mirror; 20 | push @mirrors, Carton::Mirror->new('http://backpan.perl.org/'); 21 | 22 | @mirrors; 23 | } 24 | 25 | sub custom_mirror { 26 | my $self = shift; 27 | ! $self->mirror->is_default; 28 | } 29 | 30 | sub bundle { 31 | my($self, $path, $cache_path, $snapshot) = @_; 32 | 33 | for my $dist ($snapshot->distributions) { 34 | my $source = $path->child("cache/authors/id/" . $dist->pathname); 35 | my $target = $cache_path->child("authors/id/" . $dist->pathname); 36 | 37 | if ($source->exists) { 38 | warn "Copying ", $dist->pathname, "\n"; 39 | $target->parent->mkpath; 40 | $source->copy($target) or warn "$target: $!"; 41 | } else { 42 | warn "Couldn't find @{[ $dist->pathname ]}\n"; 43 | } 44 | } 45 | } 46 | 47 | sub install { 48 | my($self, $path) = @_; 49 | 50 | $self->run_cpanm( 51 | "-L", $path, 52 | (map { ("--mirror", $_->url) } $self->effective_mirrors), 53 | ( $self->index ? ("--mirror-index", $self->index) : () ), 54 | ( $self->cascade ? "--cascade-search" : () ), 55 | ( $self->custom_mirror ? "--mirror-only" : () ), 56 | "--save-dists", "$path/cache", 57 | $self->groups, 58 | "--cpanfile", $self->cpanfile, 59 | "--installdeps", $self->cpanfile->dirname, 60 | ) or die "Installing modules failed\n"; 61 | } 62 | 63 | sub groups { 64 | my $self = shift; 65 | 66 | # TODO support --without test (don't need test on deployment) 67 | my @options = ('--with-all-features', '--with-develop'); 68 | 69 | for my $group (@{$self->without}) { 70 | push @options, '--without-develop' if $group eq 'develop'; 71 | push @options, "--without-feature=$group"; 72 | } 73 | 74 | return @options; 75 | } 76 | 77 | sub update { 78 | my($self, $path, @modules) = @_; 79 | 80 | $self->run_cpanm( 81 | "-L", $path, 82 | (map { ("--mirror", $_->url) } $self->effective_mirrors), 83 | ( $self->custom_mirror ? "--mirror-only" : () ), 84 | "--save-dists", "$path/cache", 85 | @modules 86 | ) or die "Updating modules failed\n"; 87 | } 88 | 89 | sub _build_fatscript { 90 | my $self = shift; 91 | 92 | my $fatscript; 93 | if ($Carton::Fatpacked) { 94 | require Module::Reader; 95 | my $content = Module::Reader::module_content('App::cpanminus::fatscript') 96 | or die "Can't locate App::cpanminus::fatscript"; 97 | $fatscript = Path::Tiny->tempfile; 98 | $fatscript->spew($content); 99 | } else { 100 | require Module::Metadata; 101 | $fatscript = Module::Metadata->find_module_by_name("App::cpanminus::fatscript") 102 | or die "Can't locate App::cpanminus::fatscript"; 103 | } 104 | 105 | return $fatscript; 106 | } 107 | 108 | sub run_cpanm { 109 | my($self, @args) = @_; 110 | local $ENV{PERL_CPANM_OPT}; 111 | !system $^X, $self->fatscript, "--quiet", "--notest", @args; 112 | } 113 | 114 | 1; 115 | -------------------------------------------------------------------------------- /lib/Carton/Snapshot/Parser.pm: -------------------------------------------------------------------------------- 1 | package Carton::Snapshot::Parser; 2 | use Class::Tiny; 3 | use warnings NONFATAL => 'all'; 4 | use Carton::Dist; 5 | use Carton::Error; 6 | 7 | my $machine = { 8 | init => [ 9 | { 10 | re => qr/^\# carton snapshot format: version (1\.0)/, 11 | code => sub { 12 | my($stash, $snapshot, $ver) = @_; 13 | $snapshot->version($ver); 14 | }, 15 | goto => 'section', 16 | }, 17 | # TODO support pasing error and version mismatch etc. 18 | ], 19 | section => [ 20 | { 21 | re => qr/^DISTRIBUTIONS$/, 22 | goto => 'dists', 23 | }, 24 | { 25 | re => qr/^__EOF__$/, 26 | done => 1, 27 | }, 28 | ], 29 | dists => [ 30 | { 31 | re => qr/^ (\S+)$/, 32 | code => sub { $_[0]->{dist} = Carton::Dist->new(name => $1) }, 33 | goto => 'distmeta', 34 | }, 35 | { 36 | re => qr/^\S/, 37 | goto => 'section', 38 | redo => 1, 39 | }, 40 | ], 41 | distmeta => [ 42 | { 43 | re => qr/^ pathname: (.*)$/, 44 | code => sub { $_[0]->{dist}->pathname($1) }, 45 | }, 46 | { 47 | re => qr/^\s{4}provides:$/, 48 | code => sub { $_[0]->{property} = 'provides' }, 49 | goto => 'properties', 50 | }, 51 | { 52 | re => qr/^\s{4}requirements:$/, 53 | code => sub { 54 | $_[0]->{property} = 'requirements'; 55 | }, 56 | goto => 'properties', 57 | }, 58 | { 59 | re => qr/^\s{0,2}\S/, 60 | code => sub { 61 | my($stash, $snapshot) = @_; 62 | $snapshot->add_distribution($stash->{dist}); 63 | %$stash = (); # clear 64 | }, 65 | goto => 'dists', 66 | redo => 1, 67 | }, 68 | ], 69 | properties => [ 70 | { 71 | re => qr/^\s{6}([0-9A-Za-z_:]+) ([v0-9\._,=\!<>\s]+|undef)/, 72 | code => sub { 73 | my($stash, $snapshot, $module, $version) = @_; 74 | 75 | if ($stash->{property} eq 'provides') { 76 | $stash->{dist}->provides->{$module} = { version => $version }; 77 | } else { 78 | $stash->{dist}->add_string_requirement($module, $version); 79 | } 80 | }, 81 | }, 82 | { 83 | re => qr/^\s{0,4}\S/, 84 | goto => 'distmeta', 85 | redo => 1, 86 | }, 87 | ], 88 | }; 89 | 90 | sub parse { 91 | my($self, $data, $snapshot) = @_; 92 | 93 | my @lines = split /\r?\n/, $data; 94 | 95 | my $state = $machine->{init}; 96 | my $stash = {}; 97 | 98 | LINE: 99 | for my $line (@lines, '__EOF__') { 100 | last LINE unless @$state; 101 | 102 | STATE: { 103 | for my $trans (@{$state}) { 104 | if (my @match = $line =~ $trans->{re}) { 105 | if (my $code = $trans->{code}) { 106 | $code->($stash, $snapshot, @match); 107 | } 108 | if (my $goto = $trans->{goto}) { 109 | $state = $machine->{$goto}; 110 | if ($trans->{redo}) { 111 | redo STATE; 112 | } else { 113 | next LINE; 114 | } 115 | } 116 | 117 | last STATE; 118 | } 119 | } 120 | 121 | Carton::Error::SnapshotParseError->throw(error => "Could not parse snapshot file."); 122 | } 123 | } 124 | } 125 | 126 | 1; 127 | -------------------------------------------------------------------------------- /lib/Carton/Doc/FAQ.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | Carton::Doc::FAQ - Frequently Asked Questions 4 | 5 | =head1 QUESTIONS 6 | 7 | =head2 It looks useful, but what is the use case of this tool? 8 | 9 | The particular problem that carton is trying to address is this: 10 | 11 | You develop a Perl-based application, possibly but not limited to 12 | webapps, with dozens of CPAN module dependencies. You install these 13 | modules on your development machine, and describe these dependencies 14 | in your I. 15 | 16 | Now you get a production environment, either on PaaS provider or some 17 | VPS, you install the dependencies using C and 18 | it will pull all the latest releases from CPAN as of today and 19 | everything just works. 20 | 21 | A few weeks later, your application becomes more popular, and you 22 | think you need another machine to serve more requests. You set up 23 | another machine with vanilla perl installation and install the 24 | dependencies the same way. That will pull the I releases from 25 | CPAN I, rather than the same as what you have today. 26 | 27 | And that is the problem. It's not likely that everything just breaks 28 | one day, but there's always a chance that one of the dependencies 29 | breaks an API compatibility, or just uploaded a buggy version to CPAN 30 | on that particular day. 31 | 32 | Carton allows you to I these dependencies into a version 33 | controlled system, so that every time you deploy from a checkout, it 34 | is guaranteed that all the same versions are installed into the local 35 | environment. 36 | 37 | =head2 How is this different from Pinto or CPAN::Mini::Inject? 38 | 39 | carton definitely shares the goal with these private CPAN repository 40 | management tool. But the main difference is that rather than creating 41 | an actual CPAN-like repository that works with any CPAN clients, 42 | Carton provides a way to install specific versions of distributions 43 | from CPAN, or any CPAN-like mirrors (as well as git repositories in 44 | the future version of Carton). 45 | 46 | Existing tools are designed to work I CPAN clients such as 47 | L or L, and have accomplished that by working around 48 | the CPAN mirror structure. 49 | 50 | carton I does the same thing, but its user interface is 51 | centered around the installer, by implementing a wrapper for 52 | L, so you can use the same commands in the 53 | development mode and deployment mode. 54 | 55 | Carton automatically maintains the L file, which is meant 56 | to be version controlled, inside your application directory. You don't 57 | need a separate database, a directory or a web server to maintain 58 | tarballs outside your application. The I file can always 59 | be generated with C command, and C on 60 | another machine can use the version in the snapshot. 61 | 62 | =head2 I already use Pinto to create DarkPAN mirror. Can I use Carton with this? 63 | 64 | Yes, by specifying Pinto mirror as your Carton mirror, you can take a 65 | snapshot of your dependencies including your private modules on Pinto, 66 | or whatever DarkPAN mirror. 67 | 68 | =head2 I'm already using perlbrew and local::lib. Can I use carton with this? 69 | 70 | If you're using L already with L perl, possibly 71 | with the new C command, that's great! There are multiple 72 | benefits over using L and L for development and 73 | use L for deployment. 74 | 75 | The best practice and workflow to get your perl environment as clean 76 | as possible with lots of modules installed for quick development would 77 | be this: 78 | 79 | =over 80 | 81 | =item * 82 | 83 | Install fresh perl using perlbrew. The version must be the same 84 | against the version you'll run on the production environment. 85 | 86 | =item * 87 | 88 | Once the installation is done, use C command to create a 89 | new local lib environment (let's call it I) and always use the 90 | library as a default environment. Install as many modules as you would 91 | like into the I library path. 92 | 93 | This ensures to have a vanilla C library path as clean as 94 | possible. 95 | 96 | =item * 97 | 98 | When you build a new project that you want to manage dependencies via 99 | Carton, turn off the I local::lib and create a new one, like 100 | I. Install L and all of its dependencies to the 101 | I local::lib path. Then run C like you 102 | normally do. 103 | 104 | Becuase I and I are isolated, the modules you installed 105 | into I doesn't affect the process when carton builds the 106 | dependency tree for your new project at all. This could often be 107 | critical when you have a conditional dependency in your tree, like 108 | L. 109 | 110 | =back 111 | 112 | 113 | -------------------------------------------------------------------------------- /lib/Carton/Snapshot.pm: -------------------------------------------------------------------------------- 1 | package Carton::Snapshot; 2 | use strict; 3 | use Config; 4 | use Carton::Dist; 5 | use Carton::Dist::Core; 6 | use Carton::Error; 7 | use Carton::Package; 8 | use Carton::Index; 9 | use Carton::Util; 10 | use Carton::Snapshot::Emitter; 11 | use Carton::Snapshot::Parser; 12 | use CPAN::Meta; 13 | use CPAN::Meta::Requirements; 14 | use File::Find (); 15 | use Try::Tiny; 16 | use Path::Tiny (); 17 | use Module::CoreList; 18 | 19 | use constant CARTON_SNAPSHOT_VERSION => '1.0'; 20 | 21 | use subs 'path'; 22 | use Class::Tiny { 23 | path => undef, 24 | version => sub { CARTON_SNAPSHOT_VERSION }, 25 | loaded => undef, 26 | _distributions => sub { +[] }, 27 | }; 28 | 29 | sub BUILD { 30 | my $self = shift; 31 | $self->path( $self->{path} ); 32 | } 33 | 34 | sub path { 35 | my $self = shift; 36 | if (@_) { 37 | $self->{path} = Path::Tiny->new($_[0]); 38 | } else { 39 | $self->{path}; 40 | } 41 | } 42 | 43 | sub load_if_exists { 44 | my $self = shift; 45 | $self->load if $self->path->is_file; 46 | } 47 | 48 | sub load { 49 | my $self = shift; 50 | 51 | return 1 if $self->loaded; 52 | 53 | if ($self->path->is_file) { 54 | my $parser = Carton::Snapshot::Parser->new; 55 | $parser->parse($self->path->slurp_utf8, $self); 56 | $self->loaded(1); 57 | 58 | return 1; 59 | } else { 60 | Carton::Error::SnapshotNotFound->throw( 61 | error => "Can't find cpanfile.snapshot: Run `carton install` to build the snapshot file.", 62 | path => $self->path, 63 | ); 64 | } 65 | } 66 | 67 | sub save { 68 | my $self = shift; 69 | $self->path->spew_utf8( Carton::Snapshot::Emitter->new->emit($self) ); 70 | } 71 | 72 | sub find { 73 | my($self, $module) = @_; 74 | (grep $_->provides_module($module), $self->distributions)[0]; 75 | } 76 | 77 | sub find_or_core { 78 | my($self, $module) = @_; 79 | $self->find($module) || $self->find_in_core($module); 80 | } 81 | 82 | sub find_in_core { 83 | my($self, $module) = @_; 84 | 85 | if (exists $Module::CoreList::version{$]}{$module}) { 86 | my $version = $Module::CoreList::version{$]}{$module}; # maybe undef 87 | return Carton::Dist::Core->new(name => $module, module_version => $version); 88 | } 89 | 90 | return; 91 | } 92 | 93 | sub index { 94 | my $self = shift; 95 | 96 | my $index = Carton::Index->new; 97 | for my $package ($self->packages) { 98 | $index->add_package($package); 99 | } 100 | 101 | return $index; 102 | } 103 | 104 | sub distributions { 105 | @{$_[0]->_distributions}; 106 | } 107 | 108 | sub add_distribution { 109 | my($self, $dist) = @_; 110 | push @{$self->_distributions}, $dist; 111 | } 112 | 113 | sub packages { 114 | my $self = shift; 115 | 116 | my @packages; 117 | for my $dist ($self->distributions) { 118 | while (my($package, $provides) = each %{$dist->provides}) { 119 | # TODO what if duplicates? 120 | push @packages, Carton::Package->new($package, $provides->{version}, $dist->pathname); 121 | } 122 | } 123 | 124 | return @packages; 125 | } 126 | 127 | sub write_index { 128 | my($self, $file) = @_; 129 | 130 | open my $fh, ">", $file or die $!; 131 | $self->index->write($fh); 132 | } 133 | 134 | sub find_installs { 135 | my($self, $path, $reqs) = @_; 136 | 137 | my $libdir = "$path/lib/perl5/$Config{archname}/.meta"; 138 | return {} unless -e $libdir; 139 | 140 | my @installs; 141 | my $wanted = sub { 142 | if ($_ eq 'install.json') { 143 | push @installs, [ $File::Find::name, "$File::Find::dir/MYMETA.json" ]; 144 | } 145 | }; 146 | File::Find::find($wanted, $libdir); 147 | 148 | my %installs; 149 | 150 | my $accepts = sub { 151 | my $module = shift; 152 | 153 | return 0 unless $reqs->accepts_module($module->{name}, $module->{provides}{$module->{name}}{version}); 154 | 155 | if (my $exist = $installs{$module->{name}}) { 156 | my $old_ver = version::->new($exist->{provides}{$module->{name}}{version}); 157 | my $new_ver = version::->new($module->{provides}{$module->{name}}{version}); 158 | return $new_ver >= $old_ver; 159 | } else { 160 | return 1; 161 | } 162 | }; 163 | 164 | for my $file (@installs) { 165 | my $module = Carton::Util::load_json($file->[0]); 166 | my $prereqs = -f $file->[1] ? CPAN::Meta->load_file($file->[1])->effective_prereqs : CPAN::Meta::Prereqs->new; 167 | 168 | my $reqs = CPAN::Meta::Requirements->new; 169 | $reqs->add_requirements($prereqs->requirements_for($_, 'requires')) 170 | for qw( configure build runtime ); 171 | 172 | if ($accepts->($module)) { 173 | $installs{$module->{name}} = Carton::Dist->new( 174 | name => $module->{dist}, 175 | pathname => $module->{pathname}, 176 | provides => $module->{provides}, 177 | version => $module->{version}, 178 | requirements => $reqs, 179 | ); 180 | } 181 | } 182 | 183 | my @new_dists; 184 | for my $module (sort keys %installs) { 185 | push @new_dists, $installs{$module}; 186 | } 187 | 188 | $self->_distributions(\@new_dists); 189 | } 190 | 191 | 1; 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NAME 2 | 3 | Carton - Perl module dependency manager (aka Bundler for Perl) 4 | 5 | # SYNOPSIS 6 | 7 | # On your development environment 8 | > cat cpanfile 9 | requires 'Plack', '0.9980'; 10 | requires 'Starman', '0.2000'; 11 | 12 | > carton install 13 | > git add cpanfile cpanfile.snapshot 14 | > git commit -m "add Plack and Starman" 15 | 16 | # Other developer's machine, or on a deployment box 17 | > carton install 18 | > carton exec starman -p 8080 myapp.psgi 19 | 20 | # AVAILABILITY 21 | 22 | Carton only works with perl installation with the complete set of core 23 | modules. If you use perl installed by a vendor package with modules 24 | stripped from core, Carton is not expected to work correctly. 25 | 26 | Also, Carton requires you to run your command/application with 27 | `carton exec` command, which means it's difficult or impossible to 28 | run in an embedded perl use case such as mod\_perl. 29 | 30 | # DESCRIPTION 31 | 32 | carton is a command line tool to track the Perl module dependencies 33 | for your Perl application. Dependencies are declared using [cpanfile](https://metacpan.org/pod/cpanfile) 34 | format, and the managed dependencies are tracked in a 35 | _cpanfile.snapshot_ file, which is meant to be version controlled, 36 | and the snapshot file allows other developers of your application will 37 | have the exact same versions of the modules. 38 | 39 | For `cpanfile` syntax, see [cpanfile](https://metacpan.org/pod/cpanfile) documentation. 40 | 41 | # TUTORIAL 42 | 43 | ## Initializing the environment 44 | 45 | carton will use the _local_ directory to install modules into. You're 46 | recommended to exclude these directories from the version control 47 | system. 48 | 49 | > echo local/ >> .gitignore 50 | > git add cpanfile cpanfile.snapshot 51 | > git commit -m "Start using carton" 52 | 53 | ## Tracking the dependencies 54 | 55 | You can manage the dependencies of your application via `cpanfile`. 56 | 57 | # cpanfile 58 | requires 'Plack', '0.9980'; 59 | requires 'Starman', '0.2000'; 60 | 61 | And then you can install these dependencies via: 62 | 63 | > carton install 64 | 65 | The modules are installed into your _local_ directory, and the 66 | dependencies tree and version information are analyzed and saved into 67 | _cpanfile.snapshot_ in your directory. 68 | 69 | Make sure you add _cpanfile_ and _cpanfile.snapshot_ to your version 70 | controlled repository and commit changes as you update 71 | dependencies. This will ensure that other developers on your app, as 72 | well as your deployment environment, use exactly the same versions of 73 | the modules you just installed. 74 | 75 | > git add cpanfile cpanfile.snapshot 76 | > git commit -m "Added Plack and Starman" 77 | 78 | ## Deploying your application 79 | 80 | Once you've done installing all the dependencies, you can push your 81 | application directory to a remote machine (excluding _local_ and 82 | _.carton_) and run the following command: 83 | 84 | > carton install --deployment 85 | 86 | This will look at the _cpanfile.snapshot_ and install the exact same 87 | versions of the dependencies into _local_, and now your application 88 | is ready to run. 89 | 90 | The `--deployment` flag makes sure that carton will only install 91 | modules and versions available in your snapshot, and won't fallback to 92 | query for CPAN Meta DB for missing modules. 93 | 94 | ## Bundling modules 95 | 96 | carton can bundle all the tarballs for your dependencies into a 97 | directory so that you can even install dependencies that are not 98 | available on CPAN, such as internal distribution aka DarkPAN. 99 | 100 | > carton bundle 101 | 102 | will bundle these tarballs into _vendor/cache_ directory, and 103 | 104 | > carton install --cached 105 | 106 | will install modules using this local cache. Combined with 107 | `--deployment` option, you can avoid querying for a database like 108 | CPAN Meta DB or downloading files from CPAN mirrors upon deployment 109 | time. 110 | 111 | # PERL VERSIONS 112 | 113 | When you take a snapshot in one perl version and deploy on another 114 | (different) version, you might have troubles with core modules. 115 | 116 | The simplest solution, which might not work for everybody, is to use 117 | the same version of perl in the development and deployment. 118 | 119 | To enforce that, you're recommended to use [plenv](https://metacpan.org/pod/plenv) and 120 | `.perl-version` to lock perl versions in development. 121 | 122 | You can also specify the minimum perl required in `cpanfile`: 123 | 124 | requires 'perl', '5.16.3'; 125 | 126 | and carton (and cpanm) will give you errors when deployed on hosts 127 | with perl lower than the specified version. 128 | 129 | # COMMUNITY 130 | 131 | - [https://github.com/miyagawa/carton](https://github.com/miyagawa/carton) 132 | 133 | Code repository, Wiki and Issue Tracker 134 | 135 | - [irc://irc.perl.org/#carton](irc://irc.perl.org/#carton) 136 | 137 | IRC chat room 138 | 139 | # AUTHOR 140 | 141 | Tatsuhiko Miyagawa 142 | 143 | # COPYRIGHT 144 | 145 | Tatsuhiko Miyagawa 2011- 146 | 147 | # LICENSE 148 | 149 | This software is licensed under the same terms as Perl itself. 150 | 151 | # SEE ALSO 152 | 153 | [cpanm](https://metacpan.org/pod/cpanm) 154 | 155 | [cpanfile](https://metacpan.org/pod/cpanfile) 156 | 157 | [Bundler](http://gembundler.com/) 158 | 159 | [pip](http://pypi.python.org/pypi/pip) 160 | 161 | [npm](http://npmjs.org/) 162 | 163 | [perlrocks](https://github.com/gugod/perlrocks) 164 | 165 | [only](https://metacpan.org/pod/only) 166 | -------------------------------------------------------------------------------- /lib/Carton.pm: -------------------------------------------------------------------------------- 1 | package Carton; 2 | use strict; 3 | use 5.008_005; 4 | use version; our $VERSION = version->declare("v1.0.16"); 5 | 6 | 1; 7 | __END__ 8 | 9 | =head1 NAME 10 | 11 | Carton - Perl module dependency manager (aka Bundler for Perl) 12 | 13 | =head1 SYNOPSIS 14 | 15 | # On your development environment 16 | > cat cpanfile 17 | requires 'Plack', '0.9980'; 18 | requires 'Starman', '0.2000'; 19 | 20 | > carton install 21 | > git add cpanfile cpanfile.snapshot 22 | > git commit -m "add Plack and Starman" 23 | 24 | # Other developer's machine, or on a deployment box 25 | > carton install 26 | > carton exec starman -p 8080 myapp.psgi 27 | 28 | =head1 AVAILABILITY 29 | 30 | Carton only works with perl installation with the complete set of core 31 | modules. If you use perl installed by a vendor package with modules 32 | stripped from core, Carton is not expected to work correctly. 33 | 34 | Also, Carton requires you to run your command/application with 35 | C command, which means it's difficult or impossible to 36 | run in an embedded perl use case such as mod_perl. 37 | 38 | =head1 DESCRIPTION 39 | 40 | carton is a command line tool to track the Perl module dependencies 41 | for your Perl application. Dependencies are declared using L 42 | format, and the managed dependencies are tracked in a 43 | I file, which is meant to be version controlled, 44 | and the snapshot file allows other developers of your application will 45 | have the exact same versions of the modules. 46 | 47 | For C syntax, see L documentation. 48 | 49 | =head1 TUTORIAL 50 | 51 | =head2 Initializing the environment 52 | 53 | carton will use the I directory to install modules into. You're 54 | recommended to exclude these directories from the version control 55 | system. 56 | 57 | > echo local/ >> .gitignore 58 | > git add cpanfile cpanfile.snapshot 59 | > git commit -m "Start using carton" 60 | 61 | =head2 Tracking the dependencies 62 | 63 | You can manage the dependencies of your application via C. 64 | 65 | # cpanfile 66 | requires 'Plack', '0.9980'; 67 | requires 'Starman', '0.2000'; 68 | 69 | And then you can install these dependencies via: 70 | 71 | > carton install 72 | 73 | The modules are installed into your I directory, and the 74 | dependencies tree and version information are analyzed and saved into 75 | I in your directory. 76 | 77 | Make sure you add I and I to your version 78 | controlled repository and commit changes as you update 79 | dependencies. This will ensure that other developers on your app, as 80 | well as your deployment environment, use exactly the same versions of 81 | the modules you just installed. 82 | 83 | > git add cpanfile cpanfile.snapshot 84 | > git commit -m "Added Plack and Starman" 85 | 86 | =head2 Deploying your application 87 | 88 | Once you've done installing all the dependencies, you can push your 89 | application directory to a remote machine (excluding I and 90 | I<.carton>) and run the following command: 91 | 92 | > carton install --deployment 93 | 94 | This will look at the I and install the exact same 95 | versions of the dependencies into I, and now your application 96 | is ready to run. 97 | 98 | The C<--deployment> flag makes sure that carton will only install 99 | modules and versions available in your snapshot, and won't fallback to 100 | query for CPAN Meta DB for missing modules. 101 | 102 | =head2 Bundling modules 103 | 104 | carton can bundle all the tarballs for your dependencies into a 105 | directory so that you can even install dependencies that are not 106 | available on CPAN, such as internal distribution aka DarkPAN. 107 | 108 | > carton bundle 109 | 110 | will bundle these tarballs into I directory, and 111 | 112 | > carton install --cached 113 | 114 | will install modules using this local cache. Combined with 115 | C<--deployment> option, you can avoid querying for a database like 116 | CPAN Meta DB or downloading files from CPAN mirrors upon deployment 117 | time. 118 | 119 | =head1 PERL VERSIONS 120 | 121 | When you take a snapshot in one perl version and deploy on another 122 | (different) version, you might have troubles with core modules. 123 | 124 | The simplest solution, which might not work for everybody, is to use 125 | the same version of perl in the development and deployment. 126 | 127 | To enforce that, you're recommended to use L and 128 | C<.perl-version> to lock perl versions in development. 129 | 130 | You can also specify the minimum perl required in C: 131 | 132 | requires 'perl', '5.16.3'; 133 | 134 | and carton (and cpanm) will give you errors when deployed on hosts 135 | with perl lower than the specified version. 136 | 137 | =head1 COMMUNITY 138 | 139 | =over 4 140 | 141 | =item L 142 | 143 | Code repository, Wiki and Issue Tracker 144 | 145 | =item L 146 | 147 | IRC chat room 148 | 149 | =back 150 | 151 | =head1 AUTHOR 152 | 153 | Tatsuhiko Miyagawa 154 | 155 | =head1 COPYRIGHT 156 | 157 | Tatsuhiko Miyagawa 2011- 158 | 159 | =head1 LICENSE 160 | 161 | This software is licensed under the same terms as Perl itself. 162 | 163 | =head1 SEE ALSO 164 | 165 | L 166 | 167 | L 168 | 169 | L 170 | 171 | L 172 | 173 | L 174 | 175 | L 176 | 177 | L 178 | 179 | =cut 180 | -------------------------------------------------------------------------------- /Changes: -------------------------------------------------------------------------------- 1 | Revision history for carton 2 | 3 | {{$NEXT}} 4 | 5 | v1.0.16 2015-04-25 06:37:17 PDT 6 | - update 02packages.details.txt whitespace padding to follow PAUSE 7 | 8 | v1.0.15 2015-04-20 11:13:32 CEST 9 | - downgrade some dependencies for fatpack-related tools to recommends 10 | (probably ship it as a separate distribution in the future) 11 | 12 | v1.0.14 2015-04-20 00:07:26 CEST 13 | - same as v1.0.14 14 | 15 | v1.0.13 2015-04-19 19:38:42 CEST 16 | - require cpanm 1.7030 for better cpanfile support 17 | - switch to MakeMaker 18 | - remove Exception::Class and Moo in favor of Class::Tiny 19 | - add an ability to set generator in Carton::Index for Carmel use 20 | 21 | v1.0.12 2013-09-24 20:03:47 JST 22 | - up Path::Tiny 23 | - Add --no-fatpack to carton bundle #140 24 | 25 | v1.0.11 2013-09-18 18:51:14 JST 26 | - Disable fatal warnings that comes with Moo. This will make Path::Tiny not fail 27 | on NFS without flock. #135 28 | 29 | v1.0.10 2013-09-02 17:52:42 PDT 30 | - Documentation fixes 31 | - Support CRLF in snapshot (kan) #133 32 | 33 | v1.0.9 2013-08-17 11:24:46 PDT 34 | - Workaround carton help shows wrong doc on case insensitive filesystems 35 | 36 | v1.0.8 2013-08-16 18:38:14 PDT 37 | - Include POD documentation for carton(1) 38 | 39 | v1.0.7 2013-08-10 21:55:29 PDT 40 | - Worked around fatpack issue with perl < 5.16 with missing File::Spec 41 | - Included missing requirements for fatpack executable 42 | 43 | v1.0.6 2013-08-10 17:59:51 PDT 44 | - Added upgrading documentation (carton help upgrading) 45 | - Experimental support for fatpacked carton executable in vendor/bin in bundle 46 | 47 | v1.0.5 2013-08-08 12:50:39 PDT 48 | - Bump cpanm for version extraction #126 49 | - Fix doc about --cached (shibayu36) 50 | - Fix Usage errors #123 51 | 52 | v1.0.4 2013-08-05 19:20:11 PDT 53 | - Bump cpanm to deal with failing to extract versions with version.pm #120 (lestrrat) 54 | 55 | v1.0.3 2013-08-05 10:17:59 PDT 56 | - Update obsolete docs 57 | - Added missing docs to some commands 58 | 59 | v1.0.2 2013-08-04 21:49:14 PDT 60 | - Bump cpanm dependency to deal with old dists with ancient META.yml (tokuhirom) 61 | 62 | v1.0.1 2013-08-04 17:10:10 PDT 63 | - Update docs 64 | - Fixed bug where version range requirements are not properly preserved in the snapshot #117 (lestrrat) 65 | 66 | v1.0.0 2013-08-04 12:29:31 PDT 67 | - This makes 1.0 release 68 | - Documentation update 69 | - Bump cpanm dependency 70 | 71 | v0.9.68 2013-07-26 17:49:28 PDT 72 | - Change the distribution name case, to match with the module name 73 | - Add Module::CoreList as a dependency for perl 5.8 74 | 75 | v0.9.67 2013-07-24 14:53:53 PDT 76 | - Use cpanm's fatscript interface rather than share dir 77 | 78 | v0.9.66 2013-07-24 08:46:27 PDT 79 | - Correctly raises an exception when badly formatted snapshot file is read 80 | - Fixed a bug in tree scanner where seen hash is not preserved 81 | - tree scanner should be much faster for giant set of dependencies 82 | 83 | v0.9.65 2013-07-23 18:51:59 PDT 84 | - BIG CHANGE: Use cpanfile.snapshot instead of carton.lock for 1.0 onwards 85 | There is no way to migrate carton.lock to cpanfile.snapshot just yet. 86 | - New cpanfile.snapshot is text based and is more VCS friendly 87 | - Reworked internal of prereqs/requirements loader (again!) to eliminate 88 | lots of duplicate code 89 | 90 | v0.9.64 2013-07-23 14:39:54 PDT 91 | - Locate cpanm within a dist share dir so that cpanm in user's $PATH is not executed. 92 | This will solve issues with cpanm in /usr/bin, perlbrew or with bad shebang #92 93 | 94 | v0.9.63 2013-07-23 02:26:04 PDT 95 | - Bump cpanminus requirement 96 | - Support --cpanfile for carton install 97 | - Support PERL_CARTON_CPANFILE (for commands other than install) 98 | 99 | v0.9.62 2013-07-22 14:33:11 PDT 100 | - Now all carton commands can be run from a subdirectory #69 101 | - Refactored the way cpanfile/carton.lock files are detected 102 | 103 | v0.9.61 2013-07-22 10:24:35 PDT 104 | - Implemented experimental --without option for carton install 105 | 106 | v0.9.60 2013-06-26 12:22:21 PDT 107 | - Bump MakeMaker and Module::Build dependencies to support test requires 108 | 109 | v0.9.59 2013-06-17 17:13:21 PDT 110 | - carton exec -Ilib gives a warning, while carton exec perl -Ilib won't #97 111 | 112 | v0.9.58 2013-06-10 03:17:23 PDT 113 | - Fix tests 114 | - carton exec without an arg should raise an error 115 | - typo fixes 116 | 117 | v0.9.57 2013-06-05 19:21:17 JST 118 | - Changed the output of carton tree command to include module and dist versions 119 | - Bunch of refactorings around requirements 120 | - carton install now saves tarballs in local/cache, then carton bundle copies from there 121 | - Implement carton check which checks if cpanfile requirements is satisfied locally with lock 122 | - Implement carton update! 123 | - Fix the installation collector logic to ignore dists that don't satisfy cpanfile 124 | 125 | v0.9.56 2013-06-04 00:21:53 JST 126 | - Fixed carton tree output to avoid duplicates 127 | 128 | v0.9.55 2013-06-03 23:43:52 JST 129 | - Added back carton tree command 130 | - Added --distfile option to list command 131 | 132 | v0.9.54 2013-06-02 12:38:20 JST 133 | - Install develop phase dependencies by default with carton install 134 | - carton exec now doesn't set PERL5OPT with lib::core::only, so as not to mess with 135 | the subprocess and site_perl modules (#60, #70, #82) 136 | 137 | v0.9.53 2013-06-01 23:54:53 JST 138 | - use Moo 139 | - refactored installer/downloader as Carton::Builder 140 | 141 | v0.9.52 2013-06-01 17:01:51 JST 142 | - carton exec doesn't need '--' before perl anymore #77 143 | - remove even more unused code 144 | - backed out color output support 145 | - stopped collecting dependencies from cpanfile, since cpanm installdeps can read it directly 146 | - Temporarily disabled check command for now 147 | - Upped cpanm dependency 148 | - Use vendor/cache for bundling since local is most likely gitignored #88 149 | - carton exec now requires carton install beforehand 150 | 151 | v0.9.51 2013-05-31 09:02:58 JST 152 | - Documentation fixes 153 | - Fixes test dependencies and build system 154 | 155 | v0.9.50 2013-05-31 02:18:07 JST 156 | - Documentation fixes 157 | - remove bunch of code that is unused 158 | - removed tree command for now 159 | - Overhauled the way bundle command works 160 | - refactored lock and index generation code 161 | - Enabled Travis CI tests 162 | 163 | v0.9.15 2013-03-31 18:11:28 PDT 164 | - Add minimum perl dependency 165 | 166 | v0.9.14 2013-03-30 18:25:39 PDT 167 | - Unset $VERSION on PAUSE (Thanks andk) 168 | 169 | v0.9.13 2013-03-30 15:14:49 PDT 170 | - repackage for better META files with Milla v0.9.3 171 | 172 | v0.9.12 2013-03-30 15:01:55 PDT 173 | - repackage to set $VERSION 174 | 175 | v0.9.11 2013-03-30 14:54:21 PDT 176 | - Ignore 'perl' requirements so as it won't fail, for now. #71 177 | - Install 'test' dependencies by default. #66 178 | - Convert to Milla, do not install carton-* man pages 179 | 180 | v0.9.10 Tue Feb 26 13:32:34 PST 2013 181 | - Same as v0.9_9. Still considered pre-1.0! 182 | 183 | v0.9_9 Wed Feb 6 11:02:46 PST 2013 184 | - Fixed bundle command where it updated modules, not the versions specified in carton.lock. 185 | bundle now builds mirror files like install --deployment, and downloads tarballs for the 186 | specified versions. (vti) 187 | 188 | v0.9_8 Tue Feb 5 12:17:54 PST 2013 189 | - Do not use carton.lock to build extra dependencies. Everything has to be 190 | pulled out of cpanfile, even with the deployment mode. This makes the deployment 191 | much more reliable, and could possibly work with differing os/perl versions 192 | across development and deployments. 193 | 194 | v0.9_7 Sat May 12 06:15:44 EEST 2012 195 | - Experimental multiple mirror support (nihen) 196 | - Fixed cpanm dependency to avoid cascading bug 197 | 198 | v0.9_6 Thu May 10 21:05:35 CEST 2012 199 | - use cpanfile + Module::Install for dogfooding 200 | - `carton` without args now does `carton install` (inspired by bundler) 201 | - Update bundle command to use install.json (masaki) 202 | - code cleanups and doc overhauls 203 | - removed `uninstall` command for now 204 | - Fixed CPAN::Meta::Requirements dependency 205 | 206 | v0.9_5 Thu Apr 12 19:39:19 JST 2012 207 | - Added experimental cpanfile support 208 | - Fixed POD (yanick) 209 | 210 | v0.9.4 Sat Mar 31 13:49:41 CEST 2012 211 | - use Capture::Tiny to capture output (wchristian) 212 | - Improve synopsis for exec (dagolden) 213 | - Implemented bundle command (masaki) 214 | - Fix Getopt::Long dependency (pfig) 215 | 216 | v0.9.3 Wed Oct 19 14:30:50 JST 2011 217 | - Fixed META.yml by patching Module::Install and repackaging 218 | 219 | v0.9.2 Tue Oct 18 12:53:57 JST 2011 220 | - Fixed packaging *again* by declaring version as a simple string 221 | via http://www.dagolden.com/index.php/369/version-numbers-should-be-boring/ 222 | 223 | v0.9.1 Mon Oct 17 19:05:12 JST 2011 224 | - Fixed packaging 225 | - Fixed UTF8 encoding warnings for JSON 226 | 227 | v0.9.0 Fri Oct 14 01:27:02 JST 2011 228 | - Initial non-dev release. Still considered beta before it hits 1.0.0! 229 | 230 | v0.1_0 Sun Jun 26 11:03:50 PDT 2011 231 | - original version 232 | -------------------------------------------------------------------------------- /lib/Carton/CLI.pm: -------------------------------------------------------------------------------- 1 | package Carton::CLI; 2 | use strict; 3 | use Config; 4 | use Getopt::Long; 5 | use Path::Tiny; 6 | use Try::Tiny; 7 | use Module::CoreList; 8 | use Scalar::Util qw(blessed); 9 | 10 | use Carton; 11 | use Carton::Builder; 12 | use Carton::Mirror; 13 | use Carton::Snapshot; 14 | use Carton::Util; 15 | use Carton::Environment; 16 | use Carton::Error; 17 | 18 | use constant { SUCCESS => 0, INFO => 1, WARN => 2, ERROR => 3 }; 19 | 20 | our $UseSystem = 0; # 1 for unit testing 21 | 22 | use Class::Tiny { 23 | verbose => undef, 24 | carton => sub { $_[0]->_build_carton }, 25 | mirror => sub { $_[0]->_build_mirror }, 26 | }; 27 | 28 | sub _build_mirror { 29 | my $self = shift; 30 | Carton::Mirror->new($ENV{PERL_CARTON_MIRROR} || $Carton::Mirror::DefaultMirror); 31 | } 32 | 33 | sub run { 34 | my($self, @args) = @_; 35 | 36 | my @commands; 37 | my $p = Getopt::Long::Parser->new( 38 | config => [ "no_ignore_case", "pass_through" ], 39 | ); 40 | $p->getoptionsfromarray( 41 | \@args, 42 | "h|help" => sub { unshift @commands, 'help' }, 43 | "v|version" => sub { unshift @commands, 'version' }, 44 | "verbose!" => sub { $self->verbose($_[1]) }, 45 | ); 46 | 47 | push @commands, @args; 48 | 49 | my $cmd = shift @commands || 'install'; 50 | 51 | my $code = try { 52 | my $call = $self->can("cmd_$cmd") 53 | or Carton::Error::CommandNotFound->throw(error => "Could not find command '$cmd'"); 54 | $self->$call(@commands); 55 | return 0; 56 | } catch { 57 | die $_ unless blessed $_ && $_->can('rethrow'); 58 | 59 | if ($_->isa('Carton::Error::CommandExit')) { 60 | return $_->code || 255; 61 | } elsif ($_->isa('Carton::Error::CommandNotFound')) { 62 | warn $_->error, "\n\n"; 63 | $self->cmd_usage; 64 | return 255; 65 | } elsif ($_->isa('Carton::Error')) { 66 | warn $_->error, "\n"; 67 | return 255; 68 | } 69 | }; 70 | 71 | return $code; 72 | } 73 | 74 | sub commands { 75 | my $self = shift; 76 | 77 | no strict 'refs'; 78 | map { s/^cmd_//; $_ } 79 | grep { /^cmd_.*/ && $self->can($_) } sort keys %{__PACKAGE__."::"}; 80 | } 81 | 82 | sub cmd_usage { 83 | my $self = shift; 84 | $self->print(< 86 | 87 | where is one of: 88 | @{[ join ", ", $self->commands ]} 89 | 90 | Run carton -h for help. 91 | HELP 92 | } 93 | 94 | sub parse_options { 95 | my($self, $args, @spec) = @_; 96 | my $p = Getopt::Long::Parser->new( 97 | config => [ "no_auto_abbrev", "no_ignore_case" ], 98 | ); 99 | $p->getoptionsfromarray($args, @spec); 100 | } 101 | 102 | sub parse_options_pass_through { 103 | my($self, $args, @spec) = @_; 104 | 105 | my $p = Getopt::Long::Parser->new( 106 | config => [ "no_auto_abbrev", "no_ignore_case", "pass_through" ], 107 | ); 108 | $p->getoptionsfromarray($args, @spec); 109 | 110 | # with pass_through keeps -- in args 111 | shift @$args if $args->[0] && $args->[0] eq '--'; 112 | } 113 | 114 | sub printf { 115 | my $self = shift; 116 | my $type = pop; 117 | my($temp, @args) = @_; 118 | $self->print(sprintf($temp, @args), $type); 119 | } 120 | 121 | sub print { 122 | my($self, $msg, $type) = @_; 123 | my $fh = $type && $type >= WARN ? *STDERR : *STDOUT; 124 | print {$fh} $msg; 125 | } 126 | 127 | sub error { 128 | my($self, $msg) = @_; 129 | $self->print($msg, ERROR); 130 | Carton::Error::CommandExit->throw; 131 | } 132 | 133 | sub cmd_help { 134 | my $self = shift; 135 | my $module = $_[0] ? ("Carton::Doc::" . ucfirst $_[0]) : "Carton.pm"; 136 | system "perldoc", $module; 137 | } 138 | 139 | sub cmd_version { 140 | my $self = shift; 141 | $self->print("carton $Carton::VERSION\n"); 142 | } 143 | 144 | sub cmd_bundle { 145 | my($self, @args) = @_; 146 | 147 | my $fatpack = 1; 148 | $self->parse_options( 149 | \@args, 150 | "fatpack!" => \$fatpack, 151 | ); 152 | 153 | my $env = Carton::Environment->build; 154 | $env->snapshot->load; 155 | 156 | $self->print("Bundling modules using @{[$env->cpanfile]}\n"); 157 | 158 | my $builder = Carton::Builder->new( 159 | mirror => $self->mirror, 160 | cpanfile => $env->cpanfile, 161 | ); 162 | $builder->bundle($env->install_path, $env->vendor_cache, $env->snapshot); 163 | 164 | if ($fatpack) { 165 | require Carton::Packer; 166 | Carton::Packer->new->fatpack_carton($env->vendor_bin); 167 | } 168 | 169 | $self->printf("Complete! Modules were bundled into %s\n", $env->vendor_cache, SUCCESS); 170 | } 171 | 172 | sub cmd_install { 173 | my($self, @args) = @_; 174 | 175 | my($install_path, $cpanfile_path, @without); 176 | 177 | $self->parse_options( 178 | \@args, 179 | "p|path=s" => \$install_path, 180 | "cpanfile=s" => \$cpanfile_path, 181 | "without=s" => sub { push @without, split /,/, $_[1] }, 182 | "deployment!" => \my $deployment, 183 | "cached!" => \my $cached, 184 | ); 185 | 186 | my $env = Carton::Environment->build($cpanfile_path, $install_path); 187 | $env->snapshot->load_if_exists; 188 | 189 | if ($deployment && !$env->snapshot->loaded) { 190 | $self->error("--deployment requires cpanfile.snapshot: Run `carton install` and make sure cpanfile.snapshot is checked into your version control.\n"); 191 | } 192 | 193 | my $builder = Carton::Builder->new( 194 | cascade => 1, 195 | mirror => $self->mirror, 196 | without => \@without, 197 | cpanfile => $env->cpanfile, 198 | ); 199 | 200 | # TODO: --without with no .lock won't fetch the groups, resulting in insufficient requirements 201 | 202 | if ($deployment) { 203 | $self->print("Installing modules using @{[$env->cpanfile]} (deployment mode)\n"); 204 | $builder->cascade(0); 205 | } else { 206 | $self->print("Installing modules using @{[$env->cpanfile]}\n"); 207 | } 208 | 209 | # TODO merge CPANfile git to mirror even if lock doesn't exist 210 | if ($env->snapshot->loaded) { 211 | my $index_file = $env->install_path->child("cache/modules/02packages.details.txt"); 212 | $index_file->parent->mkpath; 213 | 214 | $env->snapshot->write_index($index_file); 215 | $builder->index($index_file); 216 | } 217 | 218 | if ($cached) { 219 | $builder->mirror(Carton::Mirror->new($env->vendor_cache)); 220 | } 221 | 222 | $builder->install($env->install_path); 223 | 224 | unless ($deployment) { 225 | $env->cpanfile->load; 226 | $env->snapshot->find_installs($env->install_path, $env->cpanfile->requirements); 227 | $env->snapshot->save; 228 | } 229 | 230 | $self->print("Complete! Modules were installed into @{[$env->install_path]}\n", SUCCESS); 231 | } 232 | 233 | sub cmd_show { 234 | my($self, @args) = @_; 235 | 236 | my $env = Carton::Environment->build; 237 | $env->snapshot->load; 238 | 239 | for my $module (@args) { 240 | my $dist = $env->snapshot->find($module) 241 | or $self->error("Couldn't locate $module in cpanfile.snapshot\n"); 242 | $self->print( $dist->name . "\n" ); 243 | } 244 | } 245 | 246 | sub cmd_list { 247 | my($self, @args) = @_; 248 | 249 | my $format = 'name'; 250 | 251 | $self->parse_options( 252 | \@args, 253 | "distfile" => sub { $format = 'distfile' }, 254 | ); 255 | 256 | my $env = Carton::Environment->build; 257 | $env->snapshot->load; 258 | 259 | for my $dist ($env->snapshot->distributions) { 260 | $self->print($dist->$format . "\n"); 261 | } 262 | } 263 | 264 | sub cmd_tree { 265 | my($self, @args) = @_; 266 | 267 | my $env = Carton::Environment->build; 268 | $env->snapshot->load; 269 | $env->cpanfile->load; 270 | 271 | my %seen; 272 | my $dumper = sub { 273 | my($dependency, $reqs, $level) = @_; 274 | return if $level == 0; 275 | return Carton::Tree::STOP if $dependency->dist->is_core; 276 | return Carton::Tree::STOP if $seen{$dependency->distname}++; 277 | $self->printf( "%s%s (%s)\n", " " x ($level - 1), $dependency->module, $dependency->distname, INFO ); 278 | }; 279 | 280 | $env->tree->walk_down($dumper); 281 | } 282 | 283 | sub cmd_check { 284 | my($self, @args) = @_; 285 | 286 | my $cpanfile_path; 287 | $self->parse_options( 288 | \@args, 289 | "cpanfile=s" => \$cpanfile_path, 290 | ); 291 | 292 | my $env = Carton::Environment->build($cpanfile_path); 293 | $env->snapshot->load; 294 | $env->cpanfile->load; 295 | 296 | # TODO remove snapshot 297 | # TODO pass git spec to Requirements? 298 | my $merged_reqs = $env->tree->merged_requirements; 299 | 300 | my @missing; 301 | for my $module ($merged_reqs->required_modules) { 302 | my $install = $env->snapshot->find_or_core($module); 303 | if ($install) { 304 | unless ($merged_reqs->accepts_module($module => $install->version_for($module))) { 305 | push @missing, [ $module, 1, $install->version_for($module) ]; 306 | } 307 | } else { 308 | push @missing, [ $module, 0 ]; 309 | } 310 | } 311 | 312 | if (@missing) { 313 | $self->print("Following dependencies are not satisfied.\n", INFO); 314 | for my $missing (@missing) { 315 | my($module, $unsatisfied, $version) = @$missing; 316 | if ($unsatisfied) { 317 | $self->printf(" %s has version %s. Needs %s\n", 318 | $module, $version, $merged_reqs->requirements_for_module($module), INFO); 319 | } else { 320 | $self->printf(" %s is not installed. Needs %s\n", 321 | $module, $merged_reqs->requirements_for_module($module), INFO); 322 | } 323 | } 324 | $self->printf("Run `carton install` to install them.\n", INFO); 325 | Carton::Error::CommandExit->throw; 326 | } else { 327 | $self->print("cpanfile's dependencies are satisfied.\n", INFO); 328 | } 329 | } 330 | 331 | sub cmd_update { 332 | my($self, @args) = @_; 333 | 334 | my $env = Carton::Environment->build; 335 | $env->cpanfile->load; 336 | 337 | 338 | my $cpanfile = Module::CPANfile->load($env->cpanfile); 339 | @args = grep { $_ ne 'perl' } $env->cpanfile->required_modules unless @args; 340 | 341 | $env->snapshot->load; 342 | 343 | my @modules; 344 | for my $module (@args) { 345 | my $dist = $env->snapshot->find_or_core($module) 346 | or $self->error("Could not find module $module.\n"); 347 | next if $dist->is_core; 348 | push @modules, "$module~" . $env->cpanfile->requirements_for_module($module); 349 | } 350 | 351 | my $builder = Carton::Builder->new( 352 | mirror => $self->mirror, 353 | cpanfile => $env->cpanfile, 354 | ); 355 | $builder->update($env->install_path, @modules); 356 | 357 | $env->snapshot->find_installs($env->install_path, $env->cpanfile->requirements); 358 | $env->snapshot->save; 359 | } 360 | 361 | sub cmd_exec { 362 | my($self, @args) = @_; 363 | 364 | my $env = Carton::Environment->build; 365 | $env->snapshot->load; 366 | 367 | # allows -Ilib 368 | @args = map { /^(-[I])(.+)/ ? ($1,$2) : $_ } @args; 369 | 370 | while (@args) { 371 | if ($args[0] eq '-I') { 372 | warn "exec -Ilib is deprecated. You might want to run: carton exec perl -Ilib ...\n"; 373 | splice(@args, 0, 2); 374 | } else { 375 | last; 376 | } 377 | } 378 | 379 | $self->parse_options_pass_through(\@args); # to handle -- 380 | 381 | unless (@args) { 382 | $self->error("carton exec needs a command to run.\n"); 383 | } 384 | 385 | # PERL5LIB takes care of arch 386 | my $path = $env->install_path; 387 | local $ENV{PERL5LIB} = "$path/lib/perl5"; 388 | local $ENV{PATH} = "$path/bin:$ENV{PATH}"; 389 | 390 | $UseSystem ? system(@args) : exec(@args); 391 | } 392 | 393 | 1; 394 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is copyright (c) 2011- by Tatsuhiko Miyagawa. 2 | 3 | This is free software; you can redistribute it and/or modify it under 4 | the same terms as the Perl 5 programming language system itself. 5 | 6 | Terms of the Perl programming language system itself 7 | 8 | a) the GNU General Public License as published by the Free 9 | Software Foundation; either version 1, or (at your option) any 10 | later version, or 11 | b) the "Artistic License" 12 | 13 | --- The GNU General Public License, Version 1, February 1989 --- 14 | 15 | This software is Copyright (c) 2011- by Tatsuhiko Miyagawa. 16 | 17 | This is free software, licensed under: 18 | 19 | The GNU General Public License, Version 1, February 1989 20 | 21 | GNU GENERAL PUBLIC LICENSE 22 | Version 1, February 1989 23 | 24 | Copyright (C) 1989 Free Software Foundation, Inc. 25 | 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 26 | 27 | Everyone is permitted to copy and distribute verbatim copies 28 | of this license document, but changing it is not allowed. 29 | 30 | Preamble 31 | 32 | The license agreements of most software companies try to keep users 33 | at the mercy of those companies. By contrast, our General Public 34 | License is intended to guarantee your freedom to share and change free 35 | software--to make sure the software is free for all its users. The 36 | General Public License applies to the Free Software Foundation's 37 | software and to any other program whose authors commit to using it. 38 | You can use it for your programs, too. 39 | 40 | When we speak of free software, we are referring to freedom, not 41 | price. Specifically, the General Public License is designed to make 42 | sure that you have the freedom to give away or sell copies of free 43 | software, that you receive source code or can get it if you want it, 44 | that you can change the software or use pieces of it in new free 45 | programs; and that you know you can do these things. 46 | 47 | To protect your rights, we need to make restrictions that forbid 48 | anyone to deny you these rights or to ask you to surrender the rights. 49 | These restrictions translate to certain responsibilities for you if you 50 | distribute copies of the software, or if you modify it. 51 | 52 | For example, if you distribute copies of a such a program, whether 53 | gratis or for a fee, you must give the recipients all the rights that 54 | you have. You must make sure that they, too, receive or can get the 55 | source code. And you must tell them their rights. 56 | 57 | We protect your rights with two steps: (1) copyright the software, and 58 | (2) offer you this license which gives you legal permission to copy, 59 | distribute and/or modify the software. 60 | 61 | Also, for each author's protection and ours, we want to make certain 62 | that everyone understands that there is no warranty for this free 63 | software. If the software is modified by someone else and passed on, we 64 | want its recipients to know that what they have is not the original, so 65 | that any problems introduced by others will not reflect on the original 66 | authors' reputations. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | GNU GENERAL PUBLIC LICENSE 72 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 73 | 74 | 0. This License Agreement applies to any program or other work which 75 | contains a notice placed by the copyright holder saying it may be 76 | distributed under the terms of this General Public License. The 77 | "Program", below, refers to any such program or work, and a "work based 78 | on the Program" means either the Program or any work containing the 79 | Program or a portion of it, either verbatim or with modifications. Each 80 | licensee is addressed as "you". 81 | 82 | 1. You may copy and distribute verbatim copies of the Program's source 83 | code as you receive it, in any medium, provided that you conspicuously and 84 | appropriately publish on each copy an appropriate copyright notice and 85 | disclaimer of warranty; keep intact all the notices that refer to this 86 | General Public License and to the absence of any warranty; and give any 87 | other recipients of the Program a copy of this General Public License 88 | along with the Program. You may charge a fee for the physical act of 89 | transferring a copy. 90 | 91 | 2. You may modify your copy or copies of the Program or any portion of 92 | it, and copy and distribute such modifications under the terms of Paragraph 93 | 1 above, provided that you also do the following: 94 | 95 | a) cause the modified files to carry prominent notices stating that 96 | you changed the files and the date of any change; and 97 | 98 | b) cause the whole of any work that you distribute or publish, that 99 | in whole or in part contains the Program or any part thereof, either 100 | with or without modifications, to be licensed at no charge to all 101 | third parties under the terms of this General Public License (except 102 | that you may choose to grant warranty protection to some or all 103 | third parties, at your option). 104 | 105 | c) If the modified program normally reads commands interactively when 106 | run, you must cause it, when started running for such interactive use 107 | in the simplest and most usual way, to print or display an 108 | announcement including an appropriate copyright notice and a notice 109 | that there is no warranty (or else, saying that you provide a 110 | warranty) and that users may redistribute the program under these 111 | conditions, and telling the user how to view a copy of this General 112 | Public License. 113 | 114 | d) You may charge a fee for the physical act of transferring a 115 | copy, and you may at your option offer warranty protection in 116 | exchange for a fee. 117 | 118 | Mere aggregation of another independent work with the Program (or its 119 | derivative) on a volume of a storage or distribution medium does not bring 120 | the other work under the scope of these terms. 121 | 122 | 3. You may copy and distribute the Program (or a portion or derivative of 123 | it, under Paragraph 2) in object code or executable form under the terms of 124 | Paragraphs 1 and 2 above provided that you also do one of the following: 125 | 126 | a) accompany it with the complete corresponding machine-readable 127 | source code, which must be distributed under the terms of 128 | Paragraphs 1 and 2 above; or, 129 | 130 | b) accompany it with a written offer, valid for at least three 131 | years, to give any third party free (except for a nominal charge 132 | for the cost of distribution) a complete machine-readable copy of the 133 | corresponding source code, to be distributed under the terms of 134 | Paragraphs 1 and 2 above; or, 135 | 136 | c) accompany it with the information you received as to where the 137 | corresponding source code may be obtained. (This alternative is 138 | allowed only for noncommercial distribution and only if you 139 | received the program in object code or executable form alone.) 140 | 141 | Source code for a work means the preferred form of the work for making 142 | modifications to it. For an executable file, complete source code means 143 | all the source code for all modules it contains; but, as a special 144 | exception, it need not include source code for modules which are standard 145 | libraries that accompany the operating system on which the executable 146 | file runs, or for standard header files or definitions files that 147 | accompany that operating system. 148 | 149 | 4. You may not copy, modify, sublicense, distribute or transfer the 150 | Program except as expressly provided under this General Public License. 151 | Any attempt otherwise to copy, modify, sublicense, distribute or transfer 152 | the Program is void, and will automatically terminate your rights to use 153 | the Program under this License. However, parties who have received 154 | copies, or rights to use copies, from you under this General Public 155 | License will not have their licenses terminated so long as such parties 156 | remain in full compliance. 157 | 158 | 5. By copying, distributing or modifying the Program (or any work based 159 | on the Program) you indicate your acceptance of this license to do so, 160 | and all its terms and conditions. 161 | 162 | 6. Each time you redistribute the Program (or any work based on the 163 | Program), the recipient automatically receives a license from the original 164 | licensor to copy, distribute or modify the Program subject to these 165 | terms and conditions. You may not impose any further restrictions on the 166 | recipients' exercise of the rights granted herein. 167 | 168 | 7. The Free Software Foundation may publish revised and/or new versions 169 | of the General Public License from time to time. Such new versions will 170 | be similar in spirit to the present version, but may differ in detail to 171 | address new problems or concerns. 172 | 173 | Each version is given a distinguishing version number. If the Program 174 | specifies a version number of the license which applies to it and "any 175 | later version", you have the option of following the terms and conditions 176 | either of that version or of any later version published by the Free 177 | Software Foundation. If the Program does not specify a version number of 178 | the license, you may choose any version ever published by the Free Software 179 | Foundation. 180 | 181 | 8. If you wish to incorporate parts of the Program into other free 182 | programs whose distribution conditions are different, write to the author 183 | to ask for permission. For software which is copyrighted by the Free 184 | Software Foundation, write to the Free Software Foundation; we sometimes 185 | make exceptions for this. Our decision will be guided by the two goals 186 | of preserving the free status of all derivatives of our free software and 187 | of promoting the sharing and reuse of software generally. 188 | 189 | NO WARRANTY 190 | 191 | 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 192 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 193 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 194 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 195 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 196 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 197 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 198 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 199 | REPAIR OR CORRECTION. 200 | 201 | 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 202 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 203 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 204 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 205 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 206 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 207 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 208 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 209 | POSSIBILITY OF SUCH DAMAGES. 210 | 211 | END OF TERMS AND CONDITIONS 212 | 213 | Appendix: How to Apply These Terms to Your New Programs 214 | 215 | If you develop a new program, and you want it to be of the greatest 216 | possible use to humanity, the best way to achieve this is to make it 217 | free software which everyone can redistribute and change under these 218 | terms. 219 | 220 | To do so, attach the following notices to the program. It is safest to 221 | attach them to the start of each source file to most effectively convey 222 | the exclusion of warranty; and each file should have at least the 223 | "copyright" line and a pointer to where the full notice is found. 224 | 225 | 226 | Copyright (C) 19yy 227 | 228 | This program is free software; you can redistribute it and/or modify 229 | it under the terms of the GNU General Public License as published by 230 | the Free Software Foundation; either version 1, or (at your option) 231 | any later version. 232 | 233 | This program is distributed in the hope that it will be useful, 234 | but WITHOUT ANY WARRANTY; without even the implied warranty of 235 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 236 | GNU General Public License for more details. 237 | 238 | You should have received a copy of the GNU General Public License 239 | along with this program; if not, write to the Free Software 240 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA 241 | 242 | 243 | Also add information on how to contact you by electronic and paper mail. 244 | 245 | If the program is interactive, make it output a short notice like this 246 | when it starts in an interactive mode: 247 | 248 | Gnomovision version 69, Copyright (C) 19xx name of author 249 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 250 | This is free software, and you are welcome to redistribute it 251 | under certain conditions; type `show c' for details. 252 | 253 | The hypothetical commands `show w' and `show c' should show the 254 | appropriate parts of the General Public License. Of course, the 255 | commands you use may be called something other than `show w' and `show 256 | c'; they could even be mouse-clicks or menu items--whatever suits your 257 | program. 258 | 259 | You should also get your employer (if you work as a programmer) or your 260 | school, if any, to sign a "copyright disclaimer" for the program, if 261 | necessary. Here a sample; alter the names: 262 | 263 | Yoyodyne, Inc., hereby disclaims all copyright interest in the 264 | program `Gnomovision' (a program to direct compilers to make passes 265 | at assemblers) written by James Hacker. 266 | 267 | , 1 April 1989 268 | Ty Coon, President of Vice 269 | 270 | That's all there is to it! 271 | 272 | 273 | --- The Artistic License 1.0 --- 274 | 275 | This software is Copyright (c) 2011- by Tatsuhiko Miyagawa. 276 | 277 | This is free software, licensed under: 278 | 279 | The Artistic License 1.0 280 | 281 | The Artistic License 282 | 283 | Preamble 284 | 285 | The intent of this document is to state the conditions under which a Package 286 | may be copied, such that the Copyright Holder maintains some semblance of 287 | artistic control over the development of the package, while giving the users of 288 | the package the right to use and distribute the Package in a more-or-less 289 | customary fashion, plus the right to make reasonable modifications. 290 | 291 | Definitions: 292 | 293 | - "Package" refers to the collection of files distributed by the Copyright 294 | Holder, and derivatives of that collection of files created through 295 | textual modification. 296 | - "Standard Version" refers to such a Package if it has not been modified, 297 | or has been modified in accordance with the wishes of the Copyright 298 | Holder. 299 | - "Copyright Holder" is whoever is named in the copyright or copyrights for 300 | the package. 301 | - "You" is you, if you're thinking about copying or distributing this Package. 302 | - "Reasonable copying fee" is whatever you can justify on the basis of media 303 | cost, duplication charges, time of people involved, and so on. (You will 304 | not be required to justify it to the Copyright Holder, but only to the 305 | computing community at large as a market that must bear the fee.) 306 | - "Freely Available" means that no fee is charged for the item itself, though 307 | there may be fees involved in handling the item. It also means that 308 | recipients of the item may redistribute it under the same conditions they 309 | received it. 310 | 311 | 1. You may make and give away verbatim copies of the source form of the 312 | Standard Version of this Package without restriction, provided that you 313 | duplicate all of the original copyright notices and associated disclaimers. 314 | 315 | 2. You may apply bug fixes, portability fixes and other modifications derived 316 | from the Public Domain or from the Copyright Holder. A Package modified in such 317 | a way shall still be considered the Standard Version. 318 | 319 | 3. You may otherwise modify your copy of this Package in any way, provided that 320 | you insert a prominent notice in each changed file stating how and when you 321 | changed that file, and provided that you do at least ONE of the following: 322 | 323 | a) place your modifications in the Public Domain or otherwise make them 324 | Freely Available, such as by posting said modifications to Usenet or an 325 | equivalent medium, or placing the modifications on a major archive site 326 | such as ftp.uu.net, or by allowing the Copyright Holder to include your 327 | modifications in the Standard Version of the Package. 328 | 329 | b) use the modified Package only within your corporation or organization. 330 | 331 | c) rename any non-standard executables so the names do not conflict with 332 | standard executables, which must also be provided, and provide a separate 333 | manual page for each non-standard executable that clearly documents how it 334 | differs from the Standard Version. 335 | 336 | d) make other distribution arrangements with the Copyright Holder. 337 | 338 | 4. You may distribute the programs of this Package in object code or executable 339 | form, provided that you do at least ONE of the following: 340 | 341 | a) distribute a Standard Version of the executables and library files, 342 | together with instructions (in the manual page or equivalent) on where to 343 | get the Standard Version. 344 | 345 | b) accompany the distribution with the machine-readable source of the Package 346 | with your modifications. 347 | 348 | c) accompany any non-standard executables with their corresponding Standard 349 | Version executables, giving the non-standard executables non-standard 350 | names, and clearly documenting the differences in manual pages (or 351 | equivalent), together with instructions on where to get the Standard 352 | Version. 353 | 354 | d) make other distribution arrangements with the Copyright Holder. 355 | 356 | 5. You may charge a reasonable copying fee for any distribution of this 357 | Package. You may charge any fee you choose for support of this Package. You 358 | may not charge a fee for this Package itself. However, you may distribute this 359 | Package in aggregate with other (possibly commercial) programs as part of a 360 | larger (possibly commercial) software distribution provided that you do not 361 | advertise this Package as a product of your own. 362 | 363 | 6. The scripts and library files supplied as input to or produced as output 364 | from the programs of this Package do not automatically fall under the copyright 365 | of this Package, but belong to whomever generated them, and may be sold 366 | commercially, and may be aggregated with this Package. 367 | 368 | 7. C or perl subroutines supplied by you and linked into this Package shall not 369 | be considered part of this Package. 370 | 371 | 8. The name of the Copyright Holder may not be used to endorse or promote 372 | products derived from this software without specific prior written permission. 373 | 374 | 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED 375 | WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF 376 | MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 377 | 378 | The End 379 | 380 | --------------------------------------------------------------------------------