160ff331c7b66f22e13ad94546f0f821b409191c
[p5sagit/local-lib.git] / lib / local / lib.pm
1 use strict;
2 use warnings;
3
4 package local::lib;
5
6 use 5.008001; # probably works with earlier versions but I'm not supporting them
7               # (patches would, of course, be welcome)
8
9 use File::Spec ();
10 use File::Path ();
11 use Carp ();
12 use Config;
13
14 our $VERSION = '1.003003'; # 1.3.3
15
16 sub import {
17   my ($class, @args) = @_;
18
19   # The path is required, but last in the list, so we pop, not shift here. 
20   my $path = pop @args;
21   $path = $class->resolve_path($path);
22   $class->setup_local_lib_for($path);
23
24   # Handle the '--self-contained' option
25   my $flag = shift @args;  
26   no warnings 'uninitialized'; # the flag is optional 
27   # make sure fancy dashes cause an error
28   if ($flag =~ /−/) {
29       die <<'DEATH';
30 WHOA THERE! It looks like you've got some fancy dashes in your commandline!
31 These are *not* the traditional -- dashes that software recognizes. You
32 probably got these by copy-pasting from the perldoc for this module as
33 rendered by a UTF8-capable formatter. This most typically happens on an OS X
34 terminal, but can happen elsewhere too. Please try again after replacing the
35 dashes with normal minus signs.
36 DEATH
37   }
38   if ($flag eq '--self-contained') {
39     # The only directories that remain are those that we just defined and those where core modules are stored. 
40     @INC = ($Config::Config{privlibexp}, $Config::Config{archlibexp}, split ':', $ENV{PERL5LIB});
41   }
42   elsif (defined $flag) {
43       die "unrecognized import argument: $flag";
44   }
45
46 }
47
48 sub pipeline;
49
50 sub pipeline {
51   my @methods = @_;
52   my $last = pop(@methods);
53   if (@methods) {
54     \sub {
55       my ($obj, @args) = @_;
56       $obj->${pipeline @methods}(
57         $obj->$last(@args)
58       );
59     };
60   } else {
61     \sub {
62       shift->$last(@_);
63     };
64   }
65 }
66
67 =begin testing
68
69 #:: test pipeline
70
71 package local::lib;
72
73 { package Foo; sub foo { -$_[1] } sub bar { $_[1]+2 } sub baz { $_[1]+3 } }
74 my $foo = bless({}, 'Foo');                                                 
75 Test::More::ok($foo->${pipeline qw(foo bar baz)}(10) == -15);
76
77 =end testing
78
79 =cut
80
81 sub resolve_path {
82   my ($class, $path) = @_;
83   $class->${pipeline qw(
84     resolve_relative_path
85     resolve_home_path
86     resolve_empty_path
87   )}($path);
88 }
89
90 sub resolve_empty_path {
91   my ($class, $path) = @_;
92   if (defined $path) {
93     $path;
94   } else {
95     '~/perl5';
96   }
97 }
98
99 =begin testing
100
101 #:: test classmethod setup
102
103 my $c = 'local::lib';
104
105 =end testing
106
107 =begin testing
108
109 #:: test classmethod
110
111 is($c->resolve_empty_path, '~/perl5');
112 is($c->resolve_empty_path('foo'), 'foo');
113
114 =end testing
115
116 =cut
117
118 sub resolve_home_path {
119   my ($class, $path) = @_;
120   return $path unless ($path =~ /^~/);
121   my ($user) = ($path =~ /^~([^\/]+)/); # can assume ^~ so undef for 'us'
122   my $tried_file_homedir;
123   my $homedir = do {
124     if (eval { require File::HomeDir } && $File::HomeDir::VERSION >= 0.65) {
125       $tried_file_homedir = 1;
126       if (defined $user) {
127         File::HomeDir->users_home($user);
128       } else {
129         File::HomeDir->my_home;
130       }
131     } else {
132       if (defined $user) {
133         (getpwnam $user)[7];
134       } else {
135         if (defined $ENV{HOME}) {
136           $ENV{HOME};
137         } else {
138           (getpwuid $<)[7];
139         }
140       }
141     }
142   };
143   unless (defined $homedir) {
144     Carp::croak(
145       "Couldn't resolve homedir for "
146       .(defined $user ? $user : 'current user')
147       .($tried_file_homedir ? '' : ' - consider installing File::HomeDir')
148     );
149   }
150   $path =~ s/^~[^\/]*/$homedir/;
151   $path;
152 }
153
154 sub resolve_relative_path {
155   my ($class, $path) = @_;
156   File::Spec->rel2abs($path);
157 }
158
159 =begin testing
160
161 #:: test classmethod
162
163 local *File::Spec::rel2abs = sub { shift; 'FOO'.shift; };
164 is($c->resolve_relative_path('bar'),'FOObar');
165
166 =end testing
167
168 =cut
169
170 sub setup_local_lib_for {
171   my ($class, $path) = @_;
172   $class->ensure_dir_structure_for($path);
173   if ($0 eq '-') {
174     $class->print_environment_vars_for($path);
175     exit 0;
176   } else {
177     $class->setup_env_hash_for($path);
178     unshift(@INC, split(':', $ENV{PERL5LIB}));
179   }
180 }
181
182 sub modulebuildrc_path {
183   my ($class, $path) = @_;
184   File::Spec->catfile($path, '.modulebuildrc');
185 }
186
187 sub install_base_bin_path {
188   my ($class, $path) = @_;
189   File::Spec->catdir($path, 'bin');
190 }
191
192 sub install_base_perl_path {
193   my ($class, $path) = @_;
194   File::Spec->catdir($path, 'lib', 'perl5');
195 }
196
197 sub install_base_arch_path {
198   my ($class, $path) = @_;
199   File::Spec->catdir($class->install_base_perl_path($path), $Config{archname});
200 }
201
202 sub ensure_dir_structure_for {
203   my ($class, $path) = @_;
204   unless (-d $path) {
205     warn "Attempting to create directory ${path}\n";
206   }
207   File::Path::mkpath($path);
208   my $modulebuildrc_path = $class->modulebuildrc_path($path);
209   if (-e $modulebuildrc_path) {
210     unless (-f _) {
211       Carp::croak("${modulebuildrc_path} exists but is not a plain file");
212     }
213   } else {
214     warn "Attempting to create file ${modulebuildrc_path}\n";
215     open MODULEBUILDRC, '>', $modulebuildrc_path
216       || Carp::croak("Couldn't open ${modulebuildrc_path} for writing: $!");
217     print MODULEBUILDRC qq{install  --install_base  ${path}\n}
218       || Carp::croak("Couldn't write line to ${modulebuildrc_path}: $!");
219     close MODULEBUILDRC
220       || Carp::croak("Couldn't close file ${modulebuildrc_path}: $@");
221   }
222 }
223
224 sub INTERPOLATE_ENV () { 1 }
225 sub LITERAL_ENV     () { 0 }
226
227 sub print_environment_vars_for {
228   my ($class, $path) = @_;
229   my @envs = $class->build_environment_vars_for($path, LITERAL_ENV);
230   my $out = '';
231
232   # rather basic csh detection, goes on the assumption that something won't
233   # call itself csh unless it really is. also, default to bourne in the
234   # pathological situation where a user doesn't have $ENV{SHELL} defined.
235   # note also that shells with funny names, like zoid, are assumed to be
236   # bourne.
237   my $shellbin = 'sh';
238   if(defined $ENV{'SHELL'}) {
239       my @shell_bin_path_parts = File::Spec->splitpath($ENV{'SHELL'});
240       $shellbin = $shell_bin_path_parts[-1];
241   }
242   my $shelltype = do {
243       local $_ = $shellbin;
244       if(/csh/) {
245           'csh'
246       } else {
247           'bourne'
248       }
249   };
250
251   while (@envs) {
252     my ($name, $value) = (shift(@envs), shift(@envs));
253     $value =~ s/(\\")/\\$1/g;
254     $out .= $class->${\"build_${shelltype}_env_declaration"}($name, $value);
255   }
256   print $out;
257 }
258
259 # simple routines that take two arguments: an %ENV key and a value. return
260 # strings that are suitable for passing directly to the relevant shell to set
261 # said key to said value.
262 sub build_bourne_env_declaration {
263   my $class = shift;
264   my($name, $value) = @_;
265   return qq{export ${name}="${value}"\n};
266 }
267
268 sub build_csh_env_declaration {
269   my $class = shift;
270   my($name, $value) = @_;
271   return qq{setenv ${name} "${value}"\n};
272 }
273
274 sub setup_env_hash_for {
275   my ($class, $path) = @_;
276   my %envs = $class->build_environment_vars_for($path, INTERPOLATE_ENV);
277   @ENV{keys %envs} = values %envs;
278 }
279
280 sub build_environment_vars_for {
281   my ($class, $path, $interpolate) = @_;
282   return (
283     MODULEBUILDRC => $class->modulebuildrc_path($path),
284     PERL_MM_OPT => "INSTALL_BASE=${path}",
285     PERL5LIB => join(':',
286                   $class->install_base_perl_path($path),
287                   $class->install_base_arch_path($path),
288                   ($ENV{PERL5LIB} ?
289                     ($interpolate == INTERPOLATE_ENV
290                       ? ($ENV{PERL5LIB})
291                       : ('$PERL5LIB'))
292                     : ())
293                 ),
294     PATH => join(':',
295               $class->install_base_bin_path($path),
296               ($interpolate == INTERPOLATE_ENV
297                 ? $ENV{PATH}
298                 : '$PATH')
299              ),
300   )
301 }
302
303 =begin testing
304
305 #:: test classmethod
306
307 File::Path::rmtree('t/var/splat');
308
309 $c->ensure_dir_structure_for('t/var/splat');
310
311 ok(-d 't/var/splat');
312
313 ok(-f 't/var/splat/.modulebuildrc');
314
315 =end testing
316
317 =head1 NAME
318
319 local::lib - create and use a local lib/ for perl modules with PERL5LIB
320
321 =head1 SYNOPSIS
322
323 In code -
324
325   use local::lib; # sets up a local lib at ~/perl5
326
327   use local::lib '~/foo'; # same, but ~/foo
328
329   # Or...
330   use FindBin;
331   use local::lib "$FindBin::Bin/../support";  # app-local support library
332
333 From the shell -
334
335   # Install LWP and it's missing dependencies to the 'my_lwp' directory
336   perl -MCPAN -Mlocal::lib=my_lwp -e 'CPAN::install(LWP)'
337
338   # Install LWP and *all non-core* dependencies to the 'my_lwp' directory 
339   perl -MCPAN -Mlocal::lib=--self-contained,my_lwp -e 'CPAN::install(LWP)'
340
341   # Just print out useful shell commands
342   $ perl -Mlocal::lib
343   export MODULEBUILDRC=/home/username/perl/.modulebuildrc
344   export PERL_MM_OPT='INSTALL_BASE=/home/username/perl'
345   export PERL5LIB='/home/username/perl/lib/perl5:/home/username/perl/lib/perl5/i386-linux'
346   export PATH="/home/username/perl/bin:$PATH"
347
348 To bootstrap if you don't have local::lib itself installed -
349
350   <download local::lib tarball from CPAN, unpack and cd into dir>
351
352   $ perl Makefile.PL --bootstrap
353   $ make test && make install
354
355   $ echo 'eval $(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)' >>~/.bashrc
356
357   # Or for C shells...
358
359   $ /bin/csh
360   % echo $SHELL
361   /bin/csh
362   % perl -I$HOME/perl5/lib/perl5 -Mlocal::lib >> ~/.cshrc
363
364 You can also pass --boostrap=~/foo to get a different location -
365
366   $ perl Makefile.PL --bootstrap=~/foo
367   $ make test && make install
368
369   $ echo 'eval $(perl -I$HOME/foo/lib/perl5 -Mlocal::lib=$HOME/foo)' >>~/.bashrc
370
371 =head1 DESCRIPTION
372
373 This module provides a quick, convenient way of bootstrapping a user-local Perl
374 module library located within the user's home directory. It also constructs and
375 prints out for the user the list of environment variables using the syntax
376 appropriate for the user's current shell (as specified by the C<SHELL>
377 environment variable), suitable for directly adding to one's shell configuration
378 file.
379
380 More generally, local::lib allows for the bootstrapping and usage of a directory
381 containing Perl modules outside of Perl's C<@INC>. This makes it easier to ship
382 an application with an app-specific copy of a Perl module, or collection of
383 modules. Useful in cases like when an upstream maintainer hasn't applied a patch
384 to a module of theirs that you need for your application.
385
386 On import, local::lib sets the following environment variables to appropriate
387 values:
388
389 =over 4
390
391 =item MODULEBUILDRC
392
393 =item PERL_MM_OPT
394
395 =item PERL5LIB
396
397 =item PATH
398
399 PATH is appended to, rather than clobbered.
400
401 =back
402
403 These values are then available for reference by any code after import.
404
405 =head1 METHODS
406
407 =head2 ensure_directory_structure_for
408
409 =over 4
410
411 =item Arguments: path
412
413 =back
414
415 Attempts to create the given path, and all required parent directories. Throws
416 an exception on failure.
417
418 =head2 print_environment_vars_for
419
420 =over 4
421
422 =item Arguments: path
423
424 =back
425
426 Prints to standard output the variables listed above, properly set to use the
427 given path as the base directory.
428
429 =head2 setup_env_hash_for
430
431 =over 4
432
433 =item Arguments: path
434
435 =back
436
437 Constructs the C<%ENV> keys for the given path, by calling
438 C<build_environment_vars_for>.
439
440 =head2 install_base_perl_path
441
442 =over 4
443
444 =item Arguments: path
445
446 =back
447
448 Returns a path describing where to install the Perl modules for this local
449 library installation. Appends the directories C<lib> and C<perl5> to the given
450 path.
451
452 =head2 install_base_arch_path
453
454 =over 4
455
456 =item Arguments: path
457
458 =back
459
460 Returns a path describing where to install the architecture-specific Perl
461 modules for this local library installation. Based on the
462 L</install_base_perl_path> method's return value, and appends the value of
463 C<$Config{archname}>.
464
465 =head2 install_base_bin_path
466
467 =over 4
468
469 =item Arguments: path
470
471 =back
472
473 Returns a path describing where to install the executable programs for this
474 local library installation. Based on the L</install_base_perl_path> method's
475 return value, and appends the directory C<bin>.
476
477 =head2 modulebuildrc_path
478
479 =over 4
480
481 =item Arguments: path
482
483 =back
484
485 Returns a path describing where to install the C<.modulebuildrc> file, based on
486 the given path.
487
488 =head2 resolve_empty_path
489
490 =over 4
491
492 =item Arguments: path
493
494 =back
495
496 Builds and returns the base path into which to set up the local module
497 installation. Defaults to C<~/perl5>.
498
499 =head2 resolve_home_path
500
501 =over 4
502
503 =item Arguments: path
504
505 =back
506
507 Attempts to find the user's home directory. If installed, uses C<File::HomeDir>
508 for this purpose. If no definite answer is available, throws an exception.
509
510 =head2 resolve_relative_path
511
512 =over 4
513
514 =item Arguments: path
515
516 =back
517
518 Translates the given path into an absolute path.
519
520 =head2 resolve_path
521
522 =over 4
523
524 =item Arguments: path
525
526 =back
527
528 Calls the following in a pipeline, passing the result from the previous to the
529 next, in an attempt to find where to configure the environment for a local
530 library installation: L</resolve_empty_path>, L</resolve_home_path>,
531 L</resolve_relative_path>. Passes the given path argument to
532 L</resolve_empty_path> which then returns a result that is passed to
533 L</resolve_home_path>, which then has its result passed to
534 L</resolve_relative_path>. The result of this final call is returned from
535 L</resolve_path>.
536
537 =head1 A WARNING ABOUT UNINST=1
538
539 Be careful about using local::lib in combination with "make install UNINST=1".
540 The idea of this feature is that will uninstall an old version of a module
541 before installing a new one. However it lacks a safety check that the old
542 version and the new version will go in the same directory. Used in combination
543 with local::lib, you can potentially delete a globally accessible version of a
544 module while installing the new version in a local place. Only combine "make
545 install UNINST=1" and local::lib if you understand these possible consequences.
546
547 =head1 LIMITATIONS
548
549 Rather basic shell detection. Right now anything with csh in its name is
550 assumed to be a C shell or something compatible, and everything else is assumed
551 to be Bourne. If the C<SHELL> environment variable is not set, a
552 Bourne-compatible shell is assumed.
553
554 Bootstrap is a hack and will use CPAN.pm for ExtUtils::MakeMaker even if you
555 have CPANPLUS installed.
556
557 Kills any existing PERL5LIB, PERL_MM_OPT or MODULEBUILDRC.
558
559 Should probably auto-fixup CPAN config if not already done.
560
561 Patches very much welcome for any of the above.
562
563 =head1 ENVIRONMENT
564
565 =over 4
566
567 =item SHELL
568
569 local::lib looks at the user's C<SHELL> environment variable when printing out
570 commands to add to the shell configuration file.
571
572 =back
573
574 =head1 AUTHOR
575
576 Matt S Trout <mst@shadowcat.co.uk> http://www.shadowcat.co.uk/
577
578 auto_install fixes kindly sponsored by http://www.takkle.com/
579
580 =head1 CONTRIBUTORS
581
582 Patches to correctly output commands for csh style shells, as well as some
583 documentation additions, contributed by Christopher Nehren <apeiron@cpan.org>.
584
585 '--self-contained' feature contributed by Mark Stosberg <mark@summersault.com>.
586
587 Doc patches for a custom local::lib patch contributed by Torsten Raudssus
588 <torsten@raudssus.de>.
589
590 =head1 LICENSE
591
592 This library is free software under the same license as perl itself
593
594 =cut
595
596 1;