3 use warnings FATAL => 'all';
5 use ExtUtils::MakeMaker ();
10 our $VERSION = '0.003000';
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;
44 '' => qr{Changes|MANIFEST|README|LICENSE|META\.yml},
45 'maint' => qr{[^.].*},
48 sub manifest_include {
53 sub readme_generator {
54 die "readme_generator unsupported" if @_ && $_[0];
57 sub write_manifest_skip {
59 my @files = @Manifest;
61 while (my ($dir, $spec) = splice(@files, 0, 2)) {
62 my $re = ($dir ? $dir.'/' : '').
63 ((ref($spec) eq 'Regexp')
67 # print ref as well as stringification in case of overload ""
68 : die "spec must be string or regexp, was: ${spec} (${\ref $spec})");
71 my $dist_name = $mm->{DISTNAME};
72 my $include = join '|', map "${_}\$", @parts;
73 my $final = "^(?:\Q$dist_name\E-v?[0-9_.]+/|(?!$include))";
74 open my $skip, '>', 'MANIFEST.SKIP'
75 or die "can't open MANIFEST.SKIP: $!";
76 print $skip "${final}\n";
83 sub _gen_make_section {
89 s/^\t?/\t/mg for @lines;
94 s/\n?\z/\n/ for @lines;
100 my ($class, $args) = @_;
101 my %test = %{$args->{test}||{}};
102 my $tests = $test{TESTS} || 't/*.t';
103 $tests !~ /\b\Q$_\E\b/ and $tests .= " $_"
104 for 'xt/*.t', 'xt/*/*.t';
105 $test{TESTS} = $tests;
106 return $class->SUPER::new({
108 MIN_PERL_VERSION => '5.006',
110 AUTHOR => ($MM_VER >= 6.5702 ? $Distar::Author : join(', ', @$Distar::Author)),
112 (exists $args->{ABSTRACT} ? () : (ABSTRACT_FROM => $args->{VERSION_FROM})),
115 realclean => { FILES => (
116 ($args->{realclean}{FILES}||'')
117 . ' Distar/ MANIFEST.SKIP MANIFEST MANIFEST.bak'
124 `git ls-files --error-unmatch MANIFEST.SKIP 2>&1`;
125 my $maniskip_tracked = !$?;
127 Distar::write_manifest_skip($self)
128 unless $maniskip_tracked;
129 $self->SUPER::flush(@_);
132 sub special_targets {
134 my $targets = $self->SUPER::special_targets(@_);
135 my $phony_targets = join ' ', qw(
150 $targets =~ s/^(\.PHONY *:.*)/$1 $phony_targets/m;
156 my $out = $self->SUPER::init_dist(@_);
158 my $dn = File::Spec->devnull;
159 my $tar = $self->{TAR};
161 my $tarflags = $self->{TARFLAGS};
162 if (my ($flags) = $tarflags =~ /^-?([cvhlLf]+)$/) {
163 if ($flags =~ s/c// && $flags =~ s/f//) {
164 $tarflags = '--format=ustar -c'.$flags.'f';
166 for my $options ('--owner=0 --group=0', '--uid=0 --gid=0') {
167 my $try = `$tar -c $options "$me" 2>$dn`;
169 $tarflags = "$options $tarflags";
177 $self->{TARFLAGS} = $tarflags;
186 # default setup for Distar involves checking it out as a subdirectory at
187 # the top of the dist. Without NORECURS, EUMM would try to include
188 # Distar's Makefile.PL. Prevent EUMM from looking inside.
190 if $path eq 'Distar';
192 $self->SUPER::libscan(@_);
197 my $out = $self->SUPER::tarfile_target(@_);
198 my $verify = _gen_make_section([
199 '$(ABSPERLRUN) $(HELPERS)/verify-tarball $(DISTVNAME).tar $(DISTVNAME)/MANIFEST --tar="$(TAR)"',
201 $out =~ s{(\$\(TAR\).*\n)}{$1$verify};
208 my $distar_lib = File::Basename::dirname(__FILE__);
209 my $helpers = File::Spec->catdir($distar_lib, 'Distar', 'helpers');
211 my $licenses = $self->{LICENSE} || $self->{META_ADD}{license} || $self->{META_MERGE}{license};
212 my $authors = $self->{AUTHOR};
213 $_ = ref $_ ? $_ : [$_ || ()]
214 for $licenses, $authors;
217 DISTAR_LIB => $self->quote_literal($distar_lib),
218 HELPERS => $self->quote_literal($helpers),
219 REMAKE => join(' ', '$(PERLRUN)', '-I$(DISTAR_LIB)', '-MDistar', 'Makefile.PL', map { $self->quote_literal($_) } @ARGV),
220 BRANCH => $self->{BRANCH} ||= 'master',
221 CHANGELOG => $self->{CHANGELOG} ||= 'Changes',
222 DEV_NULL_STDOUT => ($self->{DEV_NULL} ? '>'.File::Spec->devnull : ''),
223 DISTTEST_MAKEFILE_PARAMS => '',
224 AUTHORS => $self->quote_literal(join(', ', @$authors)),
225 LICENSES => join(' ', map $self->quote_literal($_), @$licenses),
226 GET_CHANGELOG => '$(ABSPERLRUN) $(HELPERS)/get-changelog $(VERSION) $(CHANGELOG)',
228 -e File::Spec->catdir($distar_lib, File::Spec->updir, '.git')
229 ? 'git -C $(DISTAR_LIB) pull'
230 : '$(ECHO) "Distar code is not in a git repo, unable to update!"'
232 README_FROM => $self->{ABSTRACT_FROM} || $self->{VERSION_FROM},
235 my $dist_test = $self->SUPER::dist_test(@_);
236 $dist_test =~ s/(\bMakefile\.PL\b)/$1 \$(DISTTEST_MAKEFILE_PARAMS)/;
239 if (open my $fh, '<', 'maint/Makefile.include') {
240 $include = do { local $/; <$fh> };
241 $include =~ s/\n?\z/\n/;
247 'preflight: check-version check-manifest check-cpan-upload' => [
248 '$(ABSPERLRUN) $(HELPERS)/preflight $(VERSION) --changelog=$(CHANGELOG) --branch=$(BRANCH)',
250 'check-version:' => [
251 '$(ABSPERLRUN) $(HELPERS)/check-version $(VERSION) $(TO_INST_PM) $(EXE_FILES)',
253 'check-manifest:' => [
254 '$(ABSPERLRUN) $(HELPERS)/check-manifest',
256 'check-cpan-upload:' => [
257 '$(NOECHO) cpan-upload -h $(DEV_NULL_STDOUT)',
260 '$(MAKE) disttest RELEASE_TESTING=1 DISTTEST_MAKEFILE_PARAMS="PREREQ_FATAL=1" PASTHRU="$(PASTHRU) TEST_FILES=\"$(TEST_FILES)\""',
261 '$(NOECHO) $(TEST_F) $(DISTVNAME)/LICENSE || $(ECHO) "Failed to generate $(DISTVNAME)/LICENSE!" >&2',
262 '$(NOECHO) $(TEST_F) $(DISTVNAME)/LICENSE',
264 'release: preflight' => [
265 '$(MAKE) releasetest',
266 '$(GET_CHANGELOG) -p"Release commit for $(VERSION)" | git commit -a -F -',
267 '$(GET_CHANGELOG) -p"release v$(VERSION)" | git tag -a -F - "v$(VERSION)"',
268 '$(RM_RF) $(DISTVNAME)',
269 '$(MAKE) $(DISTVNAME).tar$(SUFFIX)',
270 '$(NOECHO) $(MAKE) pushrelease FAKE_RELEASE=$(FAKE_RELEASE)',
272 'pushrelease ::' => [
275 'pushrelease$(FAKE_RELEASE) ::' => [
276 'cpan-upload $(DISTVNAME).tar$(SUFFIX)',
277 'git push origin v$(VERSION) HEAD',
279 'distdir: readmefile licensefile',
280 'readmefile: create_distdir' => [
281 '$(NOECHO) $(TEST_F) $(DISTVNAME)/README || $(MAKE) $(DISTVNAME)/README',
283 '$(DISTVNAME)/README: $(README_FROM)' => [
284 '$(NOECHO) $(MKPATH) $(DISTVNAME)',
285 'pod2text $(README_FROM) >$(DISTVNAME)/README',
286 '$(NOECHO) $(ABSPERLRUN) $(HELPERS)/add-to-manifest -d $(DISTVNAME) README',
288 'distsignature: readmefile licensefile',
289 'licensefile: create_distdir' => [
290 '$(NOECHO) $(TEST_F) $(DISTVNAME)/LICENSE || $(MAKE) $(DISTVNAME)/LICENSE || $(TRUE)',
292 '$(DISTVNAME)/LICENSE: Makefile.PL' => [
293 '$(NOECHO) $(MKPATH) $(DISTVNAME)',
294 '$(ABSPERLRUN) $(HELPERS)/generate-license -o $(DISTVNAME)/LICENSE $(AUTHORS) $(LICENSES)',
295 '$(NOECHO) $(ABSPERLRUN) $(HELPERS)/add-to-manifest -d $(DISTVNAME) LICENSE',
297 'disttest: distmanicheck',
298 'distmanicheck: create_distdir' => [
299 $self->cd('$(DISTVNAME)',
300 '$(ABSPERLRUN) "-MExtUtils::Manifest=manicheck" -e "exit manicheck"',
304 '$(ABSPERLRUN) $(HELPERS)/add-changelog-heading --git $(VERSION) $(CHANGELOG)',
308 '$(RM_F) $(FIRST_MAKEFILE)',
314 grep { $include !~ /^bump$_(?: +\w+)*:/m } ('', 'minor', 'major');
318 '$(ABSPERLRUN) $(HELPERS)/bump-version --git $(VERSION) '.($_ || '$(V)'),
319 '$(RM_F) $(FIRST_MAKEFILE)',
326 "\n\n# --- Distar section:\n\n",
327 (map "$_ = $vars{$_}\n", sort keys %vars),
329 _gen_make_section(@out),
332 "# --- Makefile.include:\n",
347 Distar - Additions to ExtUtils::MakeMaker for dist authors
353 use ExtUtils::MakeMaker;
354 (do './maint/Makefile.PL.include' or die $@) unless -f 'META.yml';
358 F<maint/Makefile.PL.include>:
360 BEGIN { -e 'Distar' or system qw(git clone https://github.com/p5sagit/Distar.git) }
361 use lib 'Distar/lib';
364 author 'A. U. Thor <author@cpan.org>';
366 manifest_include t => 'test-helper.pl';
367 manifest_include corpus => '.txt';
372 $ make bump # bump version
373 $ make bump V=2.000000 # bump to specific version
374 $ make bumpminor # bump minor version component
375 $ make bumpmajor # bump major version component
376 $ make nextrelease # add version heading to Changes file
377 $ make releasetest # build dist and test (with xt/ and RELEASE_TESTING=1)
378 $ make preflight # check that repo and file state is release ready
379 $ make release # check releasetest and preflight, commits and tags,
380 # builds and uploads to CPAN, and pushes commits and
382 $ make release FAKE_RELEASE=1
383 # builds a release INCLUDING committing and tagging,
384 # but does not upload to cpan or push anything to git
388 L<ExtUtils::MakeMaker> works well enough as development tool for
389 builting and testing, but using it to release is annoying and error prone.
390 Distar adds just enough to L<ExtUtils::MakeMaker> for it to be a usable dist
391 author tool. This includes extra commands for releasing and safety checks, and
392 automatic generation of some files. It doesn't require any non-core modules and
393 is compatible with old versions of perl.
397 =head2 author( $author )
399 Set the author to include in generated META files. Can be a single entry, or
402 =head2 manifest_include( $dir, $pattern )
404 Add a pattern to include files in the MANIFEST file, and thus in the generated
407 The pattern can be either a regex, or a path suffix. It will be applied to the
408 full path past the directory specified.
410 The default files that are always included are: F<.pm> and F<.pod> files in
411 F<lib>, F<.t> files in F<t> and F<xt>, F<.pm> files in F<t/lib> and F<xt/lib>,
412 F<Changes>, F<MANIFEST>, F<README>, F<LICENSE>, F<META.yml>, and F<.PL> files in
413 the dist root, and all files in F<maint>.
415 =head1 AUTOGENERATED FILES
419 =item F<MANIFEST.SKIP>
421 The F<MANIFEST.SKIP> will be automatically generated to exclude any files not
422 explicitly allowed via C<manifest_include> or the included defaults. It will be
423 created (or updated) at C<perl Makefile.PL> time.
427 The F<README> file will be generated at dist generation time, inside the built
428 dist. It will be generated using C<pod2text> on the main module.
430 If a F<README> file exists in the repo, it will be used directly instead of
435 =head1 MAKE COMMMANDS
439 test will be adjusted to include F<xt/> tests by default. This will only apply
440 for authors, not users installing from CPAN.
444 Releases the dist. Before releasing, checks will be done on the dist using the
445 C<preflight> and C<releasetest> commands.
447 Releasing will generate a dist tarball and upload it to CPAN using cpan-upload.
448 It will also create a git tag for the release, and push the tag and branch.
452 If release is run with FAKE_RELEASE=1 set, it will skip uploading to CPAN and
453 pushing to git. A release commit will still be created and tagged locally.
457 Performs a number of checks on the files and repository, ensuring it is in a
458 sane state to do a release. The checks are:
462 =item * All version numbers match
464 =item * The F<MANIFEST> file is up to date
466 =item * The branch is correct
468 =item * There is no existing tag for the version
470 =item * There are no unmerged upstream changes
472 =item * There are no outstanding local changes
474 =item * There is an appropriate staged Changes heading
476 =item * cpan-upload is available
482 Test the dist preparing for a release. This generates a dist dir and runs the
483 tests from inside it. This ensures all appropriate files are included inside
484 the dist. C<RELEASE_TESTING> will be set in the environment.
488 Adds an appropriate changelog heading for the release, and prompts to stage the
493 Bumps the version number. This will try to preserve the length and format of
494 the version number. The least significant digit will be incremented. Versions
495 with underscores will preserve the underscore in the same position.
497 Optionally accepts a C<V> option to set the version to a specific value.
499 The version changes will automatically be committed. Unstaged modifications to
500 the files will be left untouched.
504 The V option will be passed along to the version bumping script. It can accept
505 a space separated list of options, including an explicit version number.
513 Updates version numbers even if they do not match the current expected version
518 Attempts to convert the updated version to a stable version, removing any
523 Attempts to convert the updated version to an alpha version, adding an
524 underscore in an appropriate place.
530 Like bump, but increments the minor segment of the version. This will treat
531 numeric versions as x.yyyzzz format, incrementing the yyy segment.
535 Like bumpminor, but bumping the major segment.
539 Updates Distar and re-runs C<perl Makefile.PL>
543 IRC: #web-simple on irc.perl.org
545 Git repository: L<git://git.shadowcat.co.uk/p5sagit/Distar>
547 Git browser: L<http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=p5sagit/Distar.git;a=summary>
551 mst - Matt S. Trout (cpan:MSTROUT) <mst@shadowcat.co.uk>
555 haarg - Graham Knop (cpan:HAARG) <haarg@cpan.org>
557 ether - Karen Etheridge (cpan:ETHER) <ether@cpan.org>
559 frew - Arthur Axel "fREW" Schmidt (cpan:FREW) <frioux@gmail.com>
561 Mithaldu - Christian Walde (cpan:MITHALDU) <walde.christian@googlemail.com>
565 Copyright (c) 2011-2015 the Distar L</AUTHOR> and L</CONTRIBUTORS>
570 This library is free software and may be distributed under the same terms
571 as perl itself. See L<http://dev.perl.org/licenses/>.