diff options
Diffstat (limited to 'inc')
-rw-r--r-- | inc/CheckAuthorDeps.pm | 52 | ||||
-rw-r--r-- | inc/CheckDelta.pm | 18 | ||||
-rw-r--r-- | inc/CheckReleaseType.pm | 37 | ||||
-rw-r--r-- | inc/Clean.pm | 50 | ||||
-rw-r--r-- | inc/ExtractInlineTests.pm | 58 | ||||
-rw-r--r-- | inc/GenerateDocs.pm | 57 | ||||
-rw-r--r-- | inc/GitUpToDate.pm | 52 | ||||
-rw-r--r-- | inc/MMHelper.pm | 79 | ||||
-rw-r--r-- | inc/MakeMaker.pm | 96 | ||||
-rw-r--r-- | inc/MyInline.pm | 98 | ||||
-rw-r--r-- | inc/SimpleAuthority.pm | 13 | ||||
-rw-r--r-- | inc/SimpleProvides.pm | 34 | ||||
-rw-r--r-- | inc/TestRelease.pm | 17 |
13 files changed, 661 insertions, 0 deletions
diff --git a/inc/CheckAuthorDeps.pm b/inc/CheckAuthorDeps.pm new file mode 100644 index 0000000..69f2dde --- /dev/null +++ b/inc/CheckAuthorDeps.pm @@ -0,0 +1,52 @@ +use strict; +use warnings; +package inc::CheckAuthorDeps; + +# our goal is to verify that the declared authordeps already reflect +# everything in configure + runtime prerequisites -- otherwise, we won't be +# able to bootstrap our built Moose for the purposes of running +# author/docGenerator.pl + +use Moose; +with 'Dist::Zilla::Role::AfterBuild'; + +sub after_build +{ + my $self = shift; + + # get our authordeps + require Dist::Zilla::Util::AuthorDeps; + Dist::Zilla::Util::AuthorDeps->VERSION(5.021); + + require CPAN::Meta::Requirements; + my $authordeps = CPAN::Meta::Requirements->new; + $authordeps->add_string_requirement(%$_) + foreach @{ Dist::Zilla::Util::AuthorDeps::extract_author_deps('.') }; + + # get our prereqs + my $prereqs = $self->zilla->prereqs; + + # merge prereqs into authordeps + my $merged_prereqs = CPAN::Meta::Requirements->new; + $merged_prereqs->add_requirements($authordeps); + $merged_prereqs->add_requirements($prereqs->requirements_for('configure', 'requires')); + $merged_prereqs->add_requirements($prereqs->requirements_for('runtime', 'requires')); + + # remove some false positives we know we already have fulfilled + $merged_prereqs->clear_requirement('ExtUtils::MakeMaker'); + $merged_prereqs->clear_requirement('Dist::CheckConflicts'); + + # the merged set should not be different than the original authordeps. + require Test::Deep; + my ($ok, $stack) = Test::Deep::cmp_details( + $authordeps->as_string_hash, + Test::Deep::superhashof($merged_prereqs->as_string_hash), + ); + + return if $ok; + + $self->log_fatal('authordeps does not have all prereqs found in configure, runtime prereqs: ' + . Test::Deep::deep_diag($stack)); +} + +1; diff --git a/inc/CheckDelta.pm b/inc/CheckDelta.pm new file mode 100644 index 0000000..d4ef142 --- /dev/null +++ b/inc/CheckDelta.pm @@ -0,0 +1,18 @@ +package inc::CheckDelta; +use Moose; + +with 'Dist::Zilla::Role::AfterBuild'; + +sub after_build { + my $self = shift; + + return unless $ENV{DZIL_RELEASING}; + + my ($delta) = grep { $_->name eq 'lib/Moose/Manual/Delta.pod' } + @{ $self->zilla->files }; + + die "Moose::Manual::Delta still contains \$NEXT" + if $delta->content =~ /\$NEXT/; +} + +1; diff --git a/inc/CheckReleaseType.pm b/inc/CheckReleaseType.pm new file mode 100644 index 0000000..6e79958 --- /dev/null +++ b/inc/CheckReleaseType.pm @@ -0,0 +1,37 @@ +package inc::CheckReleaseType; +use Moose; +with 'Dist::Zilla::Role::BeforeRelease'; + +# this is so I don't accidentally release 2.x<odd>xx without the --trial +# option, which has very nearly happened a few times. + +sub before_release +{ + my $self = shift; + my $version = $self->zilla->version; + + $version =~ m/^\d\.\d{4}$/ + or $self->log_fatal("version $version doesn't seem to conform to the normal specification!"); + + my $digit = substr($version, 3, 1); + if ($self->zilla->is_trial) + { + $digit % 2 == 1 + or $self->log_fatal('-TRIAL releases must be numbered 2.x{ODD}xx!'); + } + else + { + $digit % 2 == 0 + or $self->log_fatal('stable releases must be numbered 2.x{EVEN}xx!'); + + # Moose::Manual::Support says: + # 2.x{EVEN}00 must be January, April, July, October only. + if (substr($version, -2, 2) eq '00') + { + # month is 0..11 + my $month = (gmtime(time))[4]; + $month % 3 == 0 + or $self->log_fatal('2.x{EVEN}00 releases can only occur in January, April, July or October!'); + } + } +} diff --git a/inc/Clean.pm b/inc/Clean.pm new file mode 100644 index 0000000..a2a5563 --- /dev/null +++ b/inc/Clean.pm @@ -0,0 +1,50 @@ +package inc::Clean; +use Moose; + +with 'Dist::Zilla::Role::BeforeBuild', + 'Dist::Zilla::Role::AfterBuild'; +use Path::Tiny; +use File::pushd 'pushd'; +use Config; + +sub before_build { shift->_clean('.') } + +sub after_build { + my ($self, $opts) = @_; + + $self->_clean($opts->{build_root}); + + my $iter = path($opts->{build_root})->iterator({ recurse => 1 }); + my %found_files; + while (my $found_file = $iter->()) { + next if -d $found_file; + ++$found_files{ $found_file->relative($opts->{build_root}) }; + } + delete $found_files{$_->name} foreach @{ $self->zilla->files }; + + $self->log(join("\n", + "WARNING: Files were left behind in $opts->{build_root} that were not explicitly added:", + sort keys %found_files, + )) if keys %found_files; +} + +sub _clean { + my ($self, $build_dir) = @_; + + my $cwd = pushd $build_dir; + if (-e 'Makefile') { + + my $make = $Config{make} || 'make'; + + $self->log("Running $make distclean in $build_dir to clear out build cruft"); + my $pid = fork; + unless ($pid) { + close(STDIN); + close(STDOUT); + close(STDERR); + { exec("$^X Makefile.PL && $make distclean") } + die "couldn't exec: $!"; + } + waitpid($pid, 0) if $pid; + } +} diff --git a/inc/ExtractInlineTests.pm b/inc/ExtractInlineTests.pm new file mode 100644 index 0000000..e2cda0a --- /dev/null +++ b/inc/ExtractInlineTests.pm @@ -0,0 +1,58 @@ +package inc::ExtractInlineTests; + +use Moose; + +with 'Dist::Zilla::Role::FileGatherer'; + +use File::Find::Rule; +use inc::MyInline; +use Test::Inline; + +sub gather_files { + my $self = shift; + my $arg = shift; + + my $inline = Test::Inline->new( + verbose => 0, + ExtractHandler => 'My::Extract', + ContentHandler => 'My::Content', + OutputHandler => My::Output->new($self), + ); + + for my $pod ( + File::Find::Rule->file->name(qr/\.pod$/)->in('lib/Moose/Cookbook') ) { + $inline->add($pod); + } + + $inline->save; +} + +{ + package My::Output; + + sub new { + my $class = shift; + my $dzil = shift; + + return bless { dzil => $dzil }, $class; + } + + sub write { + my $self = shift; + my $name = shift; + my $content = shift; + + $name =~ s/^moose_cookbook_//; + + $self->{dzil}->add_file( + Dist::Zilla::File::InMemory->new( + name => "t/recipes/$name", + content => $content, + ) + ); + + return 1; + } +} + +1; diff --git a/inc/GenerateDocs.pm b/inc/GenerateDocs.pm new file mode 100644 index 0000000..27d4cf4 --- /dev/null +++ b/inc/GenerateDocs.pm @@ -0,0 +1,57 @@ +package inc::GenerateDocs; + +use Moose; +with 'Dist::Zilla::Role::FileGatherer', + 'Dist::Zilla::Role::AfterBuild', + 'Dist::Zilla::Role::FileInjector'; +use IPC::System::Simple qw(capturex); +use File::pushd; +use Path::Tiny; +use List::Util 'first'; + +my $filename = path(qw(lib Moose Manual Exceptions Manifest.pod)); + +sub gather_files { + my ($self, $arg) = @_; + + $self->add_file(Dist::Zilla::File::InMemory->new( + name => $filename->stringify, + # more to fill in later + content => <<'END_POD', +# PODNAME: Moose::Manual::Exceptions::Manifest +# ABSTRACT: Moose's Exception Types + +__END__ + +=for comment insert generated content here +END_POD + )); +} + +sub after_build { + my ($self, $opts) = @_; + my $build_dir = $opts->{build_root}; + + my $wd = File::pushd::pushd($build_dir); + unless ( -d 'blib' ) { + my @builders = @{ $self->zilla->plugins_with( -BuildRunner ) }; + die "no BuildRunner plugins specified" unless @builders; + $_->build for @builders; + die "no blib; failed to build properly?" unless -d 'blib'; + } + + # this must be run as a separate process because we need to use the new + # Moose we just generated, in order to introspect all the exception classes + $self->log('running author/docGenerator.pl...'); + my $text = capturex($^X, "author/docGenerator.pl"); + + my $file_obj = first { $_->name eq $filename } @{$self->zilla->files}; + + my $content = $file_obj->content; + my $pos = index($content, "\n\n=for comment insert generated content here"); + $file_obj->content(substr($content, 0, $pos) . "\n\n" . $text . substr($content, $pos, -1)); + + $filename->spew_raw($file_obj->encoded_content); +} + +1; diff --git a/inc/GitUpToDate.pm b/inc/GitUpToDate.pm new file mode 100644 index 0000000..b688d8a --- /dev/null +++ b/inc/GitUpToDate.pm @@ -0,0 +1,52 @@ +package inc::GitUpToDate; +use Moose; + +with 'Dist::Zilla::Role::BeforeBuild'; + +sub git { + if (wantarray) { + chomp(my @ret = qx{git $_[0]}); + return @ret; + } + else { + chomp(my $ret = qx{git $_[0]}); + return $ret; + } +} + +sub before_build { + my $self = shift; + + return unless $ENV{DZIL_RELEASING}; + + my $branch = git "symbolic-ref HEAD"; + die "Could not get the current branch" + unless $branch; + + $branch =~ s{refs/heads/}{}; + + $self->log("Ensuring branch $branch is up to date"); + + git "fetch origin"; + my $origin = git "rev-parse origin/$branch"; + my $head = git "rev-parse HEAD"; + + die "Branch $branch is not up to date (origin: $origin, HEAD: $head)" + if $origin ne $head; + + + # now also check that HEAD is current with the release branch + # that is, that the release branch is an ancestor commit of HEAD. + my $release_branch = ($self->zilla->plugin_named('Git::CheckFor::CorrectBranch')->release_branch)[0]; + foreach my $remote ('origin/', '') + { + my $release_commit = git "rev-parse ${remote}$release_branch"; + my $common_ancestor = git "merge-base $head $release_commit"; + + die "Branch $branch does not contain all commits from the current release branch ", + "(common ancestor for ${remote}$release_branch: $common_ancestor)" + if $common_ancestor ne $release_commit; + } +} + +1; diff --git a/inc/MMHelper.pm b/inc/MMHelper.pm new file mode 100644 index 0000000..7e340b9 --- /dev/null +++ b/inc/MMHelper.pm @@ -0,0 +1,79 @@ +package MMHelper; + +use strict; +use warnings; + +use Config; + +sub ccflags_dyn { + my $is_dev = shift; + + my $ccflags = q<( $Config::Config{ccflags} || '' ) . ' -I.'>; + if ($is_dev and ($Config{cc} !~ /^cl\b/i)) { + $ccflags .= q< . ' -Wall -Wdeclaration-after-statement'>; + } + + return $ccflags; +} + +sub ccflags_static { + my $is_dev = shift; + + return eval(ccflags_dyn($is_dev)); +} + +sub mm_args { + my ( @object, %xs ); + + for my $xs ( glob "xs/*.xs" ) { + ( my $c = $xs ) =~ s/\.xs$/.c/i; + ( my $o = $xs ) =~ s/\.xs$/\$(OBJ_EXT)/i; + + $xs{$xs} = $c; + push @object, $o; + } + + for my $c ( glob "*.c" ) { + ( my $o = $c ) =~ s/\.c$/\$(OBJ_EXT)/i; + push @object, $o; + } + + return ( + clean => { FILES => join( q{ }, @object ) }, + OBJECT => join( q{ }, @object ), + XS => \%xs, + ); +} + +sub my_package_subs { + return <<'EOP'; +{ +package MY; + +use Config; + +sub const_cccmd { + my $ret = shift->SUPER::const_cccmd(@_); + return q{} unless $ret; + + if ($Config{cc} =~ /^cl\b/i) { + warn 'you are using MSVC... we may not have gotten some options quite right.'; + $ret .= ' /Fo$@'; + } + else { + $ret .= ' -o $@'; + } + + return $ret; +} + +sub postamble { + return <<'EOF'; +$(OBJECT) : mop.h +EOF +} +} +EOP +} + +1; diff --git a/inc/MakeMaker.pm b/inc/MakeMaker.pm new file mode 100644 index 0000000..cd034ff --- /dev/null +++ b/inc/MakeMaker.pm @@ -0,0 +1,96 @@ +package inc::MakeMaker; + +use Moose; + +use lib 'inc'; + +use MMHelper; + +extends 'Dist::Zilla::Plugin::MakeMaker::Awesome'; + +override _build_MakeFile_PL_template => sub { + my $self = shift; + + my $tmpl = super(); + my $assert_compiler = <<'ASSERT_COMPILER'; +# Secondary compile testing via ExtUtils::CBuilder +sub can_xs { + # Do we have the configure_requires checker? + unless (eval 'require ExtUtils::CBuilder; ExtUtils::CBuilder->VERSION(0.27); 1') { + # They don't obey configure_requires, so it is + # someone old and delicate. Try to avoid hurting + # them by falling back to an older simpler test. + return can_cc(); + } + + return ExtUtils::CBuilder->new( quiet => 1 )->have_compiler; +} + +# can we locate a (the) C compiler +sub can_cc { + my @chunks = split(/ /, $Config::Config{cc}) or return; + + # $Config{cc} may contain args; try to find out the program part + while (@chunks) { + return can_run("@chunks") || (pop(@chunks), next); + } + + return; +} + +# check if we can run some command +sub can_run { + my ($cmd) = @_; + + return $cmd if -x $cmd; + if (my $found_cmd = MM->maybe_command($cmd)) { + return $found_cmd; + } + + for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') { + next if $dir eq ''; + my $abs = File::Spec->catfile($dir, $cmd); + return $abs if (-x $abs or $abs = MM->maybe_command($abs)); + } + + return; +} + +die 'This distribution requires a working compiler' unless can_xs(); + +ASSERT_COMPILER + + # splice in our stuff after the preamble bits + # TODO - MMA ought to make this easier. + $tmpl =~ m/use warnings;\n\n/g; + $tmpl = substr($tmpl, 0, pos($tmpl)) . $assert_compiler . substr($tmpl, pos($tmpl)); + + + # TODO: splice this in using 'around _build_WriteMakefile_args' + my $ccflags = MMHelper::ccflags_dyn(); + $tmpl =~ s/^(WriteMakefile\()/\$WriteMakefileArgs{CCFLAGS} = $ccflags;\n\n$1/m; + + return $tmpl . "\n\n" . MMHelper::my_package_subs(); +}; + +override _build_WriteMakefile_args => sub { + my $self = shift; + + my $args = super(); + + return { + %{$args}, + MMHelper::mm_args(), + }; +}; + +override test => sub { + my $self = shift; + + local $ENV{PERL5LIB} = join ':', + grep {defined} @ENV{ 'PERL5LIB', 'DZIL_TEST_INC' }; + + super(); +}; + +1; diff --git a/inc/MyInline.pm b/inc/MyInline.pm new file mode 100644 index 0000000..e0697a6 --- /dev/null +++ b/inc/MyInline.pm @@ -0,0 +1,98 @@ +package MyInline; + +use strict; +use warnings; + +{ + package My::Extract; + + use parent 'Test::Inline::Extract'; + + use List::Util qw( first ); + + # This extracts the SYNOPSIS in addition to code specifically + # marked for testing + my $search = qr/ + (?:^|\n) # After the beginning of the string, or a newline + ( # ... start capturing + # EITHER + package\s+ # A package + [^\W\d]\w*(?:(?:\'|::)[^\W\d]\w*)* # ... with a name + \s*; # And a statement terminator + | # OR + \#\s*PODNAME:\s+ # A PODNAME comment + [^\W\d]\w*(?:(?:\'|::)[^\W\d]\w*)* # ... with a name + (?:\s+|$) # And a name terminator + | + =head1[ \t]+SYNOPSIS\n + .*? + (?=\n=) + | # OR + =for[ \t]+example[ \t]+begin\n # ... when we find a =for example begin + .*? # ... and keep capturing + \n=for[ \t]+example[ \t]+end\s*? # ... until the =for example end + (?:\n|$) # ... at the end of file or a newline + | # OR + =begin[ \t]+(?:test|testing)(?:-SETUP)? # ... when we find a =begin test or testing + .*? # ... and keep capturing + \n=end[ \t]+(?:test|testing)(?:-SETUP)? # ... until an =end tag + .*? + (?:\n|$) # ... at the end of file or a newline + ) # ... and stop capturing + /isx; + + sub _elements { + my $self = shift; + my @elements = (); + while ( $self->{source} =~ m/$search/go ) { + my $elt = $1; + + # A hack to turn the SYNOPSIS into something Test::Inline + # doesn't barf on + if ( $elt =~ s/=head1[ \t]+SYNOPSIS/=begin testing-SETUP\n\n{/ ) { + $elt .= "}\n\n=end testing-SETUP"; + } + + # It seems like search.cpan doesn't like a name with + # spaces after =begin. bleah, what a mess. + $elt =~ s/testing-SETUP/testing SETUP/g; + + push @elements, $elt; + } + + # If we have just one element it's a SYNOPSIS, so there's no + # tests. + return unless @elements > 2; + + if ( @elements && $self->{source} =~ /# PODNAME: (Moose::Cookbook\S+)(?:\s|$)/ ) { + foreach my $element (@elements) + { + $element = "package $1;" if $element =~ /# PODNAME: (Moose::Cookbook\S+)(?:\s+|$)/; + } + } + + if ( @elements && $self->{source} =~ /=head1 NAME\n\n(Moose::Cookbook\S+)/ ) { + unshift @elements, 'package ' . $1 . ';'; + } + + ( first {/^=/} @elements ) ? \@elements : ''; + } +} + +{ + package My::Content; + + use parent 'Test::Inline::Content::Default'; + + sub process { + my $self = shift; + + my $base = $self->SUPER::process(@_); + + $base =~ s/(\$\| = 1;)/use Test::Fatal;\n$1/; + + return $base; + } +} + +1; diff --git a/inc/SimpleAuthority.pm b/inc/SimpleAuthority.pm new file mode 100644 index 0000000..839571a --- /dev/null +++ b/inc/SimpleAuthority.pm @@ -0,0 +1,13 @@ +use strict; +use warnings; +package inc::SimpleAuthority; + +use Moose; +with 'Dist::Zilla::Role::MetaProvider'; + +sub metadata +{ + return +{ x_authority => 'cpan:STEVAN' }; +} + +1; diff --git a/inc/SimpleProvides.pm b/inc/SimpleProvides.pm new file mode 100644 index 0000000..3d768b1 --- /dev/null +++ b/inc/SimpleProvides.pm @@ -0,0 +1,34 @@ +use strict; +use warnings; +package inc::SimpleProvides; + +use Moose; +with 'Dist::Zilla::Role::MetaProvider', + 'Dist::Zilla::Role::FileFinderUser' => { + default_finders => [ ':InstallModules' ], # this is overridden in dist.ini! + }, +; + +sub metadata +{ + my $self = shift; + + my $version = $self->zilla->version; + + return +{ + provides => { + map { + # this is an awful hack and assumes ascii package names: + # please do not cargo-cult this code elsewhere. The proper + # thing to do is to crack open the file and read the pod name. + my $filename = $_->name; + (my $package = $filename) =~ s{[/\\]}{::}g; + $package =~ s/^lib:://; + $package =~ s/\.pod$//; + $package => { file => $filename, version => $version } + } @{$self->found_files}, + } + }; +} + +__PACKAGE__->meta->make_immutable; diff --git a/inc/TestRelease.pm b/inc/TestRelease.pm new file mode 100644 index 0000000..9d30a76 --- /dev/null +++ b/inc/TestRelease.pm @@ -0,0 +1,17 @@ +package inc::TestRelease; + +use Moose; + +extends 'Dist::Zilla::Plugin::TestRelease'; + +around before_release => sub { + my $orig = shift; + my $self = shift; + + local $ENV{MOOSE_TEST_MD} = 1 if not $self->zilla->is_trial; + local $ENV{AUTHOR_TESTING} = 1 if not $self->zilla->is_trial; + + $self->$orig(@_); +}; + +1; |