Blame lib/inc/latest.pm

Packit Service d63224
use strict;
Packit Service d63224
use warnings;
Packit Service d63224
Packit Service d63224
package inc::latest;
Packit Service d63224
Packit Service d63224
# ABSTRACT: use modules bundled in inc/ if they are newer than installed ones
Packit Service d63224
Packit Service d63224
our $VERSION = '0.500';
Packit Service d63224
Packit Service d63224
package inc::latest;
Packit Service d63224
Packit Service d63224
use Carp;
Packit Service d63224
use File::Basename ();
Packit Service d63224
use File::Spec     ();
Packit Service d63224
use File::Path     ();
Packit Service d63224
use IO::File       ();
Packit Service d63224
use File::Copy     ();
Packit Service d63224
Packit Service d63224
# track and return modules loaded by inc::latest
Packit Service d63224
my @loaded_modules;
Packit Service d63224
sub loaded_modules { @loaded_modules }
Packit Service d63224
Packit Service d63224
# must ultimately "goto" the import routine of the module to be loaded
Packit Service d63224
# so that the calling package is correct when $mod->import() runs.
Packit Service d63224
sub import {
Packit Service d63224
    my ( $package, $mod, @args ) = @_;
Packit Service d63224
    return unless ( defined $mod );
Packit Service d63224
Packit Service d63224
    my $private_path = 'inc/latest/private.pm';
Packit Service d63224
    if ( -e $private_path ) {
Packit Service d63224
        # user mode - delegate work to bundled private module
Packit Service d63224
        require $private_path;
Packit Service d63224
        splice( @_, 0, 1, 'inc::latest::private' );
Packit Service d63224
        goto \&inc::latest::private::import;
Packit Service d63224
    }
Packit Service d63224
Packit Service d63224
    # author mode - just record and load the modules
Packit Service d63224
    push( @loaded_modules, $mod );
Packit Service d63224
    require inc::latest::private;
Packit Service d63224
    goto \&inc::latest::private::_load_module;
Packit Service d63224
}
Packit Service d63224
Packit Service d63224
sub write {
Packit Service d63224
    my $package = shift;
Packit Service d63224
    my ( $where, @preload ) = @_;
Packit Service d63224
Packit Service d63224
    warn "should really be writing in inc/" unless $where =~ /inc$/;
Packit Service d63224
Packit Service d63224
    # write inc/latest.pm
Packit Service d63224
    File::Path::mkpath($where);
Packit Service d63224
    my $fh = IO::File->new( File::Spec->catfile( $where, 'latest.pm' ), "w" );
Packit Service d63224
    print {$fh} "# This stub created by inc::latest $VERSION\n";
Packit Service d63224
    print {$fh} <<'HERE';
Packit Service d63224
package inc::latest;
Packit Service d63224
use strict;
Packit Service d63224
use vars '@ISA';
Packit Service d63224
require inc::latest::private;
Packit Service d63224
@ISA = qw/inc::latest::private/;
Packit Service d63224
HERE
Packit Service d63224
    if (@preload) {
Packit Service d63224
        print {$fh} "\npackage inc::latest::preload;\n";
Packit Service d63224
        for my $mod (@preload) {
Packit Service d63224
            print {$fh} "inc::latest->import('$mod');\n";
Packit Service d63224
        }
Packit Service d63224
    }
Packit Service d63224
    print {$fh} "\n1;\n";
Packit Service d63224
    close $fh;
Packit Service d63224
Packit Service d63224
    # write inc/latest/private;
Packit Service d63224
    require inc::latest::private;
Packit Service d63224
    File::Path::mkpath( File::Spec->catdir( $where, 'latest' ) );
Packit Service d63224
    my $from = $INC{'inc/latest/private.pm'};
Packit Service d63224
    my $to = File::Spec->catfile( $where, 'latest', 'private.pm' );
Packit Service d63224
    File::Copy::copy( $from, $to ) or die "Couldn't copy '$from' to '$to': $!";
Packit Service d63224
Packit Service d63224
    return 1;
Packit Service d63224
}
Packit Service d63224
Packit Service d63224
sub bundle_module {
Packit Service d63224
    my ( $package, $module, $where ) = @_;
Packit Service d63224
Packit Service d63224
    # create inc/inc_$foo
Packit Service d63224
    ( my $dist = $module ) =~ s{::}{-}g;
Packit Service d63224
    my $inc_lib = File::Spec->catdir( $where, "inc_$dist" );
Packit Service d63224
    File::Path::mkpath $inc_lib;
Packit Service d63224
Packit Service d63224
    # get list of files to copy
Packit Service d63224
    require ExtUtils::Installed;
Packit Service d63224
    # workaround buggy EU::Installed check of @INC
Packit Service d63224
    my $inst = ExtUtils::Installed->new( extra_libs => [@INC] );
Packit Service d63224
    my $packlist = $inst->packlist($module) or die "Couldn't find packlist";
Packit Service d63224
    my @files = grep { /\.pm$/ } keys %$packlist;
Packit Service d63224
Packit Service d63224
    # figure out prefix
Packit Service d63224
    my $mod_path = quotemeta $package->_mod2path($module);
Packit Service d63224
    my ($prefix) = grep { /$mod_path$/ } @files;
Packit Service d63224
    $prefix =~ s{$mod_path$}{};
Packit Service d63224
Packit Service d63224
    # copy files
Packit Service d63224
    for my $from (@files) {
Packit Service d63224
        next unless $from =~ /\.pm$/;
Packit Service d63224
        ( my $mod_path = $from ) =~ s{^\Q$prefix\E}{};
Packit Service d63224
        my $to = File::Spec->catfile( $inc_lib, $mod_path );
Packit Service d63224
        File::Path::mkpath( File::Basename::dirname($to) );
Packit Service d63224
        File::Copy::copy( $from, $to ) or die "Couldn't copy '$from' to '$to': $!";
Packit Service d63224
    }
Packit Service d63224
    return 1;
Packit Service d63224
}
Packit Service d63224
Packit Service d63224
# Translate a module name into a directory/file.pm to search for in @INC
Packit Service d63224
sub _mod2path {
Packit Service d63224
    my ( $self, $mod ) = @_;
Packit Service d63224
    my @parts = split /::/, $mod;
Packit Service d63224
    $parts[-1] .= '.pm';
Packit Service d63224
    return $parts[0] if @parts == 1;
Packit Service d63224
    return File::Spec->catfile(@parts);
Packit Service d63224
}
Packit Service d63224
Packit Service d63224
1;
Packit Service d63224
Packit Service d63224
Packit Service d63224
# vim: ts=4 sts=4 sw=4 tw=75 et:
Packit Service d63224
Packit Service d63224
__END__
Packit Service d63224
Packit Service d63224
=pod
Packit Service d63224
Packit Service d63224
=encoding UTF-8
Packit Service d63224
Packit Service d63224
=head1 NAME
Packit Service d63224
Packit Service d63224
inc::latest - use modules bundled in inc/ if they are newer than installed ones
Packit Service d63224
Packit Service d63224
=head1 VERSION
Packit Service d63224
Packit Service d63224
version 0.500
Packit Service d63224
Packit Service d63224
=head1 SYNOPSIS
Packit Service d63224
Packit Service d63224
  # in Makefile.PL or Build.PL
Packit Service d63224
  use inc::latest 'Some::Configure::Prereq';
Packit Service d63224
Packit Service d63224
=head1 DESCRIPTION
Packit Service d63224
Packit Service d63224
B<WARNING -- THIS IS AN EXPERIMENTAL MODULE>.  It was originally bundled
Packit Service d63224
(as an experiment) with L<Module::Build> and has been split out for more
Packit Service d63224
general use.
Packit Service d63224
Packit Service d63224
The C<inc::latest> module helps bootstrap configure-time dependencies for
Packit Service d63224
CPAN distributions.  These dependencies get bundled into the C<inc>
Packit Service d63224
directory within a distribution and are used by F<Makefile.PL> or F<Build.PL>.
Packit Service d63224
Packit Service d63224
Arguments to C<inc::latest> are module names that are checked against both
Packit Service d63224
the current C<@INC> array and against specially-named directories in
Packit Service d63224
C<inc>.  If the bundled version is newer than the installed one (or the
Packit Service d63224
module isn't installed, then, the bundled directory is added to the start
Packit Service d63224
of C<@INC> and the module is loaded from there.
Packit Service d63224
Packit Service d63224
There are actually two variations of C<inc::latest> -- one for authors and
Packit Service d63224
one for the C<inc> directory.  For distribution authors, the C<inc::latest>
Packit Service d63224
installed in the system will record modules loaded via C<inc::latest> and
Packit Service d63224
can be used to create the bundled files in C<inc>, including writing the
Packit Service d63224
second variation as C<inc/latest.pm>.
Packit Service d63224
Packit Service d63224
This second C<inc::latest> is the one that is loaded in a distribution
Packit Service d63224
being installed (e.g. from F<Makefile.PL> or F<Build.PL>).  This bundled
Packit Service d63224
C<inc::latest> is the one that determines which module to load.
Packit Service d63224
Packit Service d63224
=head2 Special notes on bundling
Packit Service d63224
Packit Service d63224
The C<inc::latest> module creates bundled directories based on the packlist
Packit Service d63224
file of an installed distribution.  Even though C<inc::latest> takes module
Packit Service d63224
name arguments, it is better to think of it as bundling and making
Packit Service d63224
available entire I<distributions>.  When a module is loaded through
Packit Service d63224
C<inc::latest>, it looks in all bundled distributions in C<inc/> for a
Packit Service d63224
newer module than can be found in the existing C<@INC> array.
Packit Service d63224
Packit Service d63224
Thus, the module-name provided should usually be the "top-level" module
Packit Service d63224
name of a distribution, though this is not strictly required.
Packit Service d63224
C<inc::latest> has a number of heuristics to discover module names,
Packit Service d63224
allowing users to do things like this:
Packit Service d63224
Packit Service d63224
  use inc::latest 'Devel::AssertOS::Unix';
Packit Service d63224
Packit Service d63224
even though Devel::AssertOS::Unix is contained within the Devel-CheckOS
Packit Service d63224
distribution.
Packit Service d63224
Packit Service d63224
At the current time, packlists are required.  Thus, bundling dual-core
Packit Service d63224
modules may require a 'forced install' over versions in the latest version
Packit Service d63224
of perl in order to create the necessary packlist for bundling.
Packit Service d63224
Packit Service d63224
=head2 Managing dependency chains
Packit Service d63224
Packit Service d63224
Before bundling a distribution you must ensure that all prerequisites are
Packit Service d63224
also bundled and load in the correct order.
Packit Service d63224
Packit Service d63224
For example, if you need C<Wibble>, but C<Wibble> depends on C<Wobble>,
Packit Service d63224
and you have bundled C<Module::Build>, your F<Build.PL> might look like this:
Packit Service d63224
Packit Service d63224
  use inc::latest 'Wobble';
Packit Service d63224
  use inc::latest 'Wibble';
Packit Service d63224
  use inc::latest 'Module::Build';
Packit Service d63224
Packit Service d63224
  Module::Build->new(
Packit Service d63224
    module_name => 'Foo::Bar',
Packit Service d63224
    license => 'perl',
Packit Service d63224
  )->create_build_script;
Packit Service d63224
Packit Service d63224
Authors are strongly suggested to limit the bundling of additional
Packit Service d63224
dependencies if at all possible and to carefully test their distribution
Packit Service d63224
tarballs before uploading to CPAN.
Packit Service d63224
Packit Service d63224
=head1 USAGE
Packit Service d63224
Packit Service d63224
=head2 As bundled in inc/
Packit Service d63224
Packit Service d63224
Using L</Author-mode>, a special stub module will be created in your
Packit Service d63224
distribute directory as F<inc/latest.pm>.  In your F<Makefile.PL> or
Packit Service d63224
F<Build.PL>, you can then load C<inc::latest> to load bundled modules.
Packit Service d63224
Packit Service d63224
When calling C<use>, the bundled C<inc::latest> takes a single module name
Packit Service d63224
and optional arguments to pass to that module's own import method.
Packit Service d63224
Packit Service d63224
  use inc::latest 'Foo::Bar' qw/foo bar baz/;
Packit Service d63224
Packit Service d63224
The implementation is private.  Only the C<import> method is public.
Packit Service d63224
Packit Service d63224
=head2 Author-mode
Packit Service d63224
Packit Service d63224
When you have L<inc::latest> installed from CPAN, then you are in author-mode
Packit Service d63224
if any of the Author-mode methods are available.  For example:
Packit Service d63224
Packit Service d63224
  if ( inc::latest->can('write') ) {
Packit Service d63224
    inc::latest->write('inc');
Packit Service d63224
  }
Packit Service d63224
Packit Service d63224
Using author-mode, you can create the stub F<inc/latest.pm> and bundle
Packit Service d63224
modules into F<inc>.
Packit Service d63224
Packit Service d63224
=over 4
Packit Service d63224
Packit Service d63224
=item loaded_modules()
Packit Service d63224
Packit Service d63224
  my @list = inc::latest->loaded_modules;
Packit Service d63224
Packit Service d63224
This takes no arguments and always returns a list of module names requested
Packit Service d63224
for loading via "use inc::latest 'MODULE'", regardless of whether the load
Packit Service d63224
was successful or not.
Packit Service d63224
Packit Service d63224
=item write()
Packit Service d63224
Packit Service d63224
  inc::latest->write( 'inc' );
Packit Service d63224
Packit Service d63224
This writes the bundled version of inc::latest to the directory name given
Packit Service d63224
as an argument.  It almost all cases, it should be 'C<inc>'.
Packit Service d63224
Packit Service d63224
=item bundle_module()
Packit Service d63224
Packit Service d63224
  for my $mod ( inc::latest->loaded_modules ) {
Packit Service d63224
    inc::latest->bundle_module($mod, $dir);
Packit Service d63224
  }
Packit Service d63224
Packit Service d63224
If $mod corresponds to a packlist, then this function creates a
Packit Service d63224
specially-named directory in $dir and copies all .pm files from the modlist
Packit Service d63224
to the new directory (which almost always should just be 'inc').  For
Packit Service d63224
example, if Foo::Bar is the name of the module, and $dir is 'inc', then the
Packit Service d63224
directory would be 'inc/inc_Foo-Bar' and contain files like this:
Packit Service d63224
Packit Service d63224
  inc/inc_Foo-Bar/Foo/Bar.pm
Packit Service d63224
Packit Service d63224
Currently, $mod B<must> have a packlist.  If this is not the case (e.g. for
Packit Service d63224
a dual-core module), then the bundling will fail.  You may be able to
Packit Service d63224
create a packlist by forced installing the module on top of the version
Packit Service d63224
that came with core Perl.
Packit Service d63224
Packit Service d63224
=back
Packit Service d63224
Packit Service d63224
=for :stopwords cpan testmatrix url annocpan anno bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan
Packit Service d63224
Packit Service d63224
=head1 SUPPORT
Packit Service d63224
Packit Service d63224
=head2 Bugs / Feature Requests
Packit Service d63224
Packit Service d63224
Please report any bugs or feature requests through the issue tracker
Packit Service d63224
at L<https://github.com/dagolden/inc-latest/issues>.
Packit Service d63224
You will be notified automatically of any progress on your issue.
Packit Service d63224
Packit Service d63224
=head2 Source Code
Packit Service d63224
Packit Service d63224
This is open source software.  The code repository is available for
Packit Service d63224
public review and contribution under the terms of the license.
Packit Service d63224
Packit Service d63224
L<https://github.com/dagolden/inc-latest>
Packit Service d63224
Packit Service d63224
  git clone https://github.com/dagolden/inc-latest.git
Packit Service d63224
Packit Service d63224
=head1 AUTHORS
Packit Service d63224
Packit Service d63224
=over 4
Packit Service d63224
Packit Service d63224
=item *
Packit Service d63224
Packit Service d63224
David Golden <dagolden@cpan.org>
Packit Service d63224
Packit Service d63224
=item *
Packit Service d63224
Packit Service d63224
Eric Wilhelm <ewilhelm@cpan.org>
Packit Service d63224
Packit Service d63224
=back
Packit Service d63224
Packit Service d63224
=head1 COPYRIGHT AND LICENSE
Packit Service d63224
Packit Service d63224
This software is Copyright (c) 2009 by David Golden.
Packit Service d63224
Packit Service d63224
This is free software, licensed under:
Packit Service d63224
Packit Service d63224
  The Apache License, Version 2.0, January 2004
Packit Service d63224
Packit Service d63224
=cut