3 use warnings FATAL => 'all';
5 use ExtUtils::MakeMaker ();
9 our $VERSION = '0.002000';
10 $VERSION = eval $VERSION;
12 my $MM_VER = eval $ExtUtils::MakeMaker::VERSION;
15 author manifest_include readme_generator
20 warnings->import(FATAL => 'all');
21 shift->export_to_level(1,@_);
38 '' => qr{Changes|MANIFEST|README|LICENSE|META\.yml},
39 'maint' => qr{[^.].*},
42 sub manifest_include {
46 sub readme_generator {
47 die "readme_generator unsupported" if @_ && $_[0];
50 sub write_manifest_skip {
52 my @files = @Manifest;
54 while (my ($dir, $spec) = splice(@files, 0, 2)) {
55 my $re = ($dir ? $dir.'/' : '').
56 ((ref($spec) eq 'Regexp')
60 # print ref as well as stringification in case of overload ""
61 : die "spec must be string or regexp, was: ${spec} (${\ref $spec})");
64 my $dist_name = $mm->{DISTNAME};
65 my $include = join '|', map "${_}\$", @parts;
66 my $final = "^(?:\Q$dist_name\E-v?[0-9_.]+/|(?!$include))";
67 open my $skip, '>', 'MANIFEST.SKIP'
68 or die "can't open MANIFEST.SKIP: $!";
69 print $skip "${final}\n";
77 { map +($_ => _clone($struct->{$_})), keys %$struct };
79 elsif (ref eq 'ARRAY') {
91 @MM::ISA = (__PACKAGE__);
94 my ($class, $args) = @_;
95 my %test = %{$args->{test}||{}};
96 my $tests = $test{TESTS} || 't/*.t';
97 $tests !~ /\b\Q$_\E\b/ and $tests .= " $_"
98 for 'xt/*.t', 'xt/*/*.t';
99 $test{TESTS} = $tests;
100 return $class->SUPER::new({
102 MIN_PERL_VERSION => '5.006',
104 AUTHOR => ($MM_VER >= 6.5702 ? $Distar::Author : join(', ', @$Distar::Author)),
106 (exists $args->{ABSTRACT} ? () : (ABSTRACT_FROM => $args->{VERSION_FROM})),
109 realclean => { FILES => (
110 ($args->{realclean}{FILES}||'')
111 . ' Distar/ MANIFEST.SKIP MANIFEST MANIFEST.bak'
118 my $meta = { Distar::_clone($self->SUPER::metafile_data(@_) };
120 my $spec_ver = ($meta->{'meta-spec'} && $meta->{'meta-spec'}{version} || 1.4;
121 my $resources = $meta->{resources} ||= {};
124 my $no_index_dir = ${ $meta->{no_index}||={} }{directory} ||= [];
126 @$no_index_dir = grep !$seen{$_}++, @$no_index_dir, grep -d, qw(t xt);
129 $resources->{bugtracker} ||= do {
130 (my $queue = $meta->{name} || $self->{NAME}) =~ s/::/-/g;
131 my $rt_link = 'https://rt.cpan.org/Dist/Display.html?Name='.$queue;
132 $spec_ver < 2 ? $rt_link : {
134 mailto => "bug-$queue\@rt.cpan.org",
139 my $license = $meta->{license} = [
140 map { $_ eq 'perl' ? 'perl_5' : $_ }
141 map { ref ? @$_ : $_ }
142 ( $meta->{license} || $self->{LICENSE} || 'perl_5' )
145 $resources->{license} ||= [ 'http://dev.perl.org/licenses/' ]
146 if @$license == 1 && $license->[0] eq 'perl_5';
149 $meta->{x_contributors} ||= do {
153 map /(\<[^<>]+\@[^<>]\>)/,
156 grep !/$author_email/,
157 map { chomp; s/^\s*\d+\s*//; utf8::decode($_); $_ }
158 `git shortlog -s --email HEAD`
162 if ( eval { require CPAN::Meta; require Module::Metadata; } ) {
163 $meta->{provides} ||= do {
164 my $meta_obj = CPAN::Meta->new($meta, { lazy_validation => 1 });
165 my @files = `git ls-files`;
167 my $provides = Module::Metadata->package_versions_from_directory('.', \@files);
168 for my $module (keys %$provides) {
169 delete $provides->{$module}
170 unless $meta_obj->should_index_package($module)
171 && $meta_obj->should_index_file($provides->{$module}{file});
182 `git ls-files --error-unmatch MANIFEST.SKIP 2>&1`;
183 my $maniskip_tracked = !$?;
185 Distar::write_manifest_skip($self)
186 unless $maniskip_tracked;
188 my @results = @{$self->{RESULT}};
190 utf8::encode($_) for @results;
192 local $self->{RESULT} = \@results;
193 $self->SUPER::flush(@_);
196 sub special_targets {
198 my $targets = $self->SUPER::special_targets(@_);
199 my $phony_targets = join ' ', qw(
214 $targets =~ s/^(\.PHONY *:.*)/$1 $phony_targets/m;
220 my $pre_tar = $self->{TAR};
221 my $out = $self->SUPER::init_dist(@_);
223 my $tar = $self->{TAR};
226 my $version = `$tar --version`;
227 if ($version =~ /GNU tar/) {
230 elsif (!$pre_tar && `gtar --version`) {
234 my $tarflags = $self->{TARFLAGS};
235 if (my ($flags) = $tarflags =~ /^-?([cvhlLf]+)$/) {
236 if ($flags =~ s/c// && $flags =~ s/f//) {
237 $tarflags = '--format=ustar -c'.$flags.'f';
239 $tarflags = '--owner=0 --group=0 '.$tarflags;
251 $warn .= ($warn ? ' and ' : '').'gid('.(0+$)).')';
254 warn "$warn too large! Max is ".(2**21-1).".\n"
255 ."Dist creation will likely fail. Install GNU tar to work around.\n";
260 $self->{TARFLAGS} = $tarflags;
267 my $out = $self->SUPER::tarfile_target(@_);
268 my $verify = <<'END_FRAG';
269 $(ABSPERLRUN) $(HELPERS)/verify-tarball $(DISTVNAME).tar $(DISTVNAME)/MANIFEST --tar="$(TAR)"
271 $out =~ s{(\$\(TAR\).*\n)}{$1$verify};
279 if (open my $fh, '<', 'maint/Makefile.include') {
280 $include = "\n# --- Makefile.include:\n\n" . do { local $/; <$fh> };
281 $include =~ s/\n?\z/\n/;
285 grep { $include !~ /^bump$_(?: +\w+)*:/m } ('', 'minor', 'major');
287 my $distar = File::Spec->catdir(
288 File::Spec->catpath((File::Spec->splitpath(__FILE__))[0,1], ''),
291 my $helpers = File::Spec->catdir($distar, 'helpers');
294 DISTAR => $self->quote_literal($distar),
295 HELPERS => $self->quote_literal($helpers),
296 REMAKE => join(' ', '$(PERLRUN)', '-I$(DISTAR)/lib', '-mDistar', 'Makefile.PL', map { $self->quote_literal($_) } @ARGV),
297 BRANCH => $self->{BRANCH} ||= 'master',
298 CHANGELOG => $self->{CHANGELOG} ||= 'Changes',
299 DEV_NULL_STDOUT => ($self->{DEV_NULL} ? '>'.File::Spec->devnull : ''),
300 DISTTEST_MAKEFILE_PARAMS => '',
303 my $dist_test = $self->SUPER::dist_test(@_);
304 $dist_test =~ s/(\bMakefile\.PL\b)/$1 \$(DISTTEST_MAKEFILE_PARAMS)/;
308 "\n\n# --- Distar section:\n\n",
309 (map "$_ = $vars{$_}\n", sort keys %vars),
312 preflight: check-version check-manifest check-cpan-upload
313 $(ABSPERLRUN) $(HELPERS)/preflight $(VERSION) --changelog=$(CHANGELOG) --branch=$(BRANCH)
315 $(ABSPERLRUN) $(HELPERS)/check-version $(VERSION) $(TO_INST_PM) $(EXE_FILES)
317 $(ABSPERLRUN) $(HELPERS)/check-manifest
319 $(NOECHO) cpan-upload -h $(DEV_NULL_STDOUT)
321 $(MAKE) disttest RELEASE_TESTING=1 DISTTEST_MAKEFILE_PARAMS="PREREQ_FATAL=1" PASTHRU="$(PASTHRU) TEST_FILES=\"$(TEST_FILES)\""
324 git commit -a -m "Release commit for $(VERSION)"
325 git tag v$(VERSION) -m "release v$(VERSION)"
326 $(RM_RF) $(DISTVNAME)
327 $(MAKE) $(DISTVNAME).tar$(SUFFIX)
328 $(NOECHO) $(MAKE) pushrelease FAKE_RELEASE=$(FAKE_RELEASE)
331 pushrelease$(FAKE_RELEASE) ::
332 cpan-upload $(DISTVNAME).tar$(SUFFIX)
333 git push origin v$(VERSION) HEAD
335 readmefile: create_distdir
336 $(NOECHO) $(TEST_F) $(DISTVNAME)/README || $(MAKE) $(DISTVNAME)/README
337 $(DISTVNAME)/README: $(VERSION_FROM)
338 $(NOECHO) $(MKPATH) $(DISTVNAME)
339 pod2text $(VERSION_FROM) >$(DISTVNAME)/README
340 $(NOECHO) $(ABSPERLRUN) $(HELPERS)/add-to-manifest -d $(DISTVNAME) README
341 distsignature: readmefile
342 disttest: distmanicheck
343 distmanicheck: create_distdir
344 cd $(DISTVNAME) && $(ABSPERLRUN) "-MExtUtils::Manifest=manicheck" -e "exit manicheck"
346 $(ABSPERLRUN) $(HELPERS)/add-changelog-heading --git $(VERSION) $(CHANGELOG)
348 cd $(DISTAR) && git pull || $(TRUE)
349 $(RM_F) $(FIRST_MAKEFILE)
352 map(sprintf(<<'END', "bump$_", ($_ || '$(V)')), @bump_targets),
354 $(ABSPERLRUN) $(HELPERS)/bump-version --git $(VERSION) %s
355 $(RM_F) $(FIRST_MAKEFILE)
369 Distar - Additions to ExtUtils::MakeMaker for dist authors
375 use ExtUtils::MakeMaker;
376 (do './maint/Makefile.PL.include' or die $@) unless -f 'META.yml';
380 F<maint/Makefile.PL.include>:
382 BEGIN { -e 'Distar' or system("git clone git://git.shadowcat.co.uk/p5sagit/Distar.git") }
383 use lib 'Distar/lib';
386 author 'A. U. Thor <author@cpan.org>';
388 manifest_include t => 'test-helper.pl';
389 manifest_include corpus => '.txt';
394 $ make bump # bump version
395 $ make bump V=2.000000 # bump to specific version
396 $ make bumpminor # bump minor version component
397 $ make bumpmajor # bump major version component
398 $ make nextrelease # add version heading to Changes file
399 $ make releasetest # build dist and test (with xt/ and RELEASE_TESTING=1)
400 $ make preflight # check that repo and file state is release ready
401 $ make release # check releasetest and preflight, commits and tags,
402 # builds and uploads to CPAN, and pushes commits and
404 $ make release FAKE_RELEASE=1
405 # builds a release INCLUDING committing and tagging,
406 # but does not upload to cpan or push anything to git
410 L<ExtUtils::MakeMaker> works well enough as development tool for
411 builting and testing, but using it to release is annoying and error prone.
412 Distar adds just enough to L<ExtUtils::MakeMaker> for it to be a usable dist
413 author tool. This includes extra commands for releasing and safety checks, and
414 automatic generation of some files. It doesn't require any non-core modules and
415 is compatible with old versions of perl.
419 =head2 author( $author )
421 Set the author to include in generated META files. Can be a single entry, or
424 =head2 manifest_include( $dir, $pattern )
426 Add a pattern to include files in the MANIFEST file, and thus in the generated
429 The pattern can be either a regex, or a path suffix. It will be applied to the
430 full path past the directory specified.
432 The default files that are always included are: F<.pm> and F<.pod> files in
433 F<lib>, F<.t> files in F<t> and F<xt>, F<.pm> files in F<t/lib> and F<xt/lib>,
434 F<Changes>, F<MANIFEST>, F<README>, F<LICENSE>, F<META.yml>, and F<.PL> files in
435 the dist root, and all files in F<maint>.
437 =head1 AUTOGENERATED FILES
441 =item F<MANIFEST.SKIP>
443 The F<MANIFEST.SKIP> will be automatically generated to exclude any files not
444 explicitly allowed via C<manifest_include> or the included defaults. It will be
445 created (or updated) at C<perl Makefile.PL> time.
449 The F<README> file will be generated at dist generation time, inside the built
450 dist. It will be generated using C<pod2text> on the main module.
452 If a F<README> file exists in the repo, it will be used directly instead of
457 =head1 MAKE COMMMANDS
461 test will be adjusted to include F<xt/> tests by default. This will only apply
462 for authors, not users installing from CPAN.
466 Releases the dist. Before releasing, checks will be done on the dist using the
467 C<preflight> and C<releasetest> commands.
469 Releasing will generate a dist tarball and upload it to CPAN using cpan-upload.
470 It will also create a git tag for the release, and push the tag and branch.
474 If release is run with FAKE_RELEASE=1 set, it will skip uploading to CPAN and
475 pushing to git. A release commit will still be created and tagged locally.
479 Performs a number of checks on the files and repository, ensuring it is in a
480 sane state to do a release. The checks are:
484 =item * All version numbers match
486 =item * The F<MANIFEST> file is up to date
488 =item * The branch is correct
490 =item * There is no existing tag for the version
492 =item * There are no unmerged upstream changes
494 =item * There are no outstanding local changes
496 =item * There is an appropriate staged Changes heading
498 =item * cpan-upload is available
504 Test the dist preparing for a release. This generates a dist dir and runs the
505 tests from inside it. This ensures all appropriate files are included inside
506 the dist. C<RELEASE_TESTING> will be set in the environment.
510 Adds an appropriate changelog heading for the release, and prompts to stage the
515 Bumps the version number. This will try to preserve the length and format of
516 the version number. The least significant digit will be incremented. Versions
517 with underscores will preserve the underscore in the same position.
519 Optionally accepts a C<V> option to set the version to a specific value.
521 The version changes will automatically be committed. Unstaged modifications to
522 the files will be left untouched.
526 The V option will be passed along to the version bumping script. It can accept
527 a space separated list of options, including an explicit version number.
535 Updates version numbers even if they do not match the current expected version
540 Attempts to convert the updated version to a stable version, removing any
545 Attempts to convert the updated version to an alpha version, adding an
546 underscore in an appropriate place.
552 Like bump, but increments the minor segment of the version. This will treat
553 numeric versions as x.yyyzzz format, incrementing the yyy segment.
557 Like bumpminor, but bumping the major segment.
561 Updates Distar and re-runs C<perl Makefile.PL>
565 IRC: #web-simple on irc.perl.org
567 Git repository: L<git://git.shadowcat.co.uk/p5sagit/Distar>
569 Git browser: L<http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=p5sagit/Distar.git;a=summary>
573 mst - Matt S. Trout (cpan:MSTROUT) <mst@shadowcat.co.uk>
577 haarg - Graham Knop (cpan:HAARG) <haarg@cpan.org>
579 ether - Karen Etheridge (cpan:ETHER) <ether@cpan.org>
581 frew - Arthur Axel "fREW" Schmidt (cpan:FREW) <frioux@gmail.com>
583 Mithaldu - Christian Walde (cpan:MITHALDU) <walde.christian@googlemail.com>
587 Copyright (c) 2011-2015 the Distar L</AUTHOR> and L</CONTRIBUTORS>
592 This library is free software and may be distributed under the same terms
593 as perl itself. See L<http://dev.perl.org/licenses/>.