File Coverage

blib/lib/Carmel/App.pm
Criterion Covered Total %
statement 63 390 16.1
branch 0 94 0.0
condition 0 25 0.0
subroutine 21 82 25.6
pod 0 42 0.0
total 84 633 13.2


line stmt bran cond sub pod time code
1             package Carmel::App;
2 1     1   455 use strict;
  1         2  
  1         24  
3 1     1   4 use warnings;
  1         2  
  1         24  
4              
5 1     1   5 use Carmel;
  1         1  
  1         15  
6 1     1   319 use Carmel::Runner;
  1         2  
  1         26  
7 1     1   7 use Carp ();
  1         2  
  1         14  
8 1     1   369 use Carmel::Builder;
  1         4  
  1         34  
9 1     1   377 use Carmel::CPANfile;
  1         3  
  1         32  
10 1     1   416 use Carmel::Environment;
  1         2  
  1         31  
11 1     1   7 use Carmel::Repository;
  1         2  
  1         15  
12 1     1   463 use Carmel::Resolver;
  1         4  
  1         40  
13 1     1   379 use Carmel::ProgressBar qw(progress);
  1         2  
  1         47  
14 1     1   7 use Config qw(%Config);
  1         1  
  1         29  
15 1     1   6 use CPAN::Meta::Requirements;
  1         1  
  1         19  
16 1     1   4 use File::pushd qw(pushd);
  1         2  
  1         41  
17 1     1   672 use Getopt::Long ();
  1         8640  
  1         33  
18 1     1   6 use Module::CPANfile;
  1         2  
  1         30  
19 1     1   5 use Module::Metadata;
  1         1  
  1         19  
20 1     1   5 use Path::Tiny ();
  1         1  
  1         11  
21 1     1   439 use Pod::Usage ();
  1         38950  
  1         22  
22 1     1   6 use Try::Tiny;
  1         2  
  1         73  
23              
24             # prefer Parse::CPAN::Meta in XS, PP order with JSON.pm
25             $ENV{PERL_JSON_BACKEND} = 1
26             unless defined $ENV{PERL_JSON_BACKEND};
27              
28             use Class::Tiny {
29 0           verbose => sub { 0 },
30 1     1   6 };
  1         2  
  1         8  
31              
32             sub parse_options {
33 0     0 0   my($self, $args) = @_;
34              
35 0 0 0       return if $args->[0] && $args->[0] eq 'exec';
36              
37 0           my $cmd;
38 0           my $parser = Getopt::Long::Parser->new(
39             config => [ "no_ignore_case", "pass_through" ],
40             );
41             $parser->getoptionsfromarray(
42             $args,
43 0     0     "h|help" => sub { $cmd = 'help' },
44 0     0     "version" => sub { $cmd = 'version' },
45 0     0     "v|verbose!" => sub { $Carmel::DEBUG = $self->verbose($_[1]) },
46 0           );
47              
48 0 0         unshift @$args, $cmd if $cmd;
49             }
50              
51             sub run {
52 0     0 0   my($self, @args) = @_;
53              
54 0           $self->parse_options(\@args);
55              
56 0   0       my $cmd = shift @args || 'install';
57 0 0         my $call = $self->can("cmd_$cmd")
58             or die "Can't find command '$cmd': run `carmel help` to see the list of commands.\n";
59              
60 0 0         if ($cmd eq 'run') {
61 0           return $self->cmd_run(@args);
62             }
63              
64 0           my $code = 0;
65             try {
66 0     0     $self->$call(@args);
67             } catch {
68 0     0     warn $_;
69 0           $code = 1;
70 0           };
71              
72 0           return $code;
73             }
74              
75             sub env {
76 0     0 0   my $self = shift;
77 0   0       $self->{env} ||= Carmel::Environment->new;
78             }
79              
80             sub cmd_help {
81 0     0 0   my $self = shift;
82 0           print "Carmel version $Carmel::VERSION\n\n";
83 0           Pod::Usage::pod2usage(1);
84             }
85              
86             sub cmd_version {
87 0     0 0   my $self = shift;
88 0           print "Carmel version $Carmel::VERSION\n";
89             }
90              
91             sub cmd_inject {
92 0     0 0   my($self, @args) = @_;
93              
94 0           my $reqs = CPAN::Meta::Requirements->new;
95 0           for my $arg (@args) {
96 0           my($module, $version) = split /@/, $arg, 2;
97 0 0         $reqs->add_string_requirement($module, $version ? "== $version" : 0);
98             }
99              
100 0           my $cpanfile = Module::CPANfile->from_prereqs({
101             runtime => {
102             requires => $reqs->as_string_hash,
103             },
104             });
105              
106             # FIXME: $builder->install() reads mirror info from cpanfile_path
107 0           my $path = Path::Tiny->tempfile;
108 0           $cpanfile->save($path);
109              
110 0           my @artifacts = $self->builder(cpanfile => $cpanfile, cpanfile_path => $path)->install;
111              
112 0           my @failed;
113             MODULE:
114 0           for my $module ($reqs->required_modules) {
115 0           my $want = $reqs->requirements_for_module($module);
116 0           for my $artifact (@artifacts) {
117 0 0         if ($artifact->provides->{$module}) {
118 0           my $version = $artifact->version_for($module);
119 0 0         $reqs->accepts_module($module => $version)
120             or die "Installed version for $module ($version) doesn't satisfy the requirement: $want\n";
121 0           next MODULE;
122             }
123             }
124              
125 0           push @failed, $module;
126             }
127              
128 0 0         if (@failed) {
129 0           die "Couldn't install module(s): ", join(", ", @failed), "\n";
130             }
131             }
132              
133             sub cmd_pin {
134 0     0 0   my($self, @args) = @_;
135 0           die "carmel pin is deprecated. Use `carmel update @args` instead\n";
136             }
137              
138             sub cmd_update {
139 0     0 0   my($self, @args) = @_;
140              
141 0 0         my $snapshot = $self->env->snapshot
142             or die "Can't run carmel update without snapshot. Run `carmel install` first.\n";
143              
144 0           print "---> Checking updates...\n";
145              
146 0           $self->update_or_install($snapshot, @args);
147             }
148              
149             sub update_or_install {
150 0     0 0   my($self, $snapshot, @args) = @_;
151              
152 0           my $builder = $self->builder;
153 0           my $requirements = $self->requirements;
154              
155             my $check = sub {
156 0     0     my($module, $pathname, $in_args, $version) = @_;
157              
158 0 0         return if $module eq 'perl';
159              
160 0           my $dist = $builder->search_module($module, $version);
161 0 0         unless ($dist) {
162 0 0         if ($version) {
163 0           die "Can't find $module ($version) on CPAN\n";
164             } else {
165             # workaround bad main package e.g. LWP => libwww::perl
166 0           warn "Can't find $module on CPAN\n";
167 0           return;
168             }
169             }
170              
171             # non-dual core module like "strict.pm"
172             # TODO should be $dist->is_perl
173 0 0         return if $dist->name =~ /^perl-5\.\d+\.\d+$/;
174              
175 0 0         if (defined $version) {
176             try {
177 0           $requirements->add_string_requirement($module, $version);
178             } catch {
179 0           my($err) = /illegal requirements(?: .*?): (.*) at/;
180 0           my $old = $requirements->requirements_for_module($module);
181 0           die "Requested version for $module '$version' conflicts with version required in cpanfile '$old': $err\n";
182 0           };
183             } else {
184 0           my $want_ver = $dist->version_for($module);
185             try {
186 0           $requirements->add_string_requirement($module, $want_ver);
187             } catch {
188             # there's an update but it conflicts with specs in cpanfile, ignoring
189 0 0         if ($in_args) {
190 0           my($err) = /illegal requirements(?: .*?): (.*) at/;
191 0           my $old = $requirements->requirements_for_module($module);
192 0           die "The update for $module '$want_ver' conflicts with version required in cpanfile '$old' $err\n";
193             }
194 0           };
195             }
196 0           };
197              
198 0 0         if (@args) {
199 0           for my $arg (@args) {
200 0           my($module, $version) = split '@', $arg, 2;
201 0 0         my $dist = $snapshot ? $snapshot->find($module) : undef;
202 0 0         if ($dist) {
    0          
203 0 0         $check->($module, $dist->pathname, 1, $version ? "== $version" : undef);
204             } elsif (defined $requirements->requirements_for_module($module)) {
205 0 0         $check->($module, '', 1, $version ? "== $version" : undef);
206             } else {
207 0           die "$module is not found in cpanfile or cpanfile.snapshot\n";
208             }
209             }
210             } else {
211 0           my $missing = $requirements->clone;
212              
213 0           my @checks;
214             my $resolver = $self->resolver(
215             root => $self->requirements->clone,
216             snapshot => $snapshot,
217             found => sub {
218 0     0     my $artifact = shift;
219 0           for my $pkg (keys %{$artifact->provides}) {
  0            
220 0           $missing->clear_requirement($pkg);
221             }
222 0           push @checks, [ $artifact->package, $artifact->install->{pathname}, 0 ];
223             },
224             missing => sub {
225 0     0     my($module, $want_version) = @_;
226 0           $missing->add_string_requirement($module => $want_version);
227             },
228 0           );
229 0           $resolver->resolve;
230              
231             # snapshot not supplied (first carmel install), or
232             # specified in cpanfile but not in snapshot, possibly core module
233 0           for my $module ($missing->required_modules) {
234 0           push @checks, [ $module, '', 0 ];
235             }
236              
237 0     0     progress \@checks, sub { $check->(@{$_[0]}) };
  0            
  0            
238             }
239              
240             # rebuild the snapshot
241 0           $self->update_dependencies($requirements, $snapshot);
242             }
243              
244             sub cmd_install {
245 0     0 0   my($self, @args) = @_;
246              
247 0 0         die "Usage: carmel install\n" if @args;
248              
249 0           my $snapshot = $self->env->snapshot;
250 0 0         if ($snapshot) {
251 0           $self->update_dependencies($self->requirements, $snapshot);
252             } else {
253 0           print "---> Installing modules...\n";
254 0           $self->update_or_install($snapshot);
255             }
256             }
257              
258             sub update_dependencies {
259 0     0 0   my($self, $root_reqs, $snapshot) = @_;
260              
261 0           my @artifacts = $self->install($root_reqs, $snapshot);
262 0           $self->dump_bootstrap(\@artifacts);
263 0           $self->save_snapshot(\@artifacts);
264             }
265              
266             sub resolve_dependencies {
267 0     0 0   my($self, $root_reqs, $missing, $snapshot) = @_;
268              
269 0           my @artifacts;
270             $self->resolver(
271             root => $root_reqs,
272             snapshot => $snapshot,
273             found => sub {
274 0     0     my $artifact = shift;
275 0   0       printf "Using %s (%s)\n", $artifact->package, $artifact->version || '0';
276 0           push @artifacts, $artifact;
277             },
278             missing => sub {
279 0     0     my($module, $want_version) = @_;
280 0           $missing->add_string_requirement($module => $want_version);
281             },
282 0           )->resolve;
283              
284 0           return @artifacts;
285             }
286              
287             sub is_identical_requirement {
288 0     0 0   my($self, $old, $new) = @_;
289              
290 0 0         return unless $old;
291              
292             # not super accurate but enough
293 0           join(',', sort $old->required_modules) eq join(',', sort $new->required_modules);
294             }
295              
296             sub try_install {
297 0     0 0   my($self, $root_reqs, $snapshot) = @_;
298              
299 0           my $prev;
300 0           while (1) {
301 0           my $missing = CPAN::Meta::Requirements->new;
302 0           my @artifacts = $self->resolve_dependencies($root_reqs, $missing, $snapshot);
303              
304 0 0         if (!$missing->required_modules) {
305 0           return @artifacts;
306             }
307              
308 0 0         if ($self->is_identical_requirement($prev, $missing)) {
309 0           my $prereqs = $missing->as_string_hash;
310 0   0       my $requirements = join ", ", map "$_ => @{[ $prereqs->{$_} || '0' ]}", keys %$prereqs;
  0            
311 0           die "Can't find an artifact for $requirements\n" .
312             "You need to run `carmel install` first to get the modules installed and artifacts built.\n";
313             }
314              
315 0           $prev = $missing;
316              
317 0           my $cpanfile = Module::CPANfile->from_prereqs({
318             runtime => {
319             requires => $missing->as_string_hash,
320             },
321             });
322 0           print "---> Installing new dependencies: ", join(", ", $missing->required_modules), "\n";
323 0           my $builder = $self->builder(cpanfile => $cpanfile, snapshot => $snapshot);
324 0           $builder->install;
325             }
326             }
327              
328             sub install {
329 0     0 0   my($self, $root_reqs, $snapshot) = @_;
330              
331 0           my @artifacts = $self->try_install($root_reqs, $snapshot);
332              
333             # $root_reqs has been mutated at this point. Reload requirements
334             printf "---> Complete! %d cpanfile dependencies. %d modules installed.\n",
335 0           scalar(grep { $_ ne 'perl' } $self->requirements->required_modules), scalar(@artifacts);
  0            
336              
337 0           return @artifacts;
338             }
339              
340             sub cmd_reinstall {
341 0     0 0   my($self, @args) = @_;
342              
343 0 0         my @modules = @args ? @args : $self->requirements->required_modules;
344              
345 0 0         my $snapshot = $self->env->snapshot
346             or die "Can't run carmel reinstall without snapshot. Run `carmel install` first.\n";
347              
348 0           my $reqs = CPAN::Meta::Requirements->new;
349 0           for my $module (@modules) {
350 0 0         if (my $dist = $snapshot->find($module)) {
    0          
351 0           $reqs->add_string_requirement($module, $dist->version_for($module));
352             } elsif (@args) {
353 0           die "$module is not found in cpanfile.snapshot\n";
354             }
355             }
356              
357 0           my $cpanfile = Module::CPANfile->from_prereqs({
358             runtime => {
359             requires => $reqs->as_string_hash,
360             },
361             });
362              
363 0           $self->builder(cpanfile => $cpanfile, snapshot => $snapshot)->install;
364 0           $self->cmd_install;
365             }
366              
367             sub builder {
368 0     0 0   my($self, @args) = @_;
369              
370             Carmel::Builder->new(
371             repository_base => $self->env->repository_base,
372             cpanfile_path => $self->env->cpanfile->path,
373 0     0     collect_artifact => sub { $self->env->repo->import_artifact(@_) },
374 0           @args,
375             );
376             }
377              
378             sub quote {
379 0     0 0   my $indent = shift;
380 0           $indent = " " x $indent;
381              
382 0           require Data::Dumper;
383 0           my $val = Data::Dumper->new([transform(@_)], [])
384             ->Sortkeys(1)->Terse(1)->Indent(1)->Dump;
385              
386 0           chomp $val;
387 0 0         $val =~ s/^/$indent/mg if $indent;
388 0           $val =~ s/^ *//;
389 0           $val;
390             }
391              
392             sub transform {
393 0     0 0   my $data = shift;
394              
395             # stringify elements
396 0 0         if (ref $data eq 'ARRAY') {
    0          
397 0           [map transform($_), @$data];
398             } elsif (ref $data eq 'HASH') {
399 0           my %value = map { $_ => transform($data->{$_}) } keys %$data;
  0            
400 0           \%value;
401             } else {
402 0           "$data";
403             }
404             }
405              
406             sub save_snapshot {
407 0     0 0   my($self, $artifacts) = @_;
408              
409 0           require Carton::Snapshot;
410 0           require Carton::Dist;
411              
412 0           my $snapshot = Carton::Snapshot->new(path => $self->env->snapshot_path);
413              
414 0           for my $artifact (@$artifacts) {
415             my $dist = Carton::Dist->new(
416             name => $artifact->distname,
417             pathname => $artifact->install->{pathname},
418 0           provides => $artifact->provides,
419             version => $artifact->version,
420             # compatibility with Carton snapshot
421             requirements => $artifact->requirements_for([qw( configure build runtime )], ['requires']),
422             );
423 0           $snapshot->add_distribution($dist);
424             }
425              
426 0           $snapshot->save;
427             }
428              
429             sub dump_bootstrap {
430 0     0 0   my($self, $artifacts) = @_;
431              
432 0           my @inc = map $_->nonempty_libs, @$artifacts;
433 0           my @path = map $_->nonempty_paths, @$artifacts;
434              
435 0           my(%execs);
436 0           for my $artifact (@$artifacts) {
437 0           my %bins = $artifact->executables;
438 0 0         $execs{$artifact->package} = \%bins if %bins;
439             }
440              
441 0           my %modules;
442 0           for my $artifact (@$artifacts) {
443 0           %modules = (%modules, $artifact->module_files);
444             }
445              
446 0           my $prereqs = $self->env->cpanfile->load->prereqs->as_string_hash;
447 0           my $package = "Carmel::MySetup"; # hide from PAUSE
448              
449 0           my $file = Path::Tiny->new(".carmel/MySetup.pm");
450 0           $file->parent->mkpath;
451 0           $file->spew(<
452             # DO NOT EDIT! Auto-generated via carmel install.
453             package $package;
454              
455             our %environment = (
456 0           'inc' => @{[ quote 2, \@inc ]},
457 0           'path' => @{[ quote 2, \@path ]},
458 0           'execs' => @{[ quote 2, \%execs ]},
459 0           'base' => @{[ quote(2, Path::Tiny->cwd) ]},
460 0           'modules' => @{[ quote 2, \%modules ]},
461 0           'prereqs' => @{[ quote 2, $prereqs ]},
462             );
463              
464             1;
465             EOF
466             }
467              
468             sub cmd_export {
469 0     0 0   my($self) = @_;
470 0           my %env = Carmel::Runner->new->env;
471 0           print "export ", join(" ", map qq($_="$env{$_}"), sort keys %env), "\n";
472             }
473              
474             sub cmd_env {
475 0     0 0   my($self) = @_;
476 0           my %env = Carmel::Runner->new->env;
477 0           print join "", map qq($_=$env{$_}\n), sort keys %env;
478             }
479              
480             sub cmd_run {
481 0     0 0   my($self, @args) = @_;
482 0           Carmel::Runner->new->run(@args);
483             }
484              
485             # Usually carmel exec is handled in carmel script, not here
486             sub cmd_exec {
487 0     0 0   my($self, @args) = @_;
488 0           Carmel::Runner->new->execute(@args);
489             }
490              
491             sub cmd_find {
492 0     0 0   my($self, $module, $requirement) = @_;
493              
494 0   0       my @artifacts = $self->env->repo->find_all($module, $requirement || '0');
495 0           for my $artifact (@artifacts) {
496 0   0       printf "%s (%s) in %s\n", $artifact->package, $artifact->version || '0', $artifact->path;
497             }
498             }
499              
500             sub cmd_show {
501 0     0 0   my($self, $module) = @_;
502              
503 0 0         $module or die "Usage: carmel show Module\n";
504              
505 0           my $artifact = $self->artifact_for($module);
506 0 0 0       printf "%s (%s) in %s\n", $artifact->package, $artifact->version || '0', $artifact->path
507             if $artifact;
508             }
509              
510             sub cmd_info {
511 0     0 0   my $self = shift;
512 0           $self->cmd_show(@_);
513             }
514              
515             sub cmd_list {
516 0     0 0   my $self = shift;
517              
518 0           my @artifacts;
519 0     0     $self->resolve(sub { push @artifacts, $_[0] });
  0            
520              
521 0           for my $artifact (sort { $a->package cmp $b->package } @artifacts) {
  0            
522 0   0       printf "%s (%s)\n", $artifact->package, $artifact->version || '0';
523             }
524             }
525              
526             sub cmd_look {
527 0     0 0   my($self, $module) = @_;
528              
529 0 0         $module or die "Usage: carmel look Module\n";
530              
531             my $shell = $ENV{SHELL}
532 0 0         or die "Can't determine shell from SHELL variable\n";
533              
534 0           my $artifact = $self->artifact_for($module);
535              
536 0           my $dir = pushd $artifact->path;
537 0           system $shell;
538             }
539              
540             sub cmd_diff {
541 0     0 0   my $self = shift;
542              
543 0           my $snapshot_path = $self->env->snapshot_path->relative;
544              
545             # Don't check if .git exists, and let git(2) handle the error
546              
547 0 0         if ($ENV{PERL_CARMEL_USE_DIFFTOOL}) {
548 0           my $cmd = 'carmel difftool';
549 0 0         $cmd .= ' -v' if $self->verbose;
550              
551 0           system 'git', 'difftool', '--no-prompt',
552             '--extcmd', $cmd, $snapshot_path;
553             } else {
554 0           require Carmel::Difftool;
555              
556 0 0         my $content = `git show HEAD:$snapshot_path`
557             or die "Can't retrieve snapshot content (not in git repository?)\n";
558 0           my $path = Path::Tiny->tempfile;
559 0           $path->spew($content);
560              
561 0           my $diff = Carmel::Difftool->new;
562 0           $diff->diff($path, $snapshot_path);
563             }
564             }
565              
566             sub cmd_difftool {
567 0     0 0   my($self, @args) = @_;
568              
569 0           require Carmel::Difftool;
570              
571 0           my $diff = Carmel::Difftool->new;
572 0           $diff->diff(@args);
573             }
574              
575             sub resolve {
576 0     0 0   my($self, $cb) = @_;
577 0           $self->resolver(found => $cb)->resolve;
578             }
579              
580             sub resolver {
581 0     0 0   my($self, @args) = @_;
582              
583             Carmel::Resolver->new(
584             repo => $self->env->repo,
585             root => $self->requirements,
586             snapshot => scalar $self->env->snapshot,
587 0     0     missing => sub { $self->missing_default(@_) },
588 0           @args,
589             );
590             }
591              
592             sub missing_default {
593 0     0 0   my($self, $module, $want_version, $depth) = @_;
594 0           die "Can't find an artifact for $module => $want_version\n" .
595             "You need to run `carmel install` first to get the modules installed and artifacts built.\n";
596             }
597              
598             sub artifact_for {
599 0     0 0   my($self, $module) = @_;
600              
601 0           my $found;
602 0           eval {
603             $self->resolve(sub {
604 0     0     my $artifact = shift;
605 0 0         if (exists $artifact->provides->{$module}) {
606 0           $found = $artifact;
607 0           die "__FOUND__\n";
608             }
609 0           });
610 0           die "Can't find a module named '$module' in the cpanfile dependencies.\n";
611             };
612              
613 0 0 0       die $@ if $@ && $@ ne "__FOUND__\n";
614 0           return $found;
615             }
616              
617             sub cmd_tree {
618 0     0 0   my($self) = @_;
619              
620             $self->resolve(sub {
621 0     0     my($artifact, $depth) = @_;
622 0   0       printf "%s%s (%s)\n", (" " x $depth), $artifact->package, $artifact->version || '0';
623 0           });
624             }
625              
626             sub cmd_rollout {
627 0     0 0   my $self = shift;
628              
629 0           my @artifacts;
630 0     0     $self->resolve(sub { push @artifacts, $_[0] });
  0            
631              
632             # TODO safe atomic rename
633 0           my $install_base = Path::Tiny->new("local")->absolute;
634 0 0         $install_base->remove_tree({ safe => 0 }) if $install_base->exists;
635              
636 0           $self->builder->rollout($install_base, \@artifacts);
637              
638 0           $install_base->child(".carmel")->touch;
639             }
640              
641             sub cmd_package {
642 0     0 0   my $self = shift;
643              
644 0           my $index = $self->build_index;
645              
646 0           my $source_base = $self->env->repository_base->child('cache');
647 0           my $target_base = Path::Tiny->new('vendor/cache');
648              
649 0           my %done;
650             my @found;
651 0           for my $package ($index->packages) {
652 0 0         next if $done{$package->pathname}++;
653              
654 0           my $source = $source_base->child('authors/id', $package->pathname);
655 0           my $target = $target_base->child('authors/id', $package->pathname);
656              
657 0 0         if ($source->exists) {
658             push @found, sub {
659 0     0     print "Copying ", $package->pathname, "\n";
660 0           $target->parent->mkpath;
661 0           $source->copy($target);
662 0           };
663             } else {
664 0           die sprintf "%s not found in %s.\n" .
665             "Run `carmel install` to fix this. If that didn't resolve the issue, try removing %s\n",
666             $package->pathname, $source_base, $self->env->repository_base;
667             }
668             }
669              
670 0           for my $copy (@found) {
671 0           $copy->();
672             }
673              
674 0           require IO::Compress::Gzip;
675 0           my $index_file = $target_base->child('modules/02packages.details.txt.gz');
676 0           $index_file->parent->mkpath;
677              
678 0           warn "Writing $index_file\n";
679 0 0         my $out = IO::Compress::Gzip->new($index_file->openw)
680             or die "gzip failed: $IO::Compress::Gzip::GzipError";
681 0           $index->write($out);
682              
683 0           print "---> Complete! ", scalar(@found), " distributions are packaged in vendor/cache\n";
684             }
685              
686             sub cmd_index {
687 0     0 0   my $self = shift;
688 0           $self->build_index->write(*STDOUT);
689             }
690              
691             sub build_index {
692 0     0 0   my $self = shift;
693              
694 0           require Carton::Index;
695 0           require Carton::Package;
696              
697 0           my $index = Carton::Index->new(generator => "Carmel $Carmel::VERSION");
698              
699             $self->resolve(sub {
700 0     0     my $artifact = shift;
701 0           while (my($pkg, $data) = each %{$artifact->provides}) {
  0            
702 0           my $package = Carton::Package->new($pkg, $data->{version}, $artifact->install->{pathname});
703 0           $index->add_package($package);
704             }
705 0           });
706              
707 0           $index;
708             }
709              
710             sub requirements {
711 0     0 0   my $self = shift;
712              
713 0           return $self->env->cpanfile->load->prereqs
714             ->merged_requirements(['runtime', 'test', 'develop'], ['requires']);
715             }
716              
717             1;