no_index t and xt if not specified
[p5sagit/Distar.git] / lib / Distar.pm
CommitLineData
42e08a83 1package Distar;
362ec4af 2use strict;
3use warnings FATAL => 'all';
42e08a83 4use base qw(Exporter);
ca3b3b8a 5use ExtUtils::MakeMaker ();
6use ExtUtils::MM ();
8aac77fc 7use File::Spec ();
42e08a83 8
f6286f60 9our $VERSION = '0.002000';
37e295d8 10$VERSION = eval $VERSION;
11
e076eedb 12my $MM_VER = eval $ExtUtils::MakeMaker::VERSION;
13
42e08a83 14our @EXPORT = qw(
be032607 15 author manifest_include readme_generator
42e08a83 16);
17
18sub import {
2852faf3 19 strict->import;
20 warnings->import(FATAL => 'all');
42e08a83 21 shift->export_to_level(1,@_);
22}
23
e076eedb 24sub author {
25 our $Author = shift;
26 $Author = [ $Author ]
27 if !ref $Author;
28}
42e08a83 29
30our @Manifest = (
31 'lib' => '.pm',
7b9318ce 32 'lib' => '.pod',
42e08a83 33 't' => '.t',
34 't/lib' => '.pm',
35 'xt' => '.t',
36 'xt/lib' => '.pm',
1d950b4a 37 '' => qr{[^/]*\.PL},
f1ef306e 38 '' => qr{Changes|MANIFEST|README|LICENSE|META\.yml},
42e08a83 39 'maint' => qr{[^.].*},
40);
41
42sub manifest_include {
43 push @Manifest, @_;
44}
45
6e83c113 46sub readme_generator {
f6286f60 47 die "readme_generator unsupported" if @_ && $_[0];
6e83c113 48}
49
42e08a83 50sub write_manifest_skip {
0fa73f76 51 my ($mm) = @_;
42e08a83 52 my @files = @Manifest;
53 my @parts;
54 while (my ($dir, $spec) = splice(@files, 0, 2)) {
55 my $re = ($dir ? $dir.'/' : '').
56 ((ref($spec) eq 'Regexp')
57 ? $spec
58 : !ref($spec)
59 ? ".*\Q${spec}\E"
a3e39afd 60 # print ref as well as stringification in case of overload ""
42e08a83 61 : die "spec must be string or regexp, was: ${spec} (${\ref $spec})");
62 push @parts, $re;
63 }
0fa73f76 64 my $dist_name = $mm->{DISTNAME};
65 my $include = join '|', map "${_}\$", @parts;
66 my $final = "^(?:\Q$dist_name\E-v?[0-9_.]+/|(?!$include))";
19916679 67 open my $skip, '>', 'MANIFEST.SKIP'
68 or die "can't open MANIFEST.SKIP: $!";
42e08a83 69 print $skip "${final}\n";
70 close $skip;
71}
72
a8fd5adc 73sub _clone {
74 map {
75 if (ref eq 'HASH') {
76 my $struct = $_;
77 { map +($_ => _clone($struct->{$_})), keys %$struct };
78 }
79 elsif (ref eq 'ARRAY') {
80 [ _clone(@$_) ];
81 }
82 else {
83 $_;
84 }
85 } @_;
86}
87
ca3b3b8a 88{
89 package Distar::MM;
9af7ff2e 90 our @ISA = @MM::ISA;
91 @MM::ISA = (__PACKAGE__);
ca3b3b8a 92
93 sub new {
94 my ($class, $args) = @_;
4762e03a 95 my %test = %{$args->{test}||{}};
96 my $tests = $test{TESTS} || 't/*.t';
97 $tests !~ /\b\Q$_\E\b/ and $tests .= " $_"
98 for 'xt/*.t', 'xt/*/*.t';
99 $test{TESTS} = $tests;
ca3b3b8a 100 return $class->SUPER::new({
180e908e 101 LICENSE => 'perl_5',
53e1282f 102 MIN_PERL_VERSION => '5.006',
15f10a18 103 ($Distar::Author ? (
d0f7a429 104 AUTHOR => ($MM_VER >= 6.5702 ? $Distar::Author : join(', ', @$Distar::Author)),
105 ) : ()),
1f136c70 106 (exists $args->{ABSTRACT} ? () : (ABSTRACT_FROM => $args->{VERSION_FROM})),
4a4bb570 107 %$args,
4762e03a 108 test => \%test,
0a3fdb23 109 realclean => { FILES => (
110 ($args->{realclean}{FILES}||'')
111 . ' Distar/ MANIFEST.SKIP MANIFEST MANIFEST.bak'
112 ) },
ca3b3b8a 113 });
114 }
115
a8fd5adc 116 sub metafile_data {
117 my $self = shift;
118 my $meta = { Distar::_clone($self->SUPER::metafile_data(@_) };
119
120 my $spec_ver = ($meta->{'meta-spec'} && $meta->{'meta-spec'}{version} || 1.4;
121 my $resources = $meta->{resources} ||= {};
122
3584e66b 123 {
124 my $no_index_dir = ${ $meta->{no_index}||={} }{directory} ||= [];
125 my %seen;
126 @$no_index_dir = grep !$seen{$_}++, @$no_index_dir, grep -d, qw(t xt);
127 }
128
a8fd5adc 129 %$meta;
130 }
131
eddb1ce6 132 sub flush {
133 my $self = shift;
5371ff52 134 `git ls-files --error-unmatch MANIFEST.SKIP 2>&1`;
135 my $maniskip_tracked = !$?;
136
137 Distar::write_manifest_skip($self)
138 unless $maniskip_tracked;
eddb1ce6 139 $self->SUPER::flush(@_);
140 }
141
7efea54c 142 sub special_targets {
143 my $self = shift;
144 my $targets = $self->SUPER::special_targets(@_);
f57289e3 145 my $phony_targets = join ' ', qw(
146 preflight
7b418de8 147 check-version
5baee32c 148 check-manifest
b0780d0a 149 check-cpan-upload
f57289e3 150 releasetest
151 release
152 readmefile
153 distmanicheck
154 nextrelease
155 refresh
156 bump
157 bumpmajor
158 bumpminor
159 );
160 $targets =~ s/^(\.PHONY *:.*)/$1 $phony_targets/m;
7efea54c 161 $targets;
162 }
163
42d0b436 164 sub init_dist {
165 my $self = shift;
166 my $pre_tar = $self->{TAR};
167 my $out = $self->SUPER::init_dist(@_);
168
169 my $tar = $self->{TAR};
170 my $gtar;
171 my $set_user;
172 my $version = `$tar --version`;
173 if ($version =~ /GNU tar/) {
174 $gtar = 1;
175 }
176 elsif (!$pre_tar && `gtar --version`) {
177 $tar = 'gtar';
178 $gtar = 1;
179 }
180 my $tarflags = $self->{TARFLAGS};
181 if (my ($flags) = $tarflags =~ /^-?([cvhlLf]+)$/) {
182 if ($flags =~ s/c// && $flags =~ s/f//) {
183 $tarflags = '--format=ustar -c'.$flags.'f';
184 if ($gtar) {
185 $tarflags = '--owner=0 --group=0 '.$tarflags;
186 $set_user = 1;
187 }
188 }
189 }
190
191 if (!$set_user) {
192 my $warn = '';
193 if ($> >= 2**21) {
194 $warn .= "uid ($>)";
195 }
196 if ($) >= 2**21) {
197 $warn .= ($warn ? ' and ' : '').'gid('.(0+$)).')';
198 }
199 if ($warn) {
200 warn "$warn too large! Max is ".(2**21-1).".\n"
201 ."Dist creation will likely fail. Install GNU tar to work around.\n";
202 }
203 }
204
205 $self->{TAR} = $tar;
206 $self->{TARFLAGS} = $tarflags;
207
208 $out;
209 }
210
ee801c00 211 sub tarfile_target {
212 my $self = shift;
213 my $out = $self->SUPER::tarfile_target(@_);
214 my $verify = <<'END_FRAG';
215 $(ABSPERLRUN) $(HELPERS)/verify-tarball $(DISTVNAME).tar $(DISTVNAME)/MANIFEST --tar="$(TAR)"
216END_FRAG
217 $out =~ s{(\$\(TAR\).*\n)}{$1$verify};
218 $out;
219 }
220
ca3b3b8a 221 sub dist_test {
222 my $self = shift;
8ab5ea70 223
66a2ecde 224 my $include = '';
225 if (open my $fh, '<', 'maint/Makefile.include') {
226 $include = "\n# --- Makefile.include:\n\n" . do { local $/; <$fh> };
227 $include =~ s/\n?\z/\n/;
228 }
b0401762 229
66a2ecde 230 my @bump_targets =
231 grep { $include !~ /^bump$_(?: +\w+)*:/m } ('', 'minor', 'major');
81d518f6 232
8aac77fc 233 my $distar = File::Spec->catdir(
234 File::Spec->catpath((File::Spec->splitpath(__FILE__))[0,1], ''),
235 File::Spec->updir,
236 );
237 my $helpers = File::Spec->catdir($distar, 'helpers');
238
66a2ecde 239 my %vars = (
8aac77fc 240 DISTAR => $self->quote_literal($distar),
241 HELPERS => $self->quote_literal($helpers),
2c75254f 242 REMAKE => join(' ', '$(PERLRUN)', '-I$(DISTAR)/lib', '-mDistar', 'Makefile.PL', map { $self->quote_literal($_) } @ARGV),
190976f8 243 BRANCH => $self->{BRANCH} ||= 'master',
9b920c5c 244 CHANGELOG => $self->{CHANGELOG} ||= 'Changes',
b0780d0a 245 DEV_NULL_STDOUT => ($self->{DEV_NULL} ? '>'.File::Spec->devnull : ''),
f0bbeff7 246 DISTTEST_MAKEFILE_PARAMS => '',
66a2ecde 247 );
248
f0bbeff7 249 my $dist_test = $self->SUPER::dist_test(@_);
250 $dist_test =~ s/(\bMakefile\.PL\b)/$1 \$(DISTTEST_MAKEFILE_PARAMS)/;
251
66a2ecde 252 join('',
f0bbeff7 253 $dist_test,
66a2ecde 254 "\n\n# --- Distar section:\n\n",
255 (map "$_ = $vars{$_}\n", sort keys %vars),
256 <<'END',
81d518f6 257
b0780d0a 258preflight: check-version check-manifest check-cpan-upload
8aac77fc 259 $(ABSPERLRUN) $(HELPERS)/preflight $(VERSION) --changelog=$(CHANGELOG) --branch=$(BRANCH)
7b418de8 260check-version:
8aac77fc 261 $(ABSPERLRUN) $(HELPERS)/check-version $(VERSION) $(TO_INST_PM) $(EXE_FILES)
5baee32c 262check-manifest:
8aac77fc 263 $(ABSPERLRUN) $(HELPERS)/check-manifest
b0780d0a 264check-cpan-upload:
265 $(NOECHO) cpan-upload -h $(DEV_NULL_STDOUT)
3e632431 266releasetest:
f0bbeff7 267 $(MAKE) disttest RELEASE_TESTING=1 DISTTEST_MAKEFILE_PARAMS="PREREQ_FATAL=1" PASTHRU="$(PASTHRU) TEST_FILES=\"$(TEST_FILES)\""
49beea48 268release: preflight
269 $(MAKE) releasetest
42e08a83 270 git commit -a -m "Release commit for $(VERSION)"
401ece0b 271 git tag v$(VERSION) -m "release v$(VERSION)"
49beea48 272 $(RM_RF) $(DISTVNAME)
273 $(MAKE) $(DISTVNAME).tar$(SUFFIX)
263b723e 274 $(NOECHO) $(MAKE) pushrelease FAKE_RELEASE=$(FAKE_RELEASE)
275pushrelease ::
276 $(NOECHO) $(NOOP)
277pushrelease$(FAKE_RELEASE) ::
65a1f7d9 278 cpan-upload $(DISTVNAME).tar$(SUFFIX)
2c636792 279 git push origin v$(VERSION) HEAD
5154970c 280distdir: readmefile
75f8311c 281readmefile: create_distdir
5c5deb0a 282 $(NOECHO) $(TEST_F) $(DISTVNAME)/README || $(MAKE) $(DISTVNAME)/README
f6286f60 283$(DISTVNAME)/README: $(VERSION_FROM)
284 $(NOECHO) $(MKPATH) $(DISTVNAME)
285 pod2text $(VERSION_FROM) >$(DISTVNAME)/README
8aac77fc 286 $(NOECHO) $(ABSPERLRUN) $(HELPERS)/add-to-manifest -d $(DISTVNAME) README
7934d553 287distsignature: readmefile
de048fa8 288disttest: distmanicheck
792c9e91 289distmanicheck: create_distdir
290 cd $(DISTVNAME) && $(ABSPERLRUN) "-MExtUtils::Manifest=manicheck" -e "exit manicheck"
e7a78651 291nextrelease:
8aac77fc 292 $(ABSPERLRUN) $(HELPERS)/add-changelog-heading --git $(VERSION) $(CHANGELOG)
0edb27b8 293refresh:
41e593ee 294 cd $(DISTAR) && git pull || $(TRUE)
e9f66489 295 $(RM_F) $(FIRST_MAKEFILE)
0edb27b8 296 $(REMAKE)
8ab5ea70 297END
66a2ecde 298 map(sprintf(<<'END', "bump$_", ($_ || '$(V)')), @bump_targets),
299%s:
8aac77fc 300 $(ABSPERLRUN) $(HELPERS)/bump-version --git $(VERSION) %s
66a2ecde 301 $(RM_F) $(FIRST_MAKEFILE)
302 $(REMAKE)
42e08a83 303END
66a2ecde 304 $include,
305 "\n",
306 );
42e08a83 307 }
308}
309
42e08a83 3101;
edb4539a 311__END__
312
313=head1 NAME
314
315Distar - Additions to ExtUtils::MakeMaker for dist authors
316
317=head1 SYNOPSIS
318
319F<Makefile.PL>:
320
321 use ExtUtils::MakeMaker;
83e12da3 322 (do './maint/Makefile.PL.include' or die $@) unless -f 'META.yml';
edb4539a 323
324 WriteMakefile(...);
325
326F<maint/Makefile.PL.include>:
327
328 BEGIN { -e 'Distar' or system("git clone git://git.shadowcat.co.uk/p5sagit/Distar.git") }
329 use lib 'Distar/lib';
330 use Distar 0.001;
331
332 author 'A. U. Thor <author@cpan.org>';
333
334 manifest_include t => 'test-helper.pl';
335 manifest_include corpus => '.txt';
336
337make commmands:
338
339 $ perl Makefile.PL
340 $ make bump # bump version
341 $ make bump V=2.000000 # bump to specific version
342 $ make bumpminor # bump minor version component
343 $ make bumpmajor # bump major version component
344 $ make nextrelease # add version heading to Changes file
345 $ make releasetest # build dist and test (with xt/ and RELEASE_TESTING=1)
346 $ make preflight # check that repo and file state is release ready
83e12da3 347 $ make release # check releasetest and preflight, commits and tags,
348 # builds and uploads to CPAN, and pushes commits and
349 # tag
350 $ make release FAKE_RELEASE=1
351 # builds a release INCLUDING committing and tagging,
352 # but does not upload to cpan or push anything to git
edb4539a 353
354=head1 DESCRIPTION
355
356L<ExtUtils::MakeMaker> works well enough as development tool for
357builting and testing, but using it to release is annoying and error prone.
358Distar adds just enough to L<ExtUtils::MakeMaker> for it to be a usable dist
359author tool. This includes extra commands for releasing and safety checks, and
360automatic generation of some files. It doesn't require any non-core modules and
361is compatible with old versions of perl.
362
363=head1 FUNCTIONS
364
365=head2 author( $author )
366
367Set the author to include in generated META files. Can be a single entry, or
368an arrayref.
369
370=head2 manifest_include( $dir, $pattern )
371
372Add a pattern to include files in the MANIFEST file, and thus in the generated
373dist files.
374
375The pattern can be either a regex, or a path suffix. It will be applied to the
376full path past the directory specified.
377
378The default files that are always included are: F<.pm> and F<.pod> files in
379F<lib>, F<.t> files in F<t> and F<xt>, F<.pm> files in F<t/lib> and F<xt/lib>,
380F<Changes>, F<MANIFEST>, F<README>, F<LICENSE>, F<META.yml>, and F<.PL> files in
381the dist root, and all files in F<maint>.
382
383=head1 AUTOGENERATED FILES
384
385=over 4
386
387=item F<MANIFEST.SKIP>
388
389The F<MANIFEST.SKIP> will be automatically generated to exclude any files not
390explicitly allowed via C<manifest_include> or the included defaults. It will be
391created (or updated) at C<perl Makefile.PL> time.
392
393=item F<README>
394
395The F<README> file will be generated at dist generation time, inside the built
396dist. It will be generated using C<pod2text> on the main module.
397
398If a F<README> file exists in the repo, it will be used directly instead of
399generating the file.
400
401=back
402
403=head1 MAKE COMMMANDS
404
405=head2 test
406
407test will be adjusted to include F<xt/> tests by default. This will only apply
408for authors, not users installing from CPAN.
409
410=head2 release
411
412Releases the dist. Before releasing, checks will be done on the dist using the
413C<preflight> and C<releasetest> commands.
414
415Releasing will generate a dist tarball and upload it to CPAN using cpan-upload.
416It will also create a git tag for the release, and push the tag and branch.
417
83e12da3 418=head3 FAKE_RELEASE
263b723e 419
420If release is run with FAKE_RELEASE=1 set, it will skip uploading to CPAN and
421pushing to git. A release commit will still be created and tagged locally.
422
edb4539a 423=head2 preflight
424
425Performs a number of checks on the files and repository, ensuring it is in a
426sane state to do a release. The checks are:
427
428=over 4
429
430=item * All version numbers match
431
432=item * The F<MANIFEST> file is up to date
433
434=item * The branch is correct
435
436=item * There is no existing tag for the version
437
438=item * There are no unmerged upstream changes
439
440=item * There are no outstanding local changes
441
442=item * There is an appropriate staged Changes heading
443
444=item * cpan-upload is available
445
446=back
447
448=head2 releasetest
449
450Test the dist preparing for a release. This generates a dist dir and runs the
451tests from inside it. This ensures all appropriate files are included inside
452the dist. C<RELEASE_TESTING> will be set in the environment.
453
454=head2 nextrelease
455
456Adds an appropriate changelog heading for the release, and prompts to stage the
457change.
458
459=head2 bump
460
461Bumps the version number. This will try to preserve the length and format of
83e12da3 462the version number. The least significant digit will be incremented. Versions
463with underscores will preserve the underscore in the same position.
edb4539a 464
465Optionally accepts a C<V> option to set the version to a specific value.
466
467The version changes will automatically be committed. Unstaged modifications to
468the files will be left untouched.
469
83e12da3 470=head3 V
471
472The V option will be passed along to the version bumping script. It can accept
473a space separated list of options, including an explicit version number.
474
475Options:
476
477=over 4
478
479=item --force
480
481Updates version numbers even if they do not match the current expected version
482number.
483
484=item --stable
485
486Attempts to convert the updated version to a stable version, removing any
487underscore.
488
489=item --alpha
490
491Attempts to convert the updated version to an alpha version, adding an
492underscore in an appropriate place.
493
494=back
495
edb4539a 496=head2 bumpminor
497
498Like bump, but increments the minor segment of the version. This will treat
499numeric versions as x.yyyzzz format, incrementing the yyy segment.
500
501=head2 bumpmajor
502
503Like bumpminor, but bumping the major segment.
504
505=head2 refresh
506
507Updates Distar and re-runs C<perl Makefile.PL>
508
509=head1 SUPPORT
510
511IRC: #web-simple on irc.perl.org
512
513Git repository: L<git://git.shadowcat.co.uk/p5sagit/Distar>
514
515Git browser: L<http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=p5sagit/Distar.git;a=summary>
516
517=head1 AUTHOR
518
519mst - Matt S. Trout (cpan:MSTROUT) <mst@shadowcat.co.uk>
520
521=head1 CONTRIBUTORS
522
523haarg - Graham Knop (cpan:HAARG) <haarg@cpan.org>
524
83e12da3 525ether - Karen Etheridge (cpan:ETHER) <ether@cpan.org>
edb4539a 526
527frew - Arthur Axel "fREW" Schmidt (cpan:FREW) <frioux@gmail.com>
528
529Mithaldu - Christian Walde (cpan:MITHALDU) <walde.christian@googlemail.com>
530
531=head1 COPYRIGHT
532
533Copyright (c) 2011-2015 the Distar L</AUTHOR> and L</CONTRIBUTORS>
534as listed above.
535
536=head1 LICENSE
537
538This library is free software and may be distributed under the same terms
539as perl itself. See L<http://dev.perl.org/licenses/>.
540
541=cut