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