File Coverage

blib/lib/Dist/Zilla/Dist/Builder.pm
Criterion Covered Total %
statement 278 356 78.0
branch 58 138 42.0
condition 20 59 33.9
subroutine 39 44 88.6
pod 16 16 100.0
total 411 613 67.0


line stmt bran cond sub pod time code
1             package Dist::Zilla::Dist::Builder 6.037;
2             # ABSTRACT: dist zilla subclass for building dists
3              
4 50     50   42767 use Moose 0.92; # role composition fixes
  50         383930  
  50         414  
5             extends 'Dist::Zilla';
6              
7 50     50   363794 use Dist::Zilla::Pragmas;
  50         111  
  50         493  
8              
9 50     50   978 use MooseX::Types::Moose qw(HashRef);
  50         22607  
  50         688  
10 50     50   277609 use Dist::Zilla::Types qw(Path);
  50         130  
  50         693  
11              
12 50     50   124385 use File::pushd ();
  50         118  
  50         1538  
13 50     50   259 use Dist::Zilla::Path; # because more Path::* is better, eh?
  50         103  
  50         648  
14 50     50   16754 use Try::Tiny;
  50         134  
  50         5157  
15 50     50   372 use List::Util 1.45 'uniq';
  50         1077  
  50         4440  
16 50     50   403 use Module::Runtime 'require_module';
  50         99  
  50         450  
17              
18 50     50   3019 use namespace::autoclean;
  50         99  
  50         508  
19              
20             #pod =method from_config
21             #pod
22             #pod my $zilla = Dist::Zilla->from_config(\%arg);
23             #pod
24             #pod This routine returns a new Zilla from the configuration in the current working
25             #pod directory.
26             #pod
27             #pod This method should not be relied upon, yet. Its semantics are B<certain> to
28             #pod change.
29             #pod
30             #pod Valid arguments are:
31             #pod
32             #pod config_class - the class to use to read the config
33             #pod default: Dist::Zilla::MVP::Reader::Finder
34             #pod
35             #pod =cut
36              
37             sub from_config {
38 186     186 1 597 my ($class, $arg) = @_;
39 186   50     704 $arg ||= {};
40              
41 186   100     1358 my $root = path($arg->{dist_root} || '.');
42              
43             my $sequence = $class->_load_config({
44             root => $root,
45             chrome => $arg->{chrome},
46             config_class => $arg->{config_class},
47             _global_stashes => $arg->{_global_stashes},
48 186         11954 });
49              
50 176         1380 my $self = $sequence->section_named('_')->zilla;
51              
52 176         1614 $self->_setup_default_plugins;
53              
54 176         1519 return $self;
55             }
56              
57             sub _setup_default_plugins {
58 176     176   547 my ($self) = @_;
59 176 50       1320 unless ($self->plugin_named(':InstallModules')) {
60 176         28612 require Dist::Zilla::Plugin::FinderCode;
61             my $plugin = Dist::Zilla::Plugin::FinderCode->new({
62             plugin_name => ':InstallModules',
63             zilla => $self,
64             style => 'grep',
65             code => sub {
66 411     411   817 my ($file, $self) = @_;
67 411         1372 local $_ = $file->name;
68 411 100 66     2167 return 1 if m{\Alib/} and m{\.(pm|pod)$};
69 290         945 return;
70             },
71 176         10549 });
72              
73 176         562 push @{ $self->plugins }, $plugin;
  176         6486  
74             }
75              
76 176 50       932 unless ($self->plugin_named(':IncModules')) {
77 176         1217 require Dist::Zilla::Plugin::FinderCode;
78             my $plugin = Dist::Zilla::Plugin::FinderCode->new({
79             plugin_name => ':IncModules',
80             zilla => $self,
81             style => 'grep',
82             code => sub {
83 38     38   75 my ($file, $self) = @_;
84 38         116 local $_ = $file->name;
85 38 100 66     165 return 1 if m{\Ainc/} and m{\.pm$};
86 33         86 return;
87             },
88 176         8005 });
89              
90 176         562 push @{ $self->plugins }, $plugin;
  176         5650  
91             }
92              
93 176 50       739 unless ($self->plugin_named(':TestFiles')) {
94 176         997 require Dist::Zilla::Plugin::FinderCode;
95             my $plugin = Dist::Zilla::Plugin::FinderCode->new({
96             plugin_name => ':TestFiles',
97             zilla => $self,
98             style => 'grep',
99 164     164   662 code => sub { local $_ = $_->name; m{\At/} },
  164         2268  
100 176         7554 });
101              
102 176         574 push @{ $self->plugins }, $plugin;
  176         5671  
103             }
104              
105 176 50       710 unless ($self->plugin_named(':ExtraTestFiles')) {
106 176         948 require Dist::Zilla::Plugin::FinderCode;
107             my $plugin = Dist::Zilla::Plugin::FinderCode->new({
108             plugin_name => ':ExtraTestFiles',
109             zilla => $self,
110             style => 'grep',
111 164     164   630 code => sub { local $_ = $_->name; m{\Axt/} },
  164         683  
112 176         7775 });
113              
114 176         563 push @{ $self->plugins }, $plugin;
  176         5831  
115             }
116              
117 176 50       713 unless ($self->plugin_named(':ExecFiles')) {
118 176         994 require Dist::Zilla::Plugin::FinderCode;
119             my $plugin = Dist::Zilla::Plugin::FinderCode->new({
120             plugin_name => ':ExecFiles',
121             zilla => $self,
122             style => 'list',
123             code => sub {
124 98     98   3674 my $plugins = $_[0]->zilla->plugins_with(-ExecFiles);
125 98         566 my @files = uniq map {; @{ $_->find_files } } @$plugins;
  56         107  
  56         487  
126              
127 98         774 return \@files;
128             },
129 176         8059 });
130              
131 176         563 push @{ $self->plugins }, $plugin;
  176         5749  
132             }
133              
134 176 50       714 unless ($self->plugin_named(':PerlExecFiles')) {
135 176         959 require Dist::Zilla::Plugin::FinderCode;
136             my $plugin = Dist::Zilla::Plugin::FinderCode->new({
137             plugin_name => ':PerlExecFiles',
138             zilla => $self,
139             style => 'list',
140             code => sub {
141 1     1   7 my $parent_plugin = $self->plugin_named(':ExecFiles');
142             my @files = grep {
143 3 100       17 $_->name =~ m{\.pl$}
144             or $_->content =~ m{^\s*\#\!.*perl\b};
145 1         4 } @{ $parent_plugin->find_files };
  1         9  
146 1         9 return \@files;
147             },
148 176         8060 });
149              
150 176         559 push @{ $self->plugins }, $plugin;
  176         5587  
151             }
152              
153 176 50       708 unless ($self->plugin_named(':ShareFiles')) {
154 176         1329 require Dist::Zilla::Plugin::FinderCode;
155             my $plugin = Dist::Zilla::Plugin::FinderCode->new({
156             plugin_name => ':ShareFiles',
157             zilla => $self,
158             style => 'list',
159             code => sub {
160 1     1   4 my $self = shift;
161 1         46 my $map = $self->zilla->_share_dir_map;
162 1         3 my @files;
163 1 50       5 if ( $map->{dist} ) {
164 14         41 push @files, grep {; $_->name =~ m{\A\Q$map->{dist}\E/} }
165 1         4 @{ $self->zilla->files };
  1         43  
166             }
167 1 50       6 if ( my $mod_map = $map->{module} ) {
168 0         0 for my $mod ( keys %$mod_map ) {
169 0         0 push @files, grep { $_->name =~ m{\A\Q$mod_map->{$mod}\E/} }
170 0         0 @{ $self->zilla->files };
  0         0  
171             }
172             }
173 1         8 return \@files;
174             },
175 176         8777 });
176              
177 176         550 push @{ $self->plugins }, $plugin;
  176         5905  
178             }
179              
180 176 50       737 unless ($self->plugin_named(':MainModule')) {
181 176         1023 require Dist::Zilla::Plugin::FinderCode;
182             my $plugin = Dist::Zilla::Plugin::FinderCode->new({
183             plugin_name => ':MainModule',
184             zilla => $self,
185             style => 'grep',
186             code => sub {
187 0     0   0 my ($file, $self) = @_;
188 0         0 local $_ = $file->name;
189 0 0       0 return 1 if $_ eq $self->zilla->main_module->name;
190 0         0 return;
191             },
192 176         8211 });
193              
194 176         542 push @{ $self->plugins }, $plugin;
  176         5982  
195             }
196              
197 176 50       714 unless ($self->plugin_named(':AllFiles')) {
198 176         1060 require Dist::Zilla::Plugin::FinderCode;
199             my $plugin = Dist::Zilla::Plugin::FinderCode->new({
200             plugin_name => ':AllFiles',
201             zilla => $self,
202             style => 'grep',
203 18     18   61 code => sub { return 1 },
204 176         7670 });
205              
206 176         545 push @{ $self->plugins }, $plugin;
  176         5808  
207             }
208              
209 176 50       683 unless ($self->plugin_named(':NoFiles')) {
210 176         965 require Dist::Zilla::Plugin::FinderCode;
211             my $plugin = Dist::Zilla::Plugin::FinderCode->new({
212             plugin_name => ':NoFiles',
213             zilla => $self,
214             style => 'list',
215 1     1   11 code => sub { [] },
216 176         7634 });
217              
218 176         608 push @{ $self->plugins }, $plugin;
  176         5589  
219             }
220             }
221              
222             has _share_dir_map => (
223             is => 'ro',
224             isa => HashRef,
225             init_arg => undef,
226             lazy => 1,
227             builder => '_build_share_dir_map',
228             );
229              
230             sub _build_share_dir_map {
231 37     37   136 my ($self) = @_;
232              
233 37         116 my $share_dir_map = {};
234              
235 37         152 for my $plugin (@{ $self->plugins_with(-ShareDir) }) {
  37         209  
236 21 100       169 next unless my $sub_map = $plugin->share_dir_map;
237              
238 10 100       45 if ( $sub_map->{dist} ) {
239             $self->log_fatal("can't install more than one distribution ShareDir")
240 5 50       19 if $share_dir_map->{dist};
241 5         17 $share_dir_map->{dist} = $sub_map->{dist};
242             }
243              
244 10 100       49 if ( my $mod_map = $sub_map->{module} ) {
245 5         21 for my $mod ( keys %$mod_map ) {
246             $self->log_fatal("can't install more than one ShareDir for $mod")
247 8 50       31 if $share_dir_map->{module}{$mod};
248 8         41 $share_dir_map->{module}{$mod} = $mod_map->{$mod};
249             }
250             }
251             }
252              
253 37         1598 return $share_dir_map;
254             }
255              
256              
257             sub _load_config {
258 186     186   692 my ($class, $arg) = @_;
259 186   50     789 $arg ||= {};
260              
261             my $config_class =
262 186   50     1205 $arg->{config_class} ||= 'Dist::Zilla::MVP::Reader::Finder';
263              
264 186         1313 require_module($config_class);
265              
266             $arg->{chrome}->logger->log_debug(
267 186         14301 { prefix => '[DZ] ' },
268             "reading configuration using $config_class"
269             );
270              
271 186         5495 my $root = $arg->{root};
272              
273 186         27707 require Dist::Zilla::MVP::Assembler::Zilla;
274 186         27294 require Dist::Zilla::MVP::Section;
275             my $assembler = Dist::Zilla::MVP::Assembler::Zilla->new({
276             chrome => $arg->{chrome},
277 186         11561 zilla_class => $class,
278             section_class => 'Dist::Zilla::MVP::Section', # make this DZMA default
279             });
280              
281 186         6176 for ($assembler->sequence->section_named('_')) {
282 186         13664 $_->add_value(chrome => $arg->{chrome});
283 186         14696 $_->add_value(root => $arg->{root});
284             $_->add_value(_global_stashes => $arg->{_global_stashes})
285 186 100       12641 if $arg->{_global_stashes};
286             }
287              
288 186         1831 my $seq;
289             try {
290 186     186   10975 $seq = $config_class->read_config(
291             $root->child('dist'),
292             {
293             assembler => $assembler
294             },
295             );
296             } catch {
297             die $_ unless try {
298 10 100       599 $_->isa('Config::MVP::Error')
299             and $_->ident eq 'package not installed'
300 10 100   10   295 };
301              
302 4         106 my $package = $_->package;
303 4 100       26 my $bundle = $_->section_name =~ m{^@(?!.*/)} ? ' bundle' : '';
304              
305 4         240 die <<"END_DIE";
306             Required plugin$bundle $package isn't installed.
307              
308             Run 'dzil authordeps' to see a list of all required plugins.
309             You can pipe the list to your CPAN client to install or update them:
310              
311             dzil authordeps --missing | cpanm
312              
313             END_DIE
314              
315 186         3021 };
316              
317 176         13341 return $seq;
318             }
319              
320             #pod =method build_in
321             #pod
322             #pod $zilla->build_in($root);
323             #pod
324             #pod This method builds the distribution in the given directory. If no directory
325             #pod name is given, it defaults to DistName-Version. If the distribution has
326             #pod already been built, an exception will be thrown.
327             #pod
328             #pod =method build
329             #pod
330             #pod This method just calls C<build_in> with no arguments. It gets you the default
331             #pod behavior without the weird-looking formulation of C<build_in> with no object
332             #pod for the preposition!
333             #pod
334             #pod =cut
335              
336 130     130 1 11159 sub build { $_[0]->build_in }
337              
338             sub build_in {
339 151     151 1 470 my ($self, $root) = @_;
340              
341 151 50       2279 $self->log_fatal("tried to build with a minter")
342             if $self->isa('Dist::Zilla::Dist::Minter');
343              
344 151 50       8358 $self->log_fatal("attempted to build " . $self->name . " a second time")
345             if $self->built_in;
346              
347 151         394 $_->before_build for @{ $self->plugins_with(-BeforeBuild) };
  151         1187  
348              
349 151         6253 $self->log("beginning to build " . $self->name);
350              
351 151         65788 $_->gather_files for @{ $self->plugins_with(-FileGatherer) };
  151         833  
352 150         518 $_->set_file_encodings for @{ $self->plugins_with(-EncodingProvider) };
  150         887  
353 150         444 $_->prune_files for @{ $self->plugins_with(-FilePruner) };
  150         612  
354              
355 150         6388 $self->version; # instantiate this lazy attribute now that files are gathered
356              
357 150         339 $_->munge_files for @{ $self->plugins_with(-FileMunger) };
  150         680  
358              
359 148         2671 $_->register_prereqs for @{ $self->plugins_with(-PrereqSource) };
  148         713  
360              
361 148         5774 $self->prereqs->finalize;
362              
363             # Barf if someone has already set up a prereqs entry? -- rjbs, 2010-04-13
364 148         11025 $self->distmeta->{prereqs} = $self->prereqs->as_string_hash;
365              
366 147         357 $_->setup_installer for @{ $self->plugins_with(-InstallTool) };
  147         767  
367              
368 146         1224 $self->_check_dupe_files;
369              
370 146         1093 my $build_root = $self->_prep_build_root($root);
371              
372 146         6290 $self->log("writing " . $self->name . " in $build_root");
373              
374 146         63253 for my $file (@{ $self->files }) {
  146         6391  
375 643         21682 $self->_write_out_file($file, $build_root);
376             }
377              
378             $_->after_build({ build_root => $build_root })
379 146         5826 for @{ $self->plugins_with(-AfterBuild) };
  146         895  
380              
381 146         7965 $self->built_in($build_root);
382             }
383              
384             #pod =attr built_in
385             #pod
386             #pod This is the L<Path::Tiny>, if any, in which the dist has been built.
387             #pod
388             #pod =cut
389              
390             has built_in => (
391             is => 'rw',
392             isa => Path,
393             init_arg => undef,
394             coerce => 1,
395             );
396              
397             #pod =method ensure_built_in
398             #pod
399             #pod $zilla->ensure_built_in($root);
400             #pod
401             #pod This method behaves like C<L</build_in>>, but if the dist is already built in
402             #pod C<$root> (or the default root, if no root is given), no exception is raised.
403             #pod
404             #pod =method ensure_built
405             #pod
406             #pod This method just calls C<ensure_built_in> with no arguments. It gets you the
407             #pod default behavior without the weird-looking formulation of C<ensure_built_in>
408             #pod with no object for the preposition!
409             #pod
410             #pod =cut
411              
412             sub ensure_built {
413 23     23 1 181 $_[0]->ensure_built_in;
414             }
415              
416             sub ensure_built_in {
417 24     24 1 73 my ($self, $root) = @_;
418              
419             # $root ||= $self->name . q{-} . $self->version;
420 24 50 33     1284 return $self->built_in if $self->built_in and
      66        
421             (!$root or ($self->built_in eq $root));
422              
423 21 50       749 Carp::croak("dist is already built, but not in $root") if $self->built_in;
424 21         121 $self->build_in($root);
425             }
426              
427             #pod =method dist_basename
428             #pod
429             #pod my $basename = $zilla->dist_basename;
430             #pod
431             #pod This method will return the dist's basename (e.g. C<Dist-Name-1.01>.
432             #pod The basename is used as the top-level directory in the tarball. It
433             #pod does not include C<-TRIAL>, even if building a trial dist.
434             #pod
435             #pod =cut
436              
437             sub dist_basename {
438 57     57 1 2159 my ($self) = @_;
439 57         2075 return join(q{},
440             $self->name,
441             '-',
442             $self->version,
443             );
444             }
445              
446             #pod =method archive_basename
447             #pod
448             #pod my $basename = $zilla->archive_basename;
449             #pod
450             #pod This method will return the filename, without the format extension
451             #pod (e.g. C<Dist-Name-1.01> or C<Dist-Name-1.01-TRIAL>).
452             #pod
453             #pod =cut
454              
455             sub archive_basename {
456 31     31 1 77 my ($self) = @_;
457 31 100 100     278 return join q{},
458             $self->dist_basename,
459             ( $self->is_trial && $self->version !~ /_/ ? '-TRIAL' : '' ),
460             ;
461             }
462              
463             #pod =method archive_filename
464             #pod
465             #pod my $tarball = $zilla->archive_filename;
466             #pod
467             #pod This method will return the filename (e.g. C<Dist-Name-1.01.tar.gz>)
468             #pod of the tarball of this distribution. It will include C<-TRIAL> if building a
469             #pod trial distribution, unless the version contains an underscore. The tarball
470             #pod might not exist.
471             #pod
472             #pod =cut
473              
474             sub archive_filename {
475 30     30 1 92 my ($self) = @_;
476 30         145 return join q{}, $self->archive_basename, '.tar.gz';
477             }
478              
479             #pod =method build_archive
480             #pod
481             #pod $zilla->build_archive;
482             #pod
483             #pod This method will ensure that the dist has been built, and will then build a
484             #pod tarball of the build directory in the current directory.
485             #pod
486             #pod =cut
487              
488             sub build_archive {
489 23     23 1 205 my ($self) = @_;
490              
491 23         140 my $built_in = $self->ensure_built;
492              
493 23         1067 my $basedir = path($self->dist_basename);
494              
495 23         1164 $_->before_archive for $self->plugins_with(-BeforeArchive)->@*;
496              
497 23         106 for my $builder ($self->plugins_with(-ArchiveBuilder)->@*) {
498 1         14 my $file = $builder->build_archive($self->archive_basename, $built_in, $basedir);
499 1 50       1160 return $file if defined $file;
500             }
501              
502 22         304 my $archive = $self->_build_archive($built_in, $basedir);
503              
504 22         186 my $file = path($self->archive_filename);
505              
506 22         1087 $self->log("writing archive to $file");
507 22         9534 $archive->write("$file", 9);
508              
509 22         175001 return $file;
510             }
511              
512             sub _build_archive {
513 22     22   78 my ($self, $built_in, $basedir) = @_;
514              
515 22         5021 require Archive::Tar;
516 22         545758 my $archive = Archive::Tar->new;
517 22         322 my %seen_dir;
518 22         70 for my $distfile (
519 106         461 sort { length($a->name) <=> length($b->name) } @{ $self->files }
  22         859  
520             ) {
521 82         30158 my $in = path($distfile->name)->parent;
522              
523 82 100       9527 unless ($seen_dir{ $in }++) {
524 64         726 $archive->add_data(
525             $basedir->child($in),
526             '',
527             { type => Archive::Tar::Constant::DIR(), mode => 0755 },
528             )
529             }
530              
531 82         19892 my $filename = $built_in->child( $distfile->name );
532 82         3701 $archive->add_data(
533             $basedir->child( $distfile->name ),
534             path($filename)->slurp_raw,
535             { mode => (stat $filename)[2] & ~022 },
536             );
537             }
538              
539 22         10928 return $archive;
540             }
541              
542             sub _prep_build_root {
543 146     146   450 my ($self, $build_root) = @_;
544              
545 146   66     1805 $build_root = path($build_root || $self->dist_basename);
546              
547 146 100       9641 $build_root->mkpath unless -d $build_root;
548              
549 146         11823 my $dist_root = $self->root;
550              
551 146 50       685 return $build_root if !-d $build_root;
552              
553 146         3158 my $ok = eval { $build_root->remove_tree({ safe => 0 }); 1 };
  146         1714  
  146         70278  
554 146 50       670 die "unable to delete '$build_root' in preparation of build: $@" unless $ok;
555              
556             # the following is done only on windows, and only if the deletion failed,
557             # yet rmtree reported success, because currently rmdir is non-blocking as per:
558             # https://rt.perl.org/Ticket/Display.html?id=123958
559 146 50 33     942 if ( $^O eq 'MSWin32' and -d $build_root ) {
560 0         0 $self->log("spinning for at least one second to allow other processes to release locks on $build_root");
561 0         0 my $timeout = time + 2;
562 0   0     0 while(time != $timeout and -d $build_root) { }
563 0 0       0 die "unable to delete '$build_root' in preparation of build because some process has a lock on it"
564             if -d $build_root;
565             }
566              
567 146         560 return $build_root;
568             }
569              
570             #pod =method release
571             #pod
572             #pod $zilla->release;
573             #pod
574             #pod This method releases the distribution, probably by uploading it to the CPAN.
575             #pod The actual effects of this method (as with most of the methods) is determined
576             #pod by the loaded plugins.
577             #pod
578             #pod =cut
579              
580             sub release {
581 21     21 1 72 my $self = shift;
582              
583             Carp::croak("you can't release without any Releaser plugins")
584 21 50       59 unless my @releasers = @{ $self->plugins_with(-Releaser) };
  21         191  
585              
586 21         269 $ENV{DZIL_RELEASING} = 1;
587              
588 21         160 my $tgz = $self->build_archive;
589              
590             # call all plugins implementing BeforeRelease role
591 21         66 $_->before_release($tgz) for @{ $self->plugins_with(-BeforeRelease) };
  21         147  
592              
593             # do the actual release
594 13         9174 $_->release($tgz) for @releasers;
595              
596             # call all plugins implementing AfterRelease role
597 11         3952 $_->after_release($tgz) for @{ $self->plugins_with(-AfterRelease) };
  11         113  
598             }
599              
600             #pod =method clean
601             #pod
602             #pod This method removes temporary files and directories suspected to have been
603             #pod produced by the Dist::Zilla build process. Specifically, it deletes the
604             #pod F<.build> directory and any entity that starts with the dist name and a hyphen,
605             #pod like matching the glob C<Your-Dist-*>.
606             #pod
607             #pod =cut
608              
609             sub clean {
610 0     0 1 0 my ($self, $dry_run) = @_;
611              
612 0         0 require File::Path;
613 0         0 for my $x (grep { -e } '.build', glob($self->name . '-*')) {
  0         0  
614 0 0       0 if ($dry_run) {
615 0         0 $self->log("clean: would remove $x");
616             } else {
617 0         0 $self->log("clean: removing $x");
618 0         0 File::Path::rmtree($x);
619             }
620             };
621             }
622              
623             #pod =method ensure_built_in_tmpdir
624             #pod
625             #pod $zilla->ensure_built_in_tmpdir;
626             #pod
627             #pod This method will consistently build the distribution in a temporary
628             #pod subdirectory. It will return the path for the temporary build location.
629             #pod
630             #pod =cut
631              
632             sub ensure_built_in_tmpdir {
633 1     1 1 2 my $self = shift;
634              
635 1         6 require File::Temp;
636              
637 1         4 my $build_root = path('.build');
638 1 50       47 $build_root->mkpath unless -d $build_root;
639              
640 1         282 my $target = path( File::Temp::tempdir(DIR => $build_root) );
641 1         35 $self->log("building distribution under $target for installation");
642              
643 1         299 my $os_has_symlinks = eval { symlink("",""); 1 };
  1         10  
  1         2  
644 1         2 my $previous;
645             my $latest;
646              
647 1 50       4 if( $os_has_symlinks ) {
648 1         4 $previous = path( $build_root, 'previous' );
649 1         41 $latest = path( $build_root, 'latest' );
650 1 50       27 if( -l $previous ) {
651 0 0       0 $previous->remove
652             or $self->log("cannot remove old .build/previous link");
653             }
654 1 50       57 if( -l $latest ) {
655 0 0       0 rename $latest, $previous
656             or $self->log("cannot move .build/latest link to .build/previous");
657             }
658 1 50       20 symlink $target->basename, $latest
659             or $self->log('cannot create link .build/latest');
660             }
661              
662 1         142 $self->ensure_built_in($target);
663              
664 1         58 return ($target, $latest, $previous);
665             }
666              
667             #pod =method install
668             #pod
669             #pod $zilla->install( \%arg );
670             #pod
671             #pod This method installs the distribution locally. The distribution will be built
672             #pod in a temporary subdirectory, then the process will change directory to that
673             #pod subdir and an installer will be run.
674             #pod
675             #pod Valid arguments are:
676             #pod
677             #pod keep_build_dir - if true, don't rmtree the build dir, even if everything
678             #pod seemed to work
679             #pod install_command - the command to run in the subdir to install the dist
680             #pod default (roughly): $^X -MCPAN -einstall .
681             #pod
682             #pod this argument should be an arrayref
683             #pod
684             #pod =cut
685              
686             sub install {
687 0     0 1 0 my ($self, $arg) = @_;
688 0   0     0 $arg ||= {};
689              
690 0         0 my ($target, $latest) = $self->ensure_built_in_tmpdir;
691              
692 0         0 my $ok = eval {
693             ## no critic Punctuation
694 0         0 my $wd = File::pushd::pushd($target);
695             my @cmd = $arg->{install_command}
696 0 0       0 ? @{ $arg->{install_command} }
  0         0  
697             : (cpanm => ".");
698              
699 0         0 $self->log_debug([ 'installing via %s', \@cmd ]);
700 0 0       0 system(@cmd) && $self->log_fatal([ "error running %s", \@cmd ]);
701 0         0 1;
702             };
703              
704 0 0       0 unless ($ok) {
705 0   0     0 my $error = $@ || '(exception clobered)';
706 0         0 $self->log("install failed, left failed dist in place at $target");
707 0         0 die $error;
708             }
709              
710 0 0       0 if ($arg->{keep_build_dir}) {
711 0         0 $self->log("all's well; left dist in place at $target");
712             } else {
713 0         0 $self->log("all's well; removing $target");
714 0         0 $target->remove_tree({ safe => 0 });
715 0 0       0 $latest->remove_tree({ safe => 0 }) if -d $latest; # error cannot unlink, is a directory
716 0 0       0 $latest->remove if $latest;
717             }
718              
719 0         0 return;
720             }
721              
722             #pod =method test
723             #pod
724             #pod $zilla->test(\%arg);
725             #pod
726             #pod This method builds a new copy of the distribution and tests it using
727             #pod C<L</run_tests_in>>.
728             #pod
729             #pod C<\%arg> may be omitted. Otherwise, valid arguments are:
730             #pod
731             #pod keep_build_dir - if true, don't rmtree the build dir, even if everything
732             #pod seemed to work
733             #pod
734             #pod =cut
735              
736             sub test {
737 1     1 1 3 my ($self, $arg) = @_;
738              
739             Carp::croak("you can't test without any TestRunner plugins")
740 1 50       2 unless my @testers = @{ $self->plugins_with(-TestRunner) };
  1         5  
741              
742 1         8 my ($target, $latest) = $self->ensure_built_in_tmpdir;
743 1         22 my $error = $self->run_tests_in($target, $arg);
744              
745 1 0 33     141 if ($arg and $arg->{keep_build_dir}) {
746 0         0 $self->log("all's well; left dist in place at $target");
747 0         0 return;
748             }
749              
750 1         31 $self->log("all's well; removing $target");
751 1         988 $target->remove_tree({ safe => 0 });
752 1 50 33     7359 $latest->remove_tree({ safe => 0 }) if $latest && -d $latest; # error cannot unlink, is a directory
753 1 50       138 $latest->remove if $latest;
754             }
755              
756             #pod =method run_tests_in
757             #pod
758             #pod my $error = $zilla->run_tests_in($directory, $arg);
759             #pod
760             #pod This method runs the tests in $directory (a Path::Tiny), which must contain an
761             #pod already-built copy of the distribution. It will throw an exception if there
762             #pod are test failures.
763             #pod
764             #pod It does I<not> set any of the C<*_TESTING> environment variables, nor
765             #pod does it clean up C<$directory> afterwards.
766             #pod
767             #pod =cut
768              
769             sub run_tests_in {
770 2     2 1 14 my ($self, $target, $arg) = @_;
771              
772             Carp::croak("you can't test without any TestRunner plugins")
773 2 50       8 unless my @testers = @{ $self->plugins_with(-TestRunner) };
  2         24  
774              
775 2         15 for my $tester (@testers) {
776 2         17 my $wd = File::pushd::pushd($target);
777 2         504 $tester->test( $target, $arg );
778             }
779             }
780              
781             #pod =method run_in_build
782             #pod
783             #pod $zilla->run_in_build( \@cmd );
784             #pod
785             #pod This method makes a temporary directory, builds the distribution there,
786             #pod executes all the dist's L<BuildRunner|Dist::Zilla::Role::BuildRunner>s
787             #pod (unless directed not to, via C<< $arg->{build} = 0 >>), and
788             #pod then runs the given command in the build directory. If the command exits
789             #pod non-zero, the directory will be left in place.
790             #pod
791             #pod =cut
792              
793             sub run_in_build {
794 0     0 1   my ($self, $cmd, $arg) = @_;
795              
796             $self->log_fatal("you can't build without any BuildRunner plugins")
797             unless ($arg and exists $arg->{build} and ! $arg->{build})
798 0 0 0       or @{ $self->plugins_with(-BuildRunner) };
  0   0        
      0        
799              
800 0           require "Config.pm"; # skip autoprereq
801              
802 0           my ($target, $latest) = $self->ensure_built_in_tmpdir;
803 0           my $abstarget = $target->absolute;
804              
805             # building the dist for real
806 0           my $ok = eval {
807 0           my $wd = File::pushd::pushd($target);
808              
809 0 0 0       if ($arg and exists $arg->{build} and ! $arg->{build}) {
      0        
810 0 0         system(@$cmd) and die "error while running: @$cmd";
811 0           return 1;
812             }
813              
814 0           $self->_ensure_blib;
815              
816             local $ENV{PERL5LIB} = join $Config::Config{path_sep},
817 0           (map { $abstarget->child('blib', $_) } qw(arch lib)),
818 0 0         (defined $ENV{PERL5LIB} ? $ENV{PERL5LIB} : ());
819              
820             local $ENV{PATH} = join $Config::Config{path_sep},
821 0           (map { $abstarget->child('blib', $_) } qw(bin script)),
822 0 0         (defined $ENV{PATH} ? $ENV{PATH} : ());
823              
824 0 0         system(@$cmd) and die "error while running: @$cmd";
825 0           1;
826             };
827              
828 0 0         if ($ok) {
829 0           $self->log("all's well; removing $target");
830 0           $target->remove_tree({ safe => 0 });
831 0 0         $latest->remove_tree({ safe => 0 }) if -d $latest; # error cannot unlink, is a directory
832 0 0         $latest->remove if $latest;
833             } else {
834 0   0       my $error = $@ || '(unknown error)';
835 0           $self->log($error);
836 0           $self->log_fatal("left failed dist in place at $target");
837             }
838             }
839              
840             # Ensures that a F<blib> directory exists in the build, by invoking all
841             # C<-BuildRunner> plugins to generate it. Useful for commands that operate on
842             # F<blib>, such as C<test> or C<run>.
843              
844             sub _ensure_blib {
845 0     0     my ($self) = @_;
846              
847 0 0         unless ( -d 'blib' ) {
848 0           my @builders = @{ $self->plugins_with( -BuildRunner ) };
  0            
849 0 0         $self->log_fatal("no BuildRunner plugins specified") unless @builders;
850 0           $_->build for @builders;
851 0 0         $self->log_fatal("no blib; failed to build properly?") unless -d 'blib';
852             }
853             }
854              
855             __PACKAGE__->meta->make_immutable;
856             1;
857              
858             __END__
859              
860             =pod
861              
862             =encoding UTF-8
863              
864             =head1 NAME
865              
866             Dist::Zilla::Dist::Builder - dist zilla subclass for building dists
867              
868             =head1 VERSION
869              
870             version 6.037
871              
872             =head1 PERL VERSION
873              
874             This module should work on any version of perl still receiving updates from
875             the Perl 5 Porters. This means it should work on any version of perl
876             released in the last two to three years. (That is, if the most recently
877             released version is v5.40, then this module should work on both v5.40 and
878             v5.38.)
879              
880             Although it may work on older versions of perl, no guarantee is made that the
881             minimum required version will not be increased. The version may be increased
882             for any reason, and there is no promise that patches will be accepted to
883             lower the minimum required perl.
884              
885             =head1 ATTRIBUTES
886              
887             =head2 built_in
888              
889             This is the L<Path::Tiny>, if any, in which the dist has been built.
890              
891             =head1 METHODS
892              
893             =head2 from_config
894              
895             my $zilla = Dist::Zilla->from_config(\%arg);
896              
897             This routine returns a new Zilla from the configuration in the current working
898             directory.
899              
900             This method should not be relied upon, yet. Its semantics are B<certain> to
901             change.
902              
903             Valid arguments are:
904              
905             config_class - the class to use to read the config
906             default: Dist::Zilla::MVP::Reader::Finder
907              
908             =head2 build_in
909              
910             $zilla->build_in($root);
911              
912             This method builds the distribution in the given directory. If no directory
913             name is given, it defaults to DistName-Version. If the distribution has
914             already been built, an exception will be thrown.
915              
916             =head2 build
917              
918             This method just calls C<build_in> with no arguments. It gets you the default
919             behavior without the weird-looking formulation of C<build_in> with no object
920             for the preposition!
921              
922             =head2 ensure_built_in
923              
924             $zilla->ensure_built_in($root);
925              
926             This method behaves like C<L</build_in>>, but if the dist is already built in
927             C<$root> (or the default root, if no root is given), no exception is raised.
928              
929             =head2 ensure_built
930              
931             This method just calls C<ensure_built_in> with no arguments. It gets you the
932             default behavior without the weird-looking formulation of C<ensure_built_in>
933             with no object for the preposition!
934              
935             =head2 dist_basename
936              
937             my $basename = $zilla->dist_basename;
938              
939             This method will return the dist's basename (e.g. C<Dist-Name-1.01>.
940             The basename is used as the top-level directory in the tarball. It
941             does not include C<-TRIAL>, even if building a trial dist.
942              
943             =head2 archive_basename
944              
945             my $basename = $zilla->archive_basename;
946              
947             This method will return the filename, without the format extension
948             (e.g. C<Dist-Name-1.01> or C<Dist-Name-1.01-TRIAL>).
949              
950             =head2 archive_filename
951              
952             my $tarball = $zilla->archive_filename;
953              
954             This method will return the filename (e.g. C<Dist-Name-1.01.tar.gz>)
955             of the tarball of this distribution. It will include C<-TRIAL> if building a
956             trial distribution, unless the version contains an underscore. The tarball
957             might not exist.
958              
959             =head2 build_archive
960              
961             $zilla->build_archive;
962              
963             This method will ensure that the dist has been built, and will then build a
964             tarball of the build directory in the current directory.
965              
966             =head2 release
967              
968             $zilla->release;
969              
970             This method releases the distribution, probably by uploading it to the CPAN.
971             The actual effects of this method (as with most of the methods) is determined
972             by the loaded plugins.
973              
974             =head2 clean
975              
976             This method removes temporary files and directories suspected to have been
977             produced by the Dist::Zilla build process. Specifically, it deletes the
978             F<.build> directory and any entity that starts with the dist name and a hyphen,
979             like matching the glob C<Your-Dist-*>.
980              
981             =head2 ensure_built_in_tmpdir
982              
983             $zilla->ensure_built_in_tmpdir;
984              
985             This method will consistently build the distribution in a temporary
986             subdirectory. It will return the path for the temporary build location.
987              
988             =head2 install
989              
990             $zilla->install( \%arg );
991              
992             This method installs the distribution locally. The distribution will be built
993             in a temporary subdirectory, then the process will change directory to that
994             subdir and an installer will be run.
995              
996             Valid arguments are:
997              
998             keep_build_dir - if true, don't rmtree the build dir, even if everything
999             seemed to work
1000             install_command - the command to run in the subdir to install the dist
1001             default (roughly): $^X -MCPAN -einstall .
1002              
1003             this argument should be an arrayref
1004              
1005             =head2 test
1006              
1007             $zilla->test(\%arg);
1008              
1009             This method builds a new copy of the distribution and tests it using
1010             C<L</run_tests_in>>.
1011              
1012             C<\%arg> may be omitted. Otherwise, valid arguments are:
1013              
1014             keep_build_dir - if true, don't rmtree the build dir, even if everything
1015             seemed to work
1016              
1017             =head2 run_tests_in
1018              
1019             my $error = $zilla->run_tests_in($directory, $arg);
1020              
1021             This method runs the tests in $directory (a Path::Tiny), which must contain an
1022             already-built copy of the distribution. It will throw an exception if there
1023             are test failures.
1024              
1025             It does I<not> set any of the C<*_TESTING> environment variables, nor
1026             does it clean up C<$directory> afterwards.
1027              
1028             =head2 run_in_build
1029              
1030             $zilla->run_in_build( \@cmd );
1031              
1032             This method makes a temporary directory, builds the distribution there,
1033             executes all the dist's L<BuildRunner|Dist::Zilla::Role::BuildRunner>s
1034             (unless directed not to, via C<< $arg->{build} = 0 >>), and
1035             then runs the given command in the build directory. If the command exits
1036             non-zero, the directory will be left in place.
1037              
1038             =head1 AUTHOR
1039              
1040             Ricardo SIGNES 😏 <cpan@semiotic.systems>
1041              
1042             =head1 COPYRIGHT AND LICENSE
1043              
1044             This software is copyright (c) 2026 by Ricardo SIGNES.
1045              
1046             This is free software; you can redistribute it and/or modify it under
1047             the same terms as the Perl 5 programming language system itself.
1048              
1049             =cut