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;
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";
81 sub _gen_make_section {
87 s/^\t?/\t/mg for @lines;
92 s/\n?\z/\n/ for @lines;
98 my ($class, $args) = @_;
99 my %test = %{$args->{test}||{}};
100 my $tests = $test{TESTS} || 't/*.t';
101 $tests !~ /\b\Q$_\E\b/ and $tests .= " $_"
102 for 'xt/*.t', 'xt/*/*.t';
103 $test{TESTS} = $tests;
104 return $class->SUPER::new({
106 MIN_PERL_VERSION => '5.006',
108 AUTHOR => ($MM_VER >= 6.5702 ? $Distar::Author : join(', ', @$Distar::Author)),
110 (exists $args->{ABSTRACT} ? () : (ABSTRACT_FROM => $args->{VERSION_FROM})),
113 realclean => { FILES => (
114 ($args->{realclean}{FILES}||'')
115 . ' Distar/ MANIFEST.SKIP MANIFEST MANIFEST.bak'
122 `git ls-files --error-unmatch MANIFEST.SKIP 2>&1`;
123 my $maniskip_tracked = !$?;
125 Distar::write_manifest_skip($self)
126 unless $maniskip_tracked;
127 $self->SUPER::flush(@_);
130 sub special_targets {
132 my $targets = $self->SUPER::special_targets(@_);
133 my $phony_targets = join ' ', qw(
148 $targets =~ s/^(\.PHONY *:.*)/$1 $phony_targets/m;
154 my $out = $self->SUPER::init_dist(@_);
156 my $dn = File::Spec->devnull;
157 my $tar = $self->{TAR};
159 my $tarflags = $self->{TARFLAGS};
160 if (my ($flags) = $tarflags =~ /^-?([cvhlLf]+)$/) {
161 if ($flags =~ s/c// && $flags =~ s/f//) {
162 $tarflags = '--format=ustar -c'.$flags.'f';
164 for my $options ('--owner=0 --group=0', '--uid=0 --gid=0') {
165 my $try = `$tar -c $options "$me" 2>$dn`;
167 $tarflags = "$options $tarflags";
175 $self->{TARFLAGS} = $tarflags;
184 # default setup for Distar involves checking it out as a subdirectory at
185 # the top of the dist. Without NORECURS, EUMM would try to include
186 # Distar's Makefile.PL. Prevent EUMM from looking inside.
188 if $path eq 'Distar';
190 $self->SUPER::libscan(@_);
195 my $out = $self->SUPER::tarfile_target(@_);
196 my $verify = _gen_make_section([
197 '$(ABSPERLRUN) $(HELPERS)/verify-tarball $(DISTVNAME).tar $(DISTVNAME)/MANIFEST --tar="$(TAR)"',
199 $out =~ s{(\$\(TAR\).*\n)}{$1$verify};
206 my $distar_lib = File::Basename::dirname(__FILE__);
207 my $helpers = File::Spec->catdir($distar_lib, 'Distar', 'helpers');
209 my $licenses = $self->{LICENSE} || $self->{META_ADD}{license} || $self->{META_MERGE}{license};
210 my $authors = $self->{AUTHOR};
211 $_ = ref $_ ? $_ : [$_ || ()]
212 for $licenses, $authors;
215 DISTAR_LIB => $self->quote_literal($distar_lib),
216 HELPERS => $self->quote_literal($helpers),
217 REMAKE => join(' ', '$(PERLRUN)', '-I$(DISTAR_LIB)', '-MDistar', 'Makefile.PL', map { $self->quote_literal($_) } @ARGV),
218 BRANCH => $self->{BRANCH} ||= 'master',
219 CHANGELOG => $self->{CHANGELOG} ||= 'Changes',
220 DEV_NULL_STDOUT => ($self->{DEV_NULL} ? '>'.File::Spec->devnull : ''),
221 DISTTEST_MAKEFILE_PARAMS => '',
222 AUTHORS => $self->quote_literal(join(', ', @$authors)),
223 LICENSES => join(' ', map $self->quote_literal($_), @$licenses),
224 GET_CHANGELOG => '$(ABSPERLRUN) $(HELPERS)/get-changelog $(VERSION) $(CHANGELOG)',
226 -e File::Spec->catdir($distar_lib, File::Spec->updir, '.git')
227 ? 'git -C $(DISTAR_LIB) pull'
228 : '$(ECHO) "Distar code is not in a git repo, unable to update!"'
232 my $dist_test = $self->SUPER::dist_test(@_);
233 $dist_test =~ s/(\bMakefile\.PL\b)/$1 \$(DISTTEST_MAKEFILE_PARAMS)/;
236 if (open my $fh, '<', 'maint/Makefile.include') {
237 $include = do { local $/; <$fh> };
238 $include =~ s/\n?\z/\n/;
244 'preflight: check-version check-manifest check-cpan-upload' => [
245 '$(ABSPERLRUN) $(HELPERS)/preflight $(VERSION) --changelog=$(CHANGELOG) --branch=$(BRANCH)',
247 'check-version:' => [
248 '$(ABSPERLRUN) $(HELPERS)/check-version $(VERSION) $(TO_INST_PM) $(EXE_FILES)',
250 'check-manifest:' => [
251 '$(ABSPERLRUN) $(HELPERS)/check-manifest',
253 'check-cpan-upload:' => [
254 '$(NOECHO) cpan-upload -h $(DEV_NULL_STDOUT)',
257 '$(MAKE) disttest RELEASE_TESTING=1 DISTTEST_MAKEFILE_PARAMS="PREREQ_FATAL=1" PASTHRU="$(PASTHRU) TEST_FILES=\"$(TEST_FILES)\""',
258 '$(NOECHO) $(TEST_F) $(DISTVNAME)/LICENSE || $(ECHO) "Failed to generate $(DISTVNAME)/LICENSE!" >&2',
259 '$(NOECHO) $(TEST_F) $(DISTVNAME)/LICENSE',
261 'release: preflight' => [
262 '$(MAKE) releasetest',
263 '$(GET_CHANGELOG) -p"Release commit for $(VERSION)" | git commit -a -F -',
264 '$(GET_CHANGELOG) -p"release v$(VERSION)" | git tag -a -F - "v$(VERSION)"',
265 '$(RM_RF) $(DISTVNAME)',
266 '$(MAKE) $(DISTVNAME).tar$(SUFFIX)',
267 '$(NOECHO) $(MAKE) pushrelease FAKE_RELEASE=$(FAKE_RELEASE)',
269 'pushrelease ::' => [
272 'pushrelease$(FAKE_RELEASE) ::' => [
273 'cpan-upload $(DISTVNAME).tar$(SUFFIX)',
274 'git push origin v$(VERSION) HEAD',
276 'distdir: readmefile licensefile',
277 'readmefile: create_distdir' => [
278 '$(NOECHO) $(TEST_F) $(DISTVNAME)/README || $(MAKE) $(DISTVNAME)/README',
280 '$(DISTVNAME)/README: $(VERSION_FROM)' => [
281 '$(NOECHO) $(MKPATH) $(DISTVNAME)',
282 'pod2text $(VERSION_FROM) >$(DISTVNAME)/README',
283 '$(NOECHO) $(ABSPERLRUN) $(HELPERS)/add-to-manifest -d $(DISTVNAME) README',
285 'distsignature: readmefile licensefile',
286 'licensefile: create_distdir' => [
287 '$(NOECHO) $(TEST_F) $(DISTVNAME)/LICENSE || $(MAKE) $(DISTVNAME)/LICENSE || $(TRUE)',
289 '$(DISTVNAME)/LICENSE: Makefile.PL' => [
290 '$(NOECHO) $(MKPATH) $(DISTVNAME)',
291 '$(ABSPERLRUN) $(HELPERS)/generate-license -o $(DISTVNAME)/LICENSE $(AUTHORS) $(LICENSES)',
292 '$(NOECHO) $(ABSPERLRUN) $(HELPERS)/add-to-manifest -d $(DISTVNAME) LICENSE',
294 'disttest: distmanicheck',
295 'distmanicheck: create_distdir' => [
296 $self->cd('$(DISTVNAME)',
297 '$(ABSPERLRUN) "-MExtUtils::Manifest=manicheck" -e "exit manicheck"',
301 '$(ABSPERLRUN) $(HELPERS)/add-changelog-heading --git $(VERSION) $(CHANGELOG)',
305 '$(RM_F) $(FIRST_MAKEFILE)',
311 grep { $include !~ /^bump$_(?: +\w+)*:/m } ('', 'minor', 'major');
315 '$(ABSPERLRUN) $(HELPERS)/bump-version --git $(VERSION) '.($_ || '$(V)'),
316 '$(RM_F) $(FIRST_MAKEFILE)',
323 "\n\n# --- Distar section:\n\n",
324 (map "$_ = $vars{$_}\n", sort keys %vars),
326 _gen_make_section(@out),
329 "# --- Makefile.include:\n",
344 Distar - Additions to ExtUtils::MakeMaker for dist authors
350 use ExtUtils::MakeMaker;
351 (do './maint/Makefile.PL.include' or die $@) unless -f 'META.yml';
355 F<maint/Makefile.PL.include>:
357 BEGIN { -e 'Distar' or system("git clone git://git.shadowcat.co.uk/p5sagit/Distar.git") }
358 use lib 'Distar/lib';
361 author 'A. U. Thor <author@cpan.org>';
363 manifest_include t => 'test-helper.pl';
364 manifest_include corpus => '.txt';
369 $ make bump # bump version
370 $ make bump V=2.000000 # bump to specific version
371 $ make bumpminor # bump minor version component
372 $ make bumpmajor # bump major version component
373 $ make nextrelease # add version heading to Changes file
374 $ make releasetest # build dist and test (with xt/ and RELEASE_TESTING=1)
375 $ make preflight # check that repo and file state is release ready
376 $ make release # check releasetest and preflight, commits and tags,
377 # builds and uploads to CPAN, and pushes commits and
379 $ make release FAKE_RELEASE=1
380 # builds a release INCLUDING committing and tagging,
381 # but does not upload to cpan or push anything to git
385 L<ExtUtils::MakeMaker> works well enough as development tool for
386 builting and testing, but using it to release is annoying and error prone.
387 Distar adds just enough to L<ExtUtils::MakeMaker> for it to be a usable dist
388 author tool. This includes extra commands for releasing and safety checks, and
389 automatic generation of some files. It doesn't require any non-core modules and
390 is compatible with old versions of perl.
394 =head2 author( $author )
396 Set the author to include in generated META files. Can be a single entry, or
399 =head2 manifest_include( $dir, $pattern )
401 Add a pattern to include files in the MANIFEST file, and thus in the generated
404 The pattern can be either a regex, or a path suffix. It will be applied to the
405 full path past the directory specified.
407 The default files that are always included are: F<.pm> and F<.pod> files in
408 F<lib>, F<.t> files in F<t> and F<xt>, F<.pm> files in F<t/lib> and F<xt/lib>,
409 F<Changes>, F<MANIFEST>, F<README>, F<LICENSE>, F<META.yml>, and F<.PL> files in
410 the dist root, and all files in F<maint>.
412 =head1 AUTOGENERATED FILES
416 =item F<MANIFEST.SKIP>
418 The F<MANIFEST.SKIP> will be automatically generated to exclude any files not
419 explicitly allowed via C<manifest_include> or the included defaults. It will be
420 created (or updated) at C<perl Makefile.PL> time.
424 The F<README> file will be generated at dist generation time, inside the built
425 dist. It will be generated using C<pod2text> on the main module.
427 If a F<README> file exists in the repo, it will be used directly instead of
432 =head1 MAKE COMMMANDS
436 test will be adjusted to include F<xt/> tests by default. This will only apply
437 for authors, not users installing from CPAN.
441 Releases the dist. Before releasing, checks will be done on the dist using the
442 C<preflight> and C<releasetest> commands.
444 Releasing will generate a dist tarball and upload it to CPAN using cpan-upload.
445 It will also create a git tag for the release, and push the tag and branch.
449 If release is run with FAKE_RELEASE=1 set, it will skip uploading to CPAN and
450 pushing to git. A release commit will still be created and tagged locally.
454 Performs a number of checks on the files and repository, ensuring it is in a
455 sane state to do a release. The checks are:
459 =item * All version numbers match
461 =item * The F<MANIFEST> file is up to date
463 =item * The branch is correct
465 =item * There is no existing tag for the version
467 =item * There are no unmerged upstream changes
469 =item * There are no outstanding local changes
471 =item * There is an appropriate staged Changes heading
473 =item * cpan-upload is available
479 Test the dist preparing for a release. This generates a dist dir and runs the
480 tests from inside it. This ensures all appropriate files are included inside
481 the dist. C<RELEASE_TESTING> will be set in the environment.
485 Adds an appropriate changelog heading for the release, and prompts to stage the
490 Bumps the version number. This will try to preserve the length and format of
491 the version number. The least significant digit will be incremented. Versions
492 with underscores will preserve the underscore in the same position.
494 Optionally accepts a C<V> option to set the version to a specific value.
496 The version changes will automatically be committed. Unstaged modifications to
497 the files will be left untouched.
501 The V option will be passed along to the version bumping script. It can accept
502 a space separated list of options, including an explicit version number.
510 Updates version numbers even if they do not match the current expected version
515 Attempts to convert the updated version to a stable version, removing any
520 Attempts to convert the updated version to an alpha version, adding an
521 underscore in an appropriate place.
527 Like bump, but increments the minor segment of the version. This will treat
528 numeric versions as x.yyyzzz format, incrementing the yyy segment.
532 Like bumpminor, but bumping the major segment.
536 Updates Distar and re-runs C<perl Makefile.PL>
540 IRC: #web-simple on irc.perl.org
542 Git repository: L<git://git.shadowcat.co.uk/p5sagit/Distar>
544 Git browser: L<http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=p5sagit/Distar.git;a=summary>
548 mst - Matt S. Trout (cpan:MSTROUT) <mst@shadowcat.co.uk>
552 haarg - Graham Knop (cpan:HAARG) <haarg@cpan.org>
554 ether - Karen Etheridge (cpan:ETHER) <ether@cpan.org>
556 frew - Arthur Axel "fREW" Schmidt (cpan:FREW) <frioux@gmail.com>
558 Mithaldu - Christian Walde (cpan:MITHALDU) <walde.christian@googlemail.com>
562 Copyright (c) 2011-2015 the Distar L</AUTHOR> and L</CONTRIBUTORS>
567 This library is free software and may be distributed under the same terms
568 as perl itself. See L<http://dev.perl.org/licenses/>.