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};
213 if (open my $fh, '<', 'maint/Makefile.include') {
214 $include = "\n# --- Makefile.include:\n\n" . do { local $/; <$fh> };
215 $include =~ s/\n?\z/\n/;
219 grep { $include !~ /^bump$_(?: +\w+)*:/m } ('', 'minor', 'major');
221 my $distar_lib = File::Basename::dirname(__FILE__);
222 my $helpers = File::Spec->catdir($distar_lib, File::Spec->updir, 'helpers');
224 my $licenses = $self->{LICENSE} || $self->{META_ADD}{license} || $self->{META_MERGE}{license};
225 my $authors = $self->{AUTHOR};
226 $_ = ref $_ ? $_ : [$_ || ()]
227 for $licenses, $authors;
230 DISTAR_LIB => $self->quote_literal($distar_lib),
231 HELPERS => $self->quote_literal($helpers),
232 REMAKE => join(' ', '$(PERLRUN)', '-I$(DISTAR_LIB)', '-MDistar', 'Makefile.PL', map { $self->quote_literal($_) } @ARGV),
233 BRANCH => $self->{BRANCH} ||= 'master',
234 CHANGELOG => $self->{CHANGELOG} ||= 'Changes',
235 DEV_NULL_STDOUT => ($self->{DEV_NULL} ? '>'.File::Spec->devnull : ''),
236 DISTTEST_MAKEFILE_PARAMS => '',
237 AUTHORS => $self->quote_literal(join(', ', @$authors)),
238 LICENSES => join(' ', map $self->quote_literal($_), @$licenses),
239 GET_CHANGELOG => '$(ABSPERLRUN) $(HELPERS)/get-changelog $(VERSION) $(CHANGELOG)',
241 -e File::Spec->catdir($distar_lib, File::Spec->updir, '.git')
242 ? 'git -C $(DISTAR_LIB) pull'
243 : '$(ECHO) "Distar code is not in a git repo, unable to update!"'
247 my $dist_test = $self->SUPER::dist_test(@_);
248 $dist_test =~ s/(\bMakefile\.PL\b)/$1 \$(DISTTEST_MAKEFILE_PARAMS)/;
252 "\n\n# --- Distar section:\n\n",
253 (map "$_ = $vars{$_}\n", sort keys %vars),
256 preflight: check-version check-manifest check-cpan-upload
257 $(ABSPERLRUN) $(HELPERS)/preflight $(VERSION) --changelog=$(CHANGELOG) --branch=$(BRANCH)
259 $(ABSPERLRUN) $(HELPERS)/check-version $(VERSION) $(TO_INST_PM) $(EXE_FILES)
261 $(ABSPERLRUN) $(HELPERS)/check-manifest
263 $(NOECHO) cpan-upload -h $(DEV_NULL_STDOUT)
265 $(MAKE) disttest RELEASE_TESTING=1 DISTTEST_MAKEFILE_PARAMS="PREREQ_FATAL=1" PASTHRU="$(PASTHRU) TEST_FILES=\"$(TEST_FILES)\""
266 $(NOECHO) $(TEST_F) $(DISTVNAME)/LICENSE || $(ECHO) "Failed to generate $(DISTVNAME)/LICENSE!" >&2
267 $(NOECHO) $(TEST_F) $(DISTVNAME)/LICENSE
270 $(GET_CHANGELOG) -p"Release commit for $(VERSION)" | git commit -a -F -
271 $(GET_CHANGELOG) -p"release v$(VERSION)" | git tag -a -F - "v$(VERSION)"
272 $(RM_RF) $(DISTVNAME)
273 $(MAKE) $(DISTVNAME).tar$(SUFFIX)
274 $(NOECHO) $(MAKE) pushrelease FAKE_RELEASE=$(FAKE_RELEASE)
277 pushrelease$(FAKE_RELEASE) ::
278 cpan-upload $(DISTVNAME).tar$(SUFFIX)
279 git push origin v$(VERSION) HEAD
280 distdir: readmefile licensefile
281 readmefile: create_distdir
282 $(NOECHO) $(TEST_F) $(DISTVNAME)/README || $(MAKE) $(DISTVNAME)/README
283 $(DISTVNAME)/README: $(VERSION_FROM)
284 $(NOECHO) $(MKPATH) $(DISTVNAME)
285 pod2text $(VERSION_FROM) >$(DISTVNAME)/README
286 $(NOECHO) $(ABSPERLRUN) $(HELPERS)/add-to-manifest -d $(DISTVNAME) README
287 distsignature: readmefile licensefile
288 licensefile: create_distdir
289 $(NOECHO) $(TEST_F) $(DISTVNAME)/LICENSE || $(MAKE) $(DISTVNAME)/LICENSE || $(TRUE)
290 $(DISTVNAME)/LICENSE: Makefile.PL
291 $(NOECHO) $(MKPATH) $(DISTVNAME)
292 $(ABSPERLRUN) $(HELPERS)/generate-license -o $(DISTVNAME)/LICENSE $(AUTHORS) $(LICENSES)
293 $(NOECHO) $(ABSPERLRUN) $(HELPERS)/add-to-manifest -d $(DISTVNAME) LICENSE
294 disttest: distmanicheck
295 distmanicheck: create_distdir
296 cd $(DISTVNAME) && $(ABSPERLRUN) "-MExtUtils::Manifest=manicheck" -e "exit manicheck"
298 $(ABSPERLRUN) $(HELPERS)/add-changelog-heading --git $(VERSION) $(CHANGELOG)
301 $(RM_F) $(FIRST_MAKEFILE)
304 map(sprintf(<<'END', "bump$_", ($_ || '$(V)')), @bump_targets),
306 $(ABSPERLRUN) $(HELPERS)/bump-version --git $(VERSION) %s
307 $(RM_F) $(FIRST_MAKEFILE)
321 Distar - Additions to ExtUtils::MakeMaker for dist authors
327 use ExtUtils::MakeMaker;
328 (do './maint/Makefile.PL.include' or die $@) unless -f 'META.yml';
332 F<maint/Makefile.PL.include>:
334 BEGIN { -e 'Distar' or system("git clone git://git.shadowcat.co.uk/p5sagit/Distar.git") }
335 use lib 'Distar/lib';
338 author 'A. U. Thor <author@cpan.org>';
340 manifest_include t => 'test-helper.pl';
341 manifest_include corpus => '.txt';
346 $ make bump # bump version
347 $ make bump V=2.000000 # bump to specific version
348 $ make bumpminor # bump minor version component
349 $ make bumpmajor # bump major version component
350 $ make nextrelease # add version heading to Changes file
351 $ make releasetest # build dist and test (with xt/ and RELEASE_TESTING=1)
352 $ make preflight # check that repo and file state is release ready
353 $ make release # check releasetest and preflight, commits and tags,
354 # builds and uploads to CPAN, and pushes commits and
356 $ make release FAKE_RELEASE=1
357 # builds a release INCLUDING committing and tagging,
358 # but does not upload to cpan or push anything to git
362 L<ExtUtils::MakeMaker> works well enough as development tool for
363 builting and testing, but using it to release is annoying and error prone.
364 Distar adds just enough to L<ExtUtils::MakeMaker> for it to be a usable dist
365 author tool. This includes extra commands for releasing and safety checks, and
366 automatic generation of some files. It doesn't require any non-core modules and
367 is compatible with old versions of perl.
371 =head2 author( $author )
373 Set the author to include in generated META files. Can be a single entry, or
376 =head2 manifest_include( $dir, $pattern )
378 Add a pattern to include files in the MANIFEST file, and thus in the generated
381 The pattern can be either a regex, or a path suffix. It will be applied to the
382 full path past the directory specified.
384 The default files that are always included are: F<.pm> and F<.pod> files in
385 F<lib>, F<.t> files in F<t> and F<xt>, F<.pm> files in F<t/lib> and F<xt/lib>,
386 F<Changes>, F<MANIFEST>, F<README>, F<LICENSE>, F<META.yml>, and F<.PL> files in
387 the dist root, and all files in F<maint>.
389 =head1 AUTOGENERATED FILES
393 =item F<MANIFEST.SKIP>
395 The F<MANIFEST.SKIP> will be automatically generated to exclude any files not
396 explicitly allowed via C<manifest_include> or the included defaults. It will be
397 created (or updated) at C<perl Makefile.PL> time.
401 The F<README> file will be generated at dist generation time, inside the built
402 dist. It will be generated using C<pod2text> on the main module.
404 If a F<README> file exists in the repo, it will be used directly instead of
409 =head1 MAKE COMMMANDS
413 test will be adjusted to include F<xt/> tests by default. This will only apply
414 for authors, not users installing from CPAN.
418 Releases the dist. Before releasing, checks will be done on the dist using the
419 C<preflight> and C<releasetest> commands.
421 Releasing will generate a dist tarball and upload it to CPAN using cpan-upload.
422 It will also create a git tag for the release, and push the tag and branch.
426 If release is run with FAKE_RELEASE=1 set, it will skip uploading to CPAN and
427 pushing to git. A release commit will still be created and tagged locally.
431 Performs a number of checks on the files and repository, ensuring it is in a
432 sane state to do a release. The checks are:
436 =item * All version numbers match
438 =item * The F<MANIFEST> file is up to date
440 =item * The branch is correct
442 =item * There is no existing tag for the version
444 =item * There are no unmerged upstream changes
446 =item * There are no outstanding local changes
448 =item * There is an appropriate staged Changes heading
450 =item * cpan-upload is available
456 Test the dist preparing for a release. This generates a dist dir and runs the
457 tests from inside it. This ensures all appropriate files are included inside
458 the dist. C<RELEASE_TESTING> will be set in the environment.
462 Adds an appropriate changelog heading for the release, and prompts to stage the
467 Bumps the version number. This will try to preserve the length and format of
468 the version number. The least significant digit will be incremented. Versions
469 with underscores will preserve the underscore in the same position.
471 Optionally accepts a C<V> option to set the version to a specific value.
473 The version changes will automatically be committed. Unstaged modifications to
474 the files will be left untouched.
478 The V option will be passed along to the version bumping script. It can accept
479 a space separated list of options, including an explicit version number.
487 Updates version numbers even if they do not match the current expected version
492 Attempts to convert the updated version to a stable version, removing any
497 Attempts to convert the updated version to an alpha version, adding an
498 underscore in an appropriate place.
504 Like bump, but increments the minor segment of the version. This will treat
505 numeric versions as x.yyyzzz format, incrementing the yyy segment.
509 Like bumpminor, but bumping the major segment.
513 Updates Distar and re-runs C<perl Makefile.PL>
517 IRC: #web-simple on irc.perl.org
519 Git repository: L<git://git.shadowcat.co.uk/p5sagit/Distar>
521 Git browser: L<http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=p5sagit/Distar.git;a=summary>
525 mst - Matt S. Trout (cpan:MSTROUT) <mst@shadowcat.co.uk>
529 haarg - Graham Knop (cpan:HAARG) <haarg@cpan.org>
531 ether - Karen Etheridge (cpan:ETHER) <ether@cpan.org>
533 frew - Arthur Axel "fREW" Schmidt (cpan:FREW) <frioux@gmail.com>
535 Mithaldu - Christian Walde (cpan:MITHALDU) <walde.christian@googlemail.com>
539 Copyright (c) 2011-2015 the Distar L</AUTHOR> and L</CONTRIBUTORS>
544 This library is free software and may be distributed under the same terms
545 as perl itself. See L<http://dev.perl.org/licenses/>.