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 $tar = $self->{TAR};
144 my $version = `$tar --version`;
145 if ($version =~ /GNU tar/) {
148 elsif (!$pre_tar && `gtar --version`) {
152 my $tarflags = $self->{TARFLAGS};
153 if (my ($flags) = $tarflags =~ /^-?([cvhlLf]+)$/) {
154 if ($flags =~ s/c// && $flags =~ s/f//) {
155 $tarflags = '--format=ustar -c'.$flags.'f';
157 $tarflags = '--owner=0 --group=0 '.$tarflags;
169 $warn .= ($warn ? ' and ' : '').'gid('.(0+$)).')';
172 warn "$warn too large! Max is ".(2**21-1).".\n"
173 ."Dist creation will likely fail. Install GNU tar to work around.\n";
178 $self->{TARFLAGS} = $tarflags;
185 my $out = $self->SUPER::tarfile_target(@_);
186 my $verify = <<'END_FRAG';
187 $(ABSPERLRUN) $(HELPERS)/verify-tarball $(DISTVNAME).tar $(DISTVNAME)/MANIFEST --tar="$(TAR)"
189 $out =~ s{(\$\(TAR\).*\n)}{$1$verify};
193 sub metafile_target {
195 my $metafile_target = $self->SUPER::metafile_target(@_);
196 for (qw(META.yml META.json)) {
197 my $add = "\t\$(NOECHO) \$(ABSPERLRUN) $(HELPERS)/add-git-commit \$(DISTVNAME)/$_\n";
198 $metafile_target =~ s{(.*\b\Q$_\E\b[^\n]*\n)}{$1$add}s;
207 if (open my $fh, '<', 'maint/Makefile.include') {
208 $include = "\n# --- Makefile.include:\n\n" . do { local $/; <$fh> };
209 $include =~ s/\n?\z/\n/;
213 grep { $include !~ /^bump$_(?: +\w+)*:/m } ('', 'minor', 'major');
215 my $distar_lib = File::Basename::dirname(__FILE__);
216 my $helpers = File::Spec->catdir($distar_lib, File::Spec->updir, 'helpers');
218 my $licenses = $self->{LICENSE} || $self->{META_ADD}{license} || $self->{META_MERGE}{license};
219 my $authors = $self->{AUTHOR};
220 $_ = ref $_ ? $_ : [$_ || ()]
221 for $licenses, $authors;
224 DISTAR_LIB => $self->quote_literal($distar_lib),
225 HELPERS => $self->quote_literal($helpers),
226 REMAKE => join(' ', '$(PERLRUN)', '-I$(DISTAR_LIB)', '-MDistar', 'Makefile.PL', map { $self->quote_literal($_) } @ARGV),
227 BRANCH => $self->{BRANCH} ||= 'master',
228 CHANGELOG => $self->{CHANGELOG} ||= 'Changes',
229 DEV_NULL_STDOUT => ($self->{DEV_NULL} ? '>'.File::Spec->devnull : ''),
230 DISTTEST_MAKEFILE_PARAMS => '',
231 AUTHORS => $self->quote_literal(join(', ', @$authors)),
232 LICENSES => join(' ', map $self->quote_literal($_), @$licenses),
233 GET_CHANGELOG => '$(ABSPERLRUN) $(HELPERS)/get-changelog $(VERSION) $(CHANGELOG)',
235 -e File::Spec->catdir($distar_lib, File::Spec->updir, '.git')
236 ? 'git -C $(DISTAR_LIB) pull'
237 : '$(ECHO) "Distar code is not in a git repo, unable to update!"'
241 my $dist_test = $self->SUPER::dist_test(@_);
242 $dist_test =~ s/(\bMakefile\.PL\b)/$1 \$(DISTTEST_MAKEFILE_PARAMS)/;
246 "\n\n# --- Distar section:\n\n",
247 (map "$_ = $vars{$_}\n", sort keys %vars),
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)\""
262 $(GET_CHANGELOG) -p"Release commit for $(VERSION)" | git commit -a -F -
263 $(GET_CHANGELOG) -p"release v$(VERSION)" | git tag -a -F - "v$(VERSION)"
264 $(RM_RF) $(DISTVNAME)
265 $(MAKE) $(DISTVNAME).tar$(SUFFIX)
266 $(NOECHO) $(MAKE) pushrelease FAKE_RELEASE=$(FAKE_RELEASE)
269 pushrelease$(FAKE_RELEASE) ::
270 cpan-upload $(DISTVNAME).tar$(SUFFIX)
271 git push origin v$(VERSION) HEAD
272 distdir: readmefile licensefile
273 readmefile: create_distdir
274 $(NOECHO) $(TEST_F) $(DISTVNAME)/README || $(MAKE) $(DISTVNAME)/README
275 $(DISTVNAME)/README: $(VERSION_FROM)
276 $(NOECHO) $(MKPATH) $(DISTVNAME)
277 pod2text $(VERSION_FROM) >$(DISTVNAME)/README
278 $(NOECHO) $(ABSPERLRUN) $(HELPERS)/add-to-manifest -d $(DISTVNAME) README
279 distsignature: readmefile licensefile
280 licensefile: create_distdir
281 $(NOECHO) $(TEST_F) $(DISTVNAME)/LICENSE || $(MAKE) $(DISTVNAME)/LICENSE
282 $(DISTVNAME)/LICENSE: Makefile.PL
283 $(NOECHO) $(MKPATH) $(DISTVNAME)
284 $(ABSPERLRUN) $(HELPERS)/generate-license $(AUTHORS) $(LICENSES) >$(DISTVNAME)/LICENSE
285 $(NOECHO) $(ABSPERLRUN) $(HELPERS)/add-to-manifest -d $(DISTVNAME) LICENSE
286 disttest: distmanicheck
287 distmanicheck: create_distdir
288 cd $(DISTVNAME) && $(ABSPERLRUN) "-MExtUtils::Manifest=manicheck" -e "exit manicheck"
290 $(ABSPERLRUN) $(HELPERS)/add-changelog-heading --git $(VERSION) $(CHANGELOG)
293 $(RM_F) $(FIRST_MAKEFILE)
296 map(sprintf(<<'END', "bump$_", ($_ || '$(V)')), @bump_targets),
298 $(ABSPERLRUN) $(HELPERS)/bump-version --git $(VERSION) %s
299 $(RM_F) $(FIRST_MAKEFILE)
313 Distar - Additions to ExtUtils::MakeMaker for dist authors
319 use ExtUtils::MakeMaker;
320 (do './maint/Makefile.PL.include' or die $@) unless -f 'META.yml';
324 F<maint/Makefile.PL.include>:
326 BEGIN { -e 'Distar' or system("git clone git://git.shadowcat.co.uk/p5sagit/Distar.git") }
327 use lib 'Distar/lib';
330 author 'A. U. Thor <author@cpan.org>';
332 manifest_include t => 'test-helper.pl';
333 manifest_include corpus => '.txt';
338 $ make bump # bump version
339 $ make bump V=2.000000 # bump to specific version
340 $ make bumpminor # bump minor version component
341 $ make bumpmajor # bump major version component
342 $ make nextrelease # add version heading to Changes file
343 $ make releasetest # build dist and test (with xt/ and RELEASE_TESTING=1)
344 $ make preflight # check that repo and file state is release ready
345 $ make release # check releasetest and preflight, commits and tags,
346 # builds and uploads to CPAN, and pushes commits and
348 $ make release FAKE_RELEASE=1
349 # builds a release INCLUDING committing and tagging,
350 # but does not upload to cpan or push anything to git
354 L<ExtUtils::MakeMaker> works well enough as development tool for
355 builting and testing, but using it to release is annoying and error prone.
356 Distar adds just enough to L<ExtUtils::MakeMaker> for it to be a usable dist
357 author tool. This includes extra commands for releasing and safety checks, and
358 automatic generation of some files. It doesn't require any non-core modules and
359 is compatible with old versions of perl.
363 =head2 author( $author )
365 Set the author to include in generated META files. Can be a single entry, or
368 =head2 manifest_include( $dir, $pattern )
370 Add a pattern to include files in the MANIFEST file, and thus in the generated
373 The pattern can be either a regex, or a path suffix. It will be applied to the
374 full path past the directory specified.
376 The default files that are always included are: F<.pm> and F<.pod> files in
377 F<lib>, F<.t> files in F<t> and F<xt>, F<.pm> files in F<t/lib> and F<xt/lib>,
378 F<Changes>, F<MANIFEST>, F<README>, F<LICENSE>, F<META.yml>, and F<.PL> files in
379 the dist root, and all files in F<maint>.
381 =head1 AUTOGENERATED FILES
385 =item F<MANIFEST.SKIP>
387 The F<MANIFEST.SKIP> will be automatically generated to exclude any files not
388 explicitly allowed via C<manifest_include> or the included defaults. It will be
389 created (or updated) at C<perl Makefile.PL> time.
393 The F<README> file will be generated at dist generation time, inside the built
394 dist. It will be generated using C<pod2text> on the main module.
396 If a F<README> file exists in the repo, it will be used directly instead of
401 =head1 MAKE COMMMANDS
405 test will be adjusted to include F<xt/> tests by default. This will only apply
406 for authors, not users installing from CPAN.
410 Releases the dist. Before releasing, checks will be done on the dist using the
411 C<preflight> and C<releasetest> commands.
413 Releasing will generate a dist tarball and upload it to CPAN using cpan-upload.
414 It will also create a git tag for the release, and push the tag and branch.
418 If release is run with FAKE_RELEASE=1 set, it will skip uploading to CPAN and
419 pushing to git. A release commit will still be created and tagged locally.
423 Performs a number of checks on the files and repository, ensuring it is in a
424 sane state to do a release. The checks are:
428 =item * All version numbers match
430 =item * The F<MANIFEST> file is up to date
432 =item * The branch is correct
434 =item * There is no existing tag for the version
436 =item * There are no unmerged upstream changes
438 =item * There are no outstanding local changes
440 =item * There is an appropriate staged Changes heading
442 =item * cpan-upload is available
448 Test the dist preparing for a release. This generates a dist dir and runs the
449 tests from inside it. This ensures all appropriate files are included inside
450 the dist. C<RELEASE_TESTING> will be set in the environment.
454 Adds an appropriate changelog heading for the release, and prompts to stage the
459 Bumps the version number. This will try to preserve the length and format of
460 the version number. The least significant digit will be incremented. Versions
461 with underscores will preserve the underscore in the same position.
463 Optionally accepts a C<V> option to set the version to a specific value.
465 The version changes will automatically be committed. Unstaged modifications to
466 the files will be left untouched.
470 The V option will be passed along to the version bumping script. It can accept
471 a space separated list of options, including an explicit version number.
479 Updates version numbers even if they do not match the current expected version
484 Attempts to convert the updated version to a stable version, removing any
489 Attempts to convert the updated version to an alpha version, adding an
490 underscore in an appropriate place.
496 Like bump, but increments the minor segment of the version. This will treat
497 numeric versions as x.yyyzzz format, incrementing the yyy segment.
501 Like bumpminor, but bumping the major segment.
505 Updates Distar and re-runs C<perl Makefile.PL>
509 IRC: #web-simple on irc.perl.org
511 Git repository: L<git://git.shadowcat.co.uk/p5sagit/Distar>
513 Git browser: L<http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=p5sagit/Distar.git;a=summary>
517 mst - Matt S. Trout (cpan:MSTROUT) <mst@shadowcat.co.uk>
521 haarg - Graham Knop (cpan:HAARG) <haarg@cpan.org>
523 ether - Karen Etheridge (cpan:ETHER) <ether@cpan.org>
525 frew - Arthur Axel "fREW" Schmidt (cpan:FREW) <frioux@gmail.com>
527 Mithaldu - Christian Walde (cpan:MITHALDU) <walde.christian@googlemail.com>
531 Copyright (c) 2011-2015 the Distar L</AUTHOR> and L</CONTRIBUTORS>
536 This library is free software and may be distributed under the same terms
537 as perl itself. See L<http://dev.perl.org/licenses/>.