3 use warnings FATAL => 'all';
5 use ExtUtils::MakeMaker ();
10 our $VERSION = '0.003000';
11 $VERSION = eval $VERSION;
13 my $MM_VER = eval $ExtUtils::MakeMaker::VERSION;
16 author manifest_include readme_generator
21 warnings->import(FATAL => 'all');
22 if (!(@MM::ISA == 1 && $MM::ISA[0] eq 'Distar::MM')) {
23 @Distar::MM::ISA = @MM::ISA;
24 @MM::ISA = qw(Distar::MM);
26 goto &Exporter::import;
43 '' => qr{Changes|MANIFEST|README|LICENSE|META\.yml},
44 'maint' => qr{[^.].*},
47 sub manifest_include {
51 sub readme_generator {
52 die "readme_generator unsupported" if @_ && $_[0];
55 sub write_manifest_skip {
57 my @files = @Manifest;
59 while (my ($dir, $spec) = splice(@files, 0, 2)) {
60 my $re = ($dir ? $dir.'/' : '').
61 ((ref($spec) eq 'Regexp')
65 # print ref as well as stringification in case of overload ""
66 : die "spec must be string or regexp, was: ${spec} (${\ref $spec})");
69 my $dist_name = $mm->{DISTNAME};
70 my $include = join '|', map "${_}\$", @parts;
71 my $final = "^(?:\Q$dist_name\E-v?[0-9_.]+/|(?!$include))";
72 open my $skip, '>', 'MANIFEST.SKIP'
73 or die "can't open MANIFEST.SKIP: $!";
74 print $skip "${final}\n";
82 my ($class, $args) = @_;
83 my %test = %{$args->{test}||{}};
84 my $tests = $test{TESTS} || 't/*.t';
85 $tests !~ /\b\Q$_\E\b/ and $tests .= " $_"
86 for 'xt/*.t', 'xt/*/*.t';
87 $test{TESTS} = $tests;
88 return $class->SUPER::new({
90 MIN_PERL_VERSION => '5.006',
92 AUTHOR => ($MM_VER >= 6.5702 ? $Distar::Author : join(', ', @$Distar::Author)),
94 (exists $args->{ABSTRACT} ? () : (ABSTRACT_FROM => $args->{VERSION_FROM})),
97 realclean => { FILES => (
98 ($args->{realclean}{FILES}||'')
99 . ' Distar/ MANIFEST.SKIP MANIFEST MANIFEST.bak'
106 `git ls-files --error-unmatch MANIFEST.SKIP 2>&1`;
107 my $maniskip_tracked = !$?;
109 Distar::write_manifest_skip($self)
110 unless $maniskip_tracked;
111 $self->SUPER::flush(@_);
114 sub special_targets {
116 my $targets = $self->SUPER::special_targets(@_);
117 my $phony_targets = join ' ', qw(
132 $targets =~ s/^(\.PHONY *:.*)/$1 $phony_targets/m;
138 my $pre_tar = $self->{TAR};
139 my $out = $self->SUPER::init_dist(@_);
141 my $dn = File::Spec->devnull;
142 my $tar = $self->{TAR};
145 for my $maybe_tar ($tar, qw(gtar gnutar)) {
146 my $version = `$maybe_tar --version 2>$dn`;
147 if ($version =~ /GNU tar/) {
153 my $tarflags = $self->{TARFLAGS};
154 if (my ($flags) = $tarflags =~ /^-?([cvhlLf]+)$/) {
155 if ($flags =~ s/c// && $flags =~ s/f//) {
156 $tarflags = '--format=ustar -c'.$flags.'f';
158 $tarflags = '--owner=0 --group=0 '.$tarflags;
170 $warn .= ($warn ? ' and ' : '').'gid('.(0+$)).')';
173 warn "Current $warn too large to create portable dist archives! Max is ".(2**21-1).".\n"
174 ."Dist creation will most likely fail. Install GNU tar and re-run Makefile.PL to fix this issue.\n";
176 my $brew = `which brew 2>$dn`;
179 push @try, 'brew install gnu-tar';
181 my $ports = `which ports 2>$dn`;
184 push @try, 'sudo ports install gnutar';
187 warn "Try" . (@try > 1 ? ' one of' : '') . ":\n"
188 . join '', map " $_\n", @try;
194 $self->{TARFLAGS} = $tarflags;
201 my $out = $self->SUPER::tarfile_target(@_);
202 my $verify = <<'END_FRAG';
203 $(ABSPERLRUN) $(HELPERS)/verify-tarball $(DISTVNAME).tar $(DISTVNAME)/MANIFEST --tar="$(TAR)"
205 $out =~ s{(\$\(TAR\).*\n)}{$1$verify};
212 my $distar_lib = File::Basename::dirname(__FILE__);
213 my $helpers = File::Spec->catdir($distar_lib, File::Spec->updir, 'helpers');
215 my $licenses = $self->{LICENSE} || $self->{META_ADD}{license} || $self->{META_MERGE}{license};
216 my $authors = $self->{AUTHOR};
217 $_ = ref $_ ? $_ : [$_ || ()]
218 for $licenses, $authors;
221 DISTAR_LIB => $self->quote_literal($distar_lib),
222 HELPERS => $self->quote_literal($helpers),
223 REMAKE => join(' ', '$(PERLRUN)', '-I$(DISTAR_LIB)', '-MDistar', 'Makefile.PL', map { $self->quote_literal($_) } @ARGV),
224 BRANCH => $self->{BRANCH} ||= 'master',
225 CHANGELOG => $self->{CHANGELOG} ||= 'Changes',
226 DEV_NULL_STDOUT => ($self->{DEV_NULL} ? '>'.File::Spec->devnull : ''),
227 DISTTEST_MAKEFILE_PARAMS => '',
228 AUTHORS => $self->quote_literal(join(', ', @$authors)),
229 LICENSES => join(' ', map $self->quote_literal($_), @$licenses),
230 GET_CHANGELOG => '$(ABSPERLRUN) $(HELPERS)/get-changelog $(VERSION) $(CHANGELOG)',
232 -e File::Spec->catdir($distar_lib, File::Spec->updir, '.git')
233 ? 'git -C $(DISTAR_LIB) pull'
234 : '$(ECHO) "Distar code is not in a git repo, unable to update!"'
238 my $dist_test = $self->SUPER::dist_test(@_);
239 $dist_test =~ s/(\bMakefile\.PL\b)/$1 \$(DISTTEST_MAKEFILE_PARAMS)/;
242 if (open my $fh, '<', 'maint/Makefile.include') {
243 $include = do { local $/; <$fh> };
244 $include =~ s/\n?\z/\n/;
250 preflight: check-version check-manifest check-cpan-upload
251 $(ABSPERLRUN) $(HELPERS)/preflight $(VERSION) --changelog=$(CHANGELOG) --branch=$(BRANCH)
253 $(ABSPERLRUN) $(HELPERS)/check-version $(VERSION) $(TO_INST_PM) $(EXE_FILES)
255 $(ABSPERLRUN) $(HELPERS)/check-manifest
257 $(NOECHO) cpan-upload -h $(DEV_NULL_STDOUT)
259 $(MAKE) disttest RELEASE_TESTING=1 DISTTEST_MAKEFILE_PARAMS="PREREQ_FATAL=1" PASTHRU="$(PASTHRU) TEST_FILES=\"$(TEST_FILES)\""
260 $(NOECHO) $(TEST_F) $(DISTVNAME)/LICENSE || $(ECHO) "Failed to generate $(DISTVNAME)/LICENSE!" >&2
261 $(NOECHO) $(TEST_F) $(DISTVNAME)/LICENSE
264 $(GET_CHANGELOG) -p"Release commit for $(VERSION)" | git commit -a -F -
265 $(GET_CHANGELOG) -p"release v$(VERSION)" | git tag -a -F - "v$(VERSION)"
266 $(RM_RF) $(DISTVNAME)
267 $(MAKE) $(DISTVNAME).tar$(SUFFIX)
268 $(NOECHO) $(MAKE) pushrelease FAKE_RELEASE=$(FAKE_RELEASE)
271 pushrelease$(FAKE_RELEASE) ::
272 cpan-upload $(DISTVNAME).tar$(SUFFIX)
273 git push origin v$(VERSION) HEAD
274 distdir: readmefile licensefile
275 readmefile: create_distdir
276 $(NOECHO) $(TEST_F) $(DISTVNAME)/README || $(MAKE) $(DISTVNAME)/README
277 $(DISTVNAME)/README: $(VERSION_FROM)
278 $(NOECHO) $(MKPATH) $(DISTVNAME)
279 pod2text $(VERSION_FROM) >$(DISTVNAME)/README
280 $(NOECHO) $(ABSPERLRUN) $(HELPERS)/add-to-manifest -d $(DISTVNAME) README
281 distsignature: readmefile licensefile
282 licensefile: create_distdir
283 $(NOECHO) $(TEST_F) $(DISTVNAME)/LICENSE || $(MAKE) $(DISTVNAME)/LICENSE || $(TRUE)
284 $(DISTVNAME)/LICENSE: Makefile.PL
285 $(NOECHO) $(MKPATH) $(DISTVNAME)
286 $(ABSPERLRUN) $(HELPERS)/generate-license -o $(DISTVNAME)/LICENSE $(AUTHORS) $(LICENSES)
287 $(NOECHO) $(ABSPERLRUN) $(HELPERS)/add-to-manifest -d $(DISTVNAME) LICENSE
288 disttest: distmanicheck
289 distmanicheck: create_distdir
290 cd $(DISTVNAME) && $(ABSPERLRUN) "-MExtUtils::Manifest=manicheck" -e "exit manicheck"
292 $(ABSPERLRUN) $(HELPERS)/add-changelog-heading --git $(VERSION) $(CHANGELOG)
295 $(RM_F) $(FIRST_MAKEFILE)
299 grep { $include !~ /^bump$_(?: +\w+)*:/m } ('', 'minor', 'major');
301 for my $target (@bump_targets) {
302 push @out, sprintf <<'END', "bump$target", ($target || '$(V)');
304 $(ABSPERLRUN) $(HELPERS)/bump-version --git $(VERSION) %s
305 $(RM_F) $(FIRST_MAKEFILE)
312 "\n\n# --- Distar section:\n\n",
313 (map "$_ = $vars{$_}\n", sort keys %vars),
318 "# --- Makefile.include:\n",
333 Distar - Additions to ExtUtils::MakeMaker for dist authors
339 use ExtUtils::MakeMaker;
340 (do './maint/Makefile.PL.include' or die $@) unless -f 'META.yml';
344 F<maint/Makefile.PL.include>:
346 BEGIN { -e 'Distar' or system("git clone git://git.shadowcat.co.uk/p5sagit/Distar.git") }
347 use lib 'Distar/lib';
350 author 'A. U. Thor <author@cpan.org>';
352 manifest_include t => 'test-helper.pl';
353 manifest_include corpus => '.txt';
358 $ make bump # bump version
359 $ make bump V=2.000000 # bump to specific version
360 $ make bumpminor # bump minor version component
361 $ make bumpmajor # bump major version component
362 $ make nextrelease # add version heading to Changes file
363 $ make releasetest # build dist and test (with xt/ and RELEASE_TESTING=1)
364 $ make preflight # check that repo and file state is release ready
365 $ make release # check releasetest and preflight, commits and tags,
366 # builds and uploads to CPAN, and pushes commits and
368 $ make release FAKE_RELEASE=1
369 # builds a release INCLUDING committing and tagging,
370 # but does not upload to cpan or push anything to git
374 L<ExtUtils::MakeMaker> works well enough as development tool for
375 builting and testing, but using it to release is annoying and error prone.
376 Distar adds just enough to L<ExtUtils::MakeMaker> for it to be a usable dist
377 author tool. This includes extra commands for releasing and safety checks, and
378 automatic generation of some files. It doesn't require any non-core modules and
379 is compatible with old versions of perl.
383 =head2 author( $author )
385 Set the author to include in generated META files. Can be a single entry, or
388 =head2 manifest_include( $dir, $pattern )
390 Add a pattern to include files in the MANIFEST file, and thus in the generated
393 The pattern can be either a regex, or a path suffix. It will be applied to the
394 full path past the directory specified.
396 The default files that are always included are: F<.pm> and F<.pod> files in
397 F<lib>, F<.t> files in F<t> and F<xt>, F<.pm> files in F<t/lib> and F<xt/lib>,
398 F<Changes>, F<MANIFEST>, F<README>, F<LICENSE>, F<META.yml>, and F<.PL> files in
399 the dist root, and all files in F<maint>.
401 =head1 AUTOGENERATED FILES
405 =item F<MANIFEST.SKIP>
407 The F<MANIFEST.SKIP> will be automatically generated to exclude any files not
408 explicitly allowed via C<manifest_include> or the included defaults. It will be
409 created (or updated) at C<perl Makefile.PL> time.
413 The F<README> file will be generated at dist generation time, inside the built
414 dist. It will be generated using C<pod2text> on the main module.
416 If a F<README> file exists in the repo, it will be used directly instead of
421 =head1 MAKE COMMMANDS
425 test will be adjusted to include F<xt/> tests by default. This will only apply
426 for authors, not users installing from CPAN.
430 Releases the dist. Before releasing, checks will be done on the dist using the
431 C<preflight> and C<releasetest> commands.
433 Releasing will generate a dist tarball and upload it to CPAN using cpan-upload.
434 It will also create a git tag for the release, and push the tag and branch.
438 If release is run with FAKE_RELEASE=1 set, it will skip uploading to CPAN and
439 pushing to git. A release commit will still be created and tagged locally.
443 Performs a number of checks on the files and repository, ensuring it is in a
444 sane state to do a release. The checks are:
448 =item * All version numbers match
450 =item * The F<MANIFEST> file is up to date
452 =item * The branch is correct
454 =item * There is no existing tag for the version
456 =item * There are no unmerged upstream changes
458 =item * There are no outstanding local changes
460 =item * There is an appropriate staged Changes heading
462 =item * cpan-upload is available
468 Test the dist preparing for a release. This generates a dist dir and runs the
469 tests from inside it. This ensures all appropriate files are included inside
470 the dist. C<RELEASE_TESTING> will be set in the environment.
474 Adds an appropriate changelog heading for the release, and prompts to stage the
479 Bumps the version number. This will try to preserve the length and format of
480 the version number. The least significant digit will be incremented. Versions
481 with underscores will preserve the underscore in the same position.
483 Optionally accepts a C<V> option to set the version to a specific value.
485 The version changes will automatically be committed. Unstaged modifications to
486 the files will be left untouched.
490 The V option will be passed along to the version bumping script. It can accept
491 a space separated list of options, including an explicit version number.
499 Updates version numbers even if they do not match the current expected version
504 Attempts to convert the updated version to a stable version, removing any
509 Attempts to convert the updated version to an alpha version, adding an
510 underscore in an appropriate place.
516 Like bump, but increments the minor segment of the version. This will treat
517 numeric versions as x.yyyzzz format, incrementing the yyy segment.
521 Like bumpminor, but bumping the major segment.
525 Updates Distar and re-runs C<perl Makefile.PL>
529 IRC: #web-simple on irc.perl.org
531 Git repository: L<git://git.shadowcat.co.uk/p5sagit/Distar>
533 Git browser: L<http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=p5sagit/Distar.git;a=summary>
537 mst - Matt S. Trout (cpan:MSTROUT) <mst@shadowcat.co.uk>
541 haarg - Graham Knop (cpan:HAARG) <haarg@cpan.org>
543 ether - Karen Etheridge (cpan:ETHER) <ether@cpan.org>
545 frew - Arthur Axel "fREW" Schmidt (cpan:FREW) <frioux@gmail.com>
547 Mithaldu - Christian Walde (cpan:MITHALDU) <walde.christian@googlemail.com>
551 Copyright (c) 2011-2015 the Distar L</AUTHOR> and L</CONTRIBUTORS>
556 This library is free software and may be distributed under the same terms
557 as perl itself. See L<http://dev.perl.org/licenses/>.