File Coverage

blib/lib/pip.pm
Criterion Covered Total %
statement 28 106 26.4
branch 0 42 0.0
condition 0 3 0.0
subroutine 10 18 55.5
pod 0 8 0.0
total 38 177 21.4


line stmt bran cond sub pod time code
1             package pip;
2              
3 1     1   1291 use 5.006;
  1         5  
  1         33  
4 1     1   4 use strict;
  1         2  
  1         29  
5 1     1   7 use File::Spec ();
  1         2  
  1         15  
6 1     1   4 use File::Temp ();
  1         2  
  1         13  
7 1     1   1006 use File::Which ();
  1         847  
  1         20  
8 1     1   1229 use Getopt::Long ();
  1         11057  
  1         29  
9 1     1   26 use URI::file ();
  1         3  
  1         16  
10 1     1   6 use Module::Plan::Base ();
  1         2  
  1         17  
11              
12 1     1   6 use vars qw{$VERSION};
  1         2  
  1         45  
13             BEGIN {
14 1     1   1099 $VERSION = '1.19';
15             }
16              
17              
18              
19              
20              
21             #####################################################################
22             # Main Function
23              
24             # Save a copy of @ARGV for error messages
25             my $install = 0;
26             Getopt::Long::GetOptions(
27             install => \$install,
28             );
29              
30             sub main {
31 0 0   0 0   unless ( @ARGV ) {
32 0           error("Did not provide a command");
33             }
34              
35             # If the first argument is a file, install it
36 0 0         if ( $ARGV[0] =~ /^(https?|ftp)\:\/\// ) {
37             # resolving redirects to get the real URI, handy for handling
38             # e.g. github URLs like http://github.com/john/repo-name/tarball/master
39 0           require LWP::Simple;
40 0           my $h = LWP::Simple::head($ARGV[0]);
41 0 0         error("Probably non existing URI '$ARGV[0]'") unless defined($h);
42 0   0       return fetch_any($h->request->uri || $ARGV[0]);
43             }
44 0 0         if ( -f $ARGV[0] ) {
45 0           return install_any($ARGV[0]);
46             }
47              
48 0           error("Unknown or unsupported command '$ARGV[0]'");
49             }
50              
51             sub fetch_any {
52 0     0 0   my $uri = $_[0];
53              
54             # Handle tarballs via a custom Module::Plan::Lite object.
55             # Also handle PAR archives
56 0 0         if ( $uri =~ /\.(?:par|zip|tar\.gz)$/ ) {
57 0           require Module::Plan::Lite;
58 0           my $plan = Module::Plan::Lite->new(
59             p5i => 'default.p5i',
60             lines => [ '', $uri ],
61             );
62 0           $plan->run;
63 0           return 1;
64             }
65              
66             # P5I files can have a plan created for the remote URI
67 0 0         if ( $uri =~ /\.p5i$/ ) {
68 0           require Module::Plan::Lite;
69 0           my $plan = Module::Plan::Lite->new(
70             p5i => $uri,
71             );
72 0           $plan->run;
73 0           return 1;
74             }
75              
76             # We don't yet support remote p5z files
77 0 0         if ( $uri =~ /\.p5z$/ ) {
78 0           error("Remote p5z installation is not yet supported");
79             }
80              
81 0           error("Unknown or unsupported uri '$uri'");
82             }
83              
84             sub install_any {
85             # Load the plan
86 0     0 0   my $plan = read_any(@_);
87              
88             # Run it
89 0           $plan->run;
90              
91 0           return 1;
92             }
93              
94             sub read_any {
95 0     0 0   my $param = $_[0];
96              
97             # If the first argument is a tar.gz file, hand off to install
98 0 0         if ( $param =~ /\.(?:zip|tar\.gz|tgz)$/ ) {
99 0           return read_archive(@_);
100             }
101              
102             # If the first argument is a par file, hand off to install
103 0 0         if ( $param =~ /\.par$/ ) {
104 0           return read_archive(@_);
105             }
106              
107             # If the first argument is a p5i file, hand off to read
108 0 0         if ( $param =~ /\.p5i$/ ) {
109 0           return read_p5i(@_);
110             }
111              
112             # If the first argument is a p5z file, hand off to instal
113 0 0         if ( $param =~ /\.p5z$/ ) {
114 0           return read_p5z(@_);
115             }
116              
117 0           error("Unknown or unsupported file '$param'");
118             }
119              
120             # Create the plan object from a file
121             sub read_p5i {
122 0 0   0 0   my $pip = @_ ? shift : File::Spec->curdir;
123 0 0         if ( -d $pip ) {
124 0           $pip = File::Spec->catfile( $pip, 'default.p5i' );
125             }
126 0           $pip = File::Spec->rel2abs( $pip );
127 0 0         unless ( -f $pip ) {
128 0           error( "The plan file $pip does not exist" );
129             }
130              
131             # Create the plan object
132 0           my $plan = eval {
133 0           Module::Plan::Base->read( $pip );
134             };
135 0 0         if ( $@ ) {
136 0 0         unless ( $@ =~ /The sources directory is not owned by the current user/ ) {
137             # Rethrow the error
138 0           die $@;
139             }
140              
141             # Generate an appropriate error
142 0           my @msg = (
143             "The current user does not control the default CPAN client",
144             );
145 0 0         if ( File::Which::which('sudo') ) {
146 0           my $cmd = join(' ', 'sudo', '-H', $0, @_);
147 0           push @msg, "You may need to try again with the following command:";
148 0           push @msg, "";
149 0           push @msg, " $cmd";
150             }
151 0           error( @msg );
152             }
153              
154 0           return $plan;
155             }
156              
157             sub read_archive {
158 0     0 0   my $archive = File::Spec->rel2abs(shift);
159 0 0         unless ( -f $archive ) {
160 0           error("Filed does no exist: $archive");
161             }
162 0           require Module::Plan::Lite;
163 0           Module::Plan::Lite->new(
164             p5i => 'default.p5i',
165             lines => [ '', URI::file->new($archive)->as_string ],
166             );
167             }
168              
169             sub read_p5z {
170 0     0 0   my $p5z = File::Spec->rel2abs(shift);
171 0 0         unless ( -f $p5z ) {
172 0           error("File does not exist: $p5z");
173             }
174              
175             # Create the temp directory
176 0           my $dir = File::Temp::tempdir( CLEANUP => 1 );
177 0           my $pushd = File::pushd::pushd( $dir );
178              
179             # Extract the tarball
180 0           require Archive::Tar;
181 0           my @files = Archive::Tar->extract_archive( $p5z, 1 );
182 0 0         unless ( @files ) {
183 0           error( "Failed to extract P5Z file: " . Archive::Tar->error );
184             }
185              
186             # Find the plan
187 0           my $path = File::Spec->catfile( $dir, 'default.p5i' );
188 0 0         unless ( -f $path ) {
189 0           error("P5Z file did not contain a default.p5i");
190             }
191              
192             # Load the plan
193 0           return read_p5i( $path );
194             }
195              
196              
197              
198              
199              
200             #####################################################################
201             # Support Functions
202              
203             sub error {
204 0     0 0   print "\n";
205 0           print map { $_ . "\n" } @_;
  0            
206 0           exit(255);
207             }
208              
209             1;
210              
211             =pod
212              
213             =head1 NAME
214              
215             pip - The Perl Installation Program, for scripted and third-party
216             distribution installation.
217              
218             =head1 SYNOPSIS
219              
220             pip script.p5i
221             pip script.p5z
222             pip Distribution-1.23.tgz
223             pip Distribution-1.23.tar.gz
224             pip Distribution-1.23-MSWin32-5.8.0.par
225             pip http://server/Distribution-1.23.tar.gz
226             pip http://github.com/gitpan/Distribution/tarball/1.23
227              
228             =head1 DESCRIPTION
229              
230             The B ("Perl Installation Program") console application is used to
231             install Perl distributions in a wide variety of formats, both from CPAN
232             and from external third-party locations, while supporting module
233             dependencies that go across the boundary from third-party to CPAN.
234              
235             Using B you can install CPAN modules, arbitrary tarballs from both
236             the local file-system or across the internet from arbitrary URIs.
237              
238             You can use B to ensure that specific versions of CPAN modules are
239             installed I of the most current version.
240              
241             And beyond just single installations, you script script a series of
242             these installations by creating a "P5I" (Perl 5 Installation) file.
243              
244             A Perl 5 Installation (P5I) file is a small script-like file that
245             describes a set of distributions to install, and integrates the
246             installation of these distributions with the CPAN installer.
247              
248             The primary use of P5I files are for installing proprietary or
249             non-CPAN software that may still require the installation of a
250             number of CPAN dependencies in order to function.
251              
252             P5I files are also extensible, with the first line of the file
253             specifying the name of the Perl class that implements the plan.
254              
255             For the moment, the class described at the top of the P5I file
256             must be installed.
257              
258             The simple L plan class is bundled with the main
259             distribution, and additional types can be installed if needed.
260              
261             =head2 Future Additions
262              
263             Also on the development schedule for B is the creation and
264             installation of distributions via "P5Z" files, which are tarballs
265             containing a P5I file, as well as all the distribution tarballs
266             referenced by the P5I file.
267              
268             It is also anticipated that B will gain support for L
269             binary packages and potentially also for ActivePerl L files.
270              
271             =head1 USAGE
272              
273             The primary use of F is to install from a P5I script, with the
274             canonical use case as follows:
275              
276             pip directory/myplan.p5i
277              
278             This command will load the plan file F, create
279             the plan, and then execute it.
280              
281             If only a directory name is given, F will look for a F
282             plan in the directory. Thus, all of the following are equivalent
283              
284             pip directory
285             pip directory/
286             pip directory/default.p5i
287              
288             If no target is provided at all, then the current directory will be used.
289             Thus, the following are equivalent
290              
291             pip
292             pip .
293             pip default.p5i
294             pip ./default.p5i
295              
296             =head2 Syntax of a plan file
297              
298             Initially, the only plan is available is the L
299             (MPL) plan.
300              
301             A typical MPL plan will look like the following
302              
303             # myplan.p5i
304             Module::Plan::Lite
305            
306             Process-0.17.tar.gz
307             YAML-Tiny-0.10.tar.gz
308              
309             =head2 Direct installation of a single tarball
310              
311             With the functionality available in F, you can find that sometimes
312             you don't even want to make a file at all, you just want to install a
313             single tarball.
314              
315             The C<-i> option lets you pass the name of a single file and it will treat
316             it as an installer for that single file. Further, if the extension of the
317             tarball is .tar.gz, the B<-i> option is implied.
318              
319             For example, the following are equivalent.
320              
321             # Installing with the -i|--install option
322             > pip Process-0.17.tar.gz
323             > pip -i Process-0.17.tar.gz
324             > pip --install Process-0.17.tar.gz
325            
326             # Installing from the file as normal
327             > pip ./default.p5i
328            
329             # myplan.p5i
330             Module::Plan::Lite
331            
332             Process-0.17.tar.gz
333              
334             The C<-i> option can be used with any single value supported by
335             L (see above).
336              
337             This means you can also use B to install a distribution from any
338             arbitrary URI, including installing direct from a subversion repository.
339              
340             > pip http://svn.ali.as/cpan/release/Process-0.17.tar.gz
341              
342             =head1 SUPPORT
343              
344             This module is stored in an Open Repository at the following address.
345              
346             L
347              
348             Write access to the repository is made available automatically to any
349             published CPAN author, and to most other volunteers on request.
350              
351             If you are able to submit your bug report in the form of new (failing)
352             unit tests, or can apply your fix directly instead of submitting a patch,
353             you are B encouraged to do so. The author currently maintains
354             over 100 modules and it may take some time to deal with non-Critical bug
355             reports or patches.
356              
357             This will guarentee that your issue will be addressed in the next
358             release of the module.
359              
360             If you cannot provide a direct test or fix, or don't have time to do so,
361             then regular bug reports are still accepted and appreciated via the CPAN
362             bug tracker.
363              
364             L
365              
366             For other issues, for commercial enhancement and support, or to have your
367             write access enabled for the repository, contact the author at the email
368             address above.
369              
370             =head1 AUTHORS
371              
372             Adam Kennedy Eadamk@cpan.orgE
373              
374             =head1 SEE ALSO
375              
376             L, L, L
377              
378             =head1 COPYRIGHT
379              
380             Copyright 2006 - 2010 Adam Kennedy.
381              
382             This program is free software; you can redistribute
383             it and/or modify it under the same terms as Perl itself.
384              
385             The full text of the license can be found in the
386             LICENSE file included with this module.
387              
388             =cut