9440ad33c97d787fce1de2cdd72f7f8c19b9fcb3
[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.004002'; # 1.4.2
15
16 sub import {
17   my ($class, @args) = @_;
18
19   # Remember what PERL5LIB was when we started
20   my $perl5lib = $ENV{PERL5LIB};
21
22   # The path is required, but last in the list, so we pop, not shift here. 
23   my $path = pop @args;
24   $path = $class->resolve_path($path);
25   $class->setup_local_lib_for($path);
26
27   # Handle the '--self-contained' option
28   my $flag = shift @args;  
29   no warnings 'uninitialized'; # the flag is optional 
30   # make sure fancy dashes cause an error
31   if ($flag =~ /−/) {
32       die <<'DEATH';
33 WHOA THERE! It looks like you've got some fancy dashes in your commandline!
34 These are *not* the traditional -- dashes that software recognizes. You
35 probably got these by copy-pasting from the perldoc for this module as
36 rendered by a UTF8-capable formatter. This most typically happens on an OS X
37 terminal, but can happen elsewhere too. Please try again after replacing the
38 dashes with normal minus signs.
39 DEATH
40   }
41   if ($flag eq '--self-contained') {
42     # The only directories that remain are those that we just defined and those where core modules are stored. 
43     # We put PERL5LIB first, so it'll be favored over privlibexp and archlibexp
44     my %seen;
45     @INC = grep { ! $seen{$_}++ } (
46       $class->install_base_perl_path($path),
47       $class->install_base_arch_path($path),
48       split( $Config{path_sep}, $perl5lib ),
49       $Config::Config{privlibexp},
50       $Config::Config{archlibexp}
51   );
52     
53     # We explicitly set PERL5LIB here to the above de-duped list to prevent
54     # @INC from growing with each invocation 
55     $ENV{PERL5LIB} = join( $Config{path_sep}, @INC );
56   }
57   elsif (defined $flag) {
58       die "unrecognized import argument: $flag";
59   }
60
61   for (@INC) { # Untaint @INC
62     next if ref; # Skip entry if it is an ARRAY, CODE, blessed, etc.
63     m/(.*)/ and $_ = $1;
64   }
65 }
66
67 sub pipeline;
68
69 sub pipeline {
70   my @methods = @_;
71   my $last = pop(@methods);
72   if (@methods) {
73     \sub {
74       my ($obj, @args) = @_;
75       $obj->${pipeline @methods}(
76         $obj->$last(@args)
77       );
78     };
79   } else {
80     \sub {
81       shift->$last(@_);
82     };
83   }
84 }
85
86 =begin testing
87
88 #:: test pipeline
89
90 package local::lib;
91
92 { package Foo; sub foo { -$_[1] } sub bar { $_[1]+2 } sub baz { $_[1]+3 } }
93 my $foo = bless({}, 'Foo');                                                 
94 Test::More::ok($foo->${pipeline qw(foo bar baz)}(10) == -15);
95
96 =end testing
97
98 =cut
99
100 sub resolve_path {
101   my ($class, $path) = @_;
102   $class->${pipeline qw(
103     resolve_relative_path
104     resolve_home_path
105     resolve_empty_path
106   )}($path);
107 }
108
109 sub resolve_empty_path {
110   my ($class, $path) = @_;
111   if (defined $path) {
112     $path;
113   } else {
114     '~/perl5';
115   }
116 }
117
118 =begin testing
119
120 #:: test classmethod setup
121
122 my $c = 'local::lib';
123
124 =end testing
125
126 =begin testing
127
128 #:: test classmethod
129
130 is($c->resolve_empty_path, '~/perl5');
131 is($c->resolve_empty_path('foo'), 'foo');
132
133 =end testing
134
135 =cut
136
137 sub resolve_home_path {
138   my ($class, $path) = @_;
139   return $path unless ($path =~ /^~/);
140   my ($user) = ($path =~ /^~([^\/]+)/); # can assume ^~ so undef for 'us'
141   my $tried_file_homedir;
142   my $homedir = do {
143     if (eval { require File::HomeDir } && $File::HomeDir::VERSION >= 0.65) {
144       $tried_file_homedir = 1;
145       if (defined $user) {
146         File::HomeDir->users_home($user);
147       } else {
148         File::HomeDir->my_home;
149       }
150     } else {
151       if (defined $user) {
152         (getpwnam $user)[7];
153       } else {
154         if (defined $ENV{HOME}) {
155           $ENV{HOME};
156         } else {
157           (getpwuid $<)[7];
158         }
159       }
160     }
161   };
162   unless (defined $homedir) {
163     Carp::croak(
164       "Couldn't resolve homedir for "
165       .(defined $user ? $user : 'current user')
166       .($tried_file_homedir ? '' : ' - consider installing File::HomeDir')
167     );
168   }
169   $path =~ s/^~[^\/]*/$homedir/;
170   $path;
171 }
172
173 sub resolve_relative_path {
174   my ($class, $path) = @_;
175   $path = File::Spec->rel2abs($path);
176 }
177
178 =begin testing
179
180 #:: test classmethod
181
182 local *File::Spec::rel2abs = sub { shift; 'FOO'.shift; };
183 is($c->resolve_relative_path('bar'),'FOObar');
184
185 =end testing
186
187 =cut
188
189 sub setup_local_lib_for {
190   my ($class, $path) = @_;
191   $path = $class->ensure_dir_structure_for($path);
192   if ($0 eq '-') {
193     $class->print_environment_vars_for($path);
194     exit 0;
195   } else {
196     $class->setup_env_hash_for($path);
197     unshift(@INC, split($Config{path_sep}, $ENV{PERL5LIB}));
198   }
199 }
200
201 sub modulebuildrc_path {
202   my ($class, $path) = @_;
203   File::Spec->catfile($path, '.modulebuildrc');
204 }
205
206 sub install_base_bin_path {
207   my ($class, $path) = @_;
208   File::Spec->catdir($path, 'bin');
209 }
210
211 sub install_base_perl_path {
212   my ($class, $path) = @_;
213   File::Spec->catdir($path, 'lib', 'perl5');
214 }
215
216 sub install_base_arch_path {
217   my ($class, $path) = @_;
218   File::Spec->catdir($class->install_base_perl_path($path), $Config{archname});
219 }
220
221 sub ensure_dir_structure_for {
222   my ($class, $path) = @_;
223   unless (-d $path) {
224     warn "Attempting to create directory ${path}\n";
225   }
226   File::Path::mkpath($path);
227   # Need to have the path exist to make a short name for it, so
228   # converting to a short name here.
229   $path = Win32::GetShortPathName($path) if $^O eq 'MSWin32';
230   my $modulebuildrc_path = $class->modulebuildrc_path($path);
231   if (-e $modulebuildrc_path) {
232     unless (-f _) {
233       Carp::croak("${modulebuildrc_path} exists but is not a plain file");
234     }
235   } else {
236     warn "Attempting to create file ${modulebuildrc_path}\n";
237     open MODULEBUILDRC, '>', $modulebuildrc_path
238       || Carp::croak("Couldn't open ${modulebuildrc_path} for writing: $!");
239     print MODULEBUILDRC qq{install  --install_base  ${path}\n}
240       || Carp::croak("Couldn't write line to ${modulebuildrc_path}: $!");
241     close MODULEBUILDRC
242       || Carp::croak("Couldn't close file ${modulebuildrc_path}: $@");
243   }
244
245   return $path;
246 }
247
248 sub INTERPOLATE_ENV () { 1 }
249 sub LITERAL_ENV     () { 0 }
250
251 sub print_environment_vars_for {
252   my ($class, $path) = @_;
253   my @envs = $class->build_environment_vars_for($path, LITERAL_ENV);
254   my $out = '';
255
256   # rather basic csh detection, goes on the assumption that something won't
257   # call itself csh unless it really is. also, default to bourne in the
258   # pathological situation where a user doesn't have $ENV{SHELL} defined.
259   # note also that shells with funny names, like zoid, are assumed to be
260   # bourne.
261   my $shellbin = 'sh';
262   if(defined $ENV{'SHELL'}) {
263       my @shell_bin_path_parts = File::Spec->splitpath($ENV{'SHELL'});
264       $shellbin = $shell_bin_path_parts[-1];
265   }
266   my $shelltype = do {
267       local $_ = $shellbin;
268       if(/csh/) {
269           'csh'
270       } else {
271           'bourne'
272       }
273   };
274
275   # Win32 uses this variable.
276   if (defined $ENV{'COMSPEC'}) {
277       my @shell_bin_path_parts = File::Spec->splitpath($ENV{'COMSPEC'});
278       $shellbin = $shell_bin_path_parts[-1];
279          $shelltype = do {
280                  local $_ = $shellbin;
281                  if(/command\.com/) {
282                          'win32'
283                  } elsif(/cmd\.exe/) {
284                          'win32'
285                  } elsif(/4nt\.exe/) {
286                          'win32'
287                  } else {
288                          $shelltype
289                  }
290          };
291   }
292
293   while (@envs) {
294     my ($name, $value) = (shift(@envs), shift(@envs));
295     $value =~ s/(\\")/\\$1/g;
296     $out .= $class->${\"build_${shelltype}_env_declaration"}($name, $value);
297   }
298   print $out;
299 }
300
301 # simple routines that take two arguments: an %ENV key and a value. return
302 # strings that are suitable for passing directly to the relevant shell to set
303 # said key to said value.
304 sub build_bourne_env_declaration {
305   my $class = shift;
306   my($name, $value) = @_;
307   return qq{export ${name}="${value}"\n};
308 }
309
310 sub build_csh_env_declaration {
311   my $class = shift;
312   my($name, $value) = @_;
313   return qq{setenv ${name} "${value}"\n};
314 }
315
316 sub build_win32_env_declaration {
317   my $class = shift;
318   my($name, $value) = @_;
319   return qq{set ${name}=${value}\n};
320 }
321
322 sub setup_env_hash_for {
323   my ($class, $path) = @_;
324   my %envs = $class->build_environment_vars_for($path, INTERPOLATE_ENV);
325   @ENV{keys %envs} = values %envs;
326 }
327
328 sub build_environment_vars_for {
329   my ($class, $path, $interpolate) = @_;
330   return (
331     MODULEBUILDRC => $class->modulebuildrc_path($path),
332     PERL_MM_OPT => "INSTALL_BASE=${path}",
333     PERL5LIB => join($Config{path_sep},
334                   $class->install_base_perl_path($path),
335                   $class->install_base_arch_path($path),
336                   ($ENV{PERL5LIB} ?
337                     ($interpolate == INTERPOLATE_ENV
338                       ? ($ENV{PERL5LIB})
339                       : (($^O ne 'MSWin32') ? '$PERL5LIB' : '%PERL5LIB%' ))
340                     : ())
341                 ),
342     PATH => join($Config{path_sep},
343               $class->install_base_bin_path($path),
344               ($interpolate == INTERPOLATE_ENV
345                 ? $ENV{PATH}
346                 : (($^O ne 'MSWin32') ? '$PATH' : '%PATH%' ))
347              ),
348   )
349 }
350
351 =begin testing
352
353 #:: test classmethod
354
355 File::Path::rmtree('t/var/splat');
356
357 $c->ensure_dir_structure_for('t/var/splat');
358
359 ok(-d 't/var/splat');
360
361 ok(-f 't/var/splat/.modulebuildrc');
362
363 =end testing
364
365 =head1 NAME
366
367 local::lib - create and use a local lib/ for perl modules with PERL5LIB
368
369 =head1 SYNOPSIS
370
371 In code -
372
373   use local::lib; # sets up a local lib at ~/perl5
374
375   use local::lib '~/foo'; # same, but ~/foo
376
377   # Or...
378   use FindBin;
379   use local::lib "$FindBin::Bin/../support";  # app-local support library
380
381 From the shell -
382
383   # Install LWP and it's missing dependencies to the 'my_lwp' directory
384   perl -MCPAN -Mlocal::lib=my_lwp -e 'CPAN::install(LWP)'
385
386   # Install LWP and *all non-core* dependencies to the 'my_lwp' directory 
387   perl -MCPAN -Mlocal::lib=--self-contained,my_lwp -e 'CPAN::install(LWP)'
388
389   # Just print out useful shell commands
390   $ perl -Mlocal::lib
391   export MODULEBUILDRC=/home/username/perl/.modulebuildrc
392   export PERL_MM_OPT='INSTALL_BASE=/home/username/perl'
393   export PERL5LIB='/home/username/perl/lib/perl5:/home/username/perl/lib/perl5/i386-linux'
394   export PATH="/home/username/perl/bin:$PATH"
395
396 To bootstrap if you don't have local::lib itself installed -
397
398   <download local::lib tarball from CPAN, unpack and cd into dir>
399
400   $ perl Makefile.PL --bootstrap
401   $ make test && make install
402
403   $ echo 'eval $(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)' >>~/.bashrc
404
405   # Or for C shells...
406
407   $ /bin/csh
408   % echo $SHELL
409   /bin/csh
410   % perl -I$HOME/perl5/lib/perl5 -Mlocal::lib >> ~/.cshrc
411
412 You can also pass --bootstrap=~/foo to get a different location -
413
414   $ perl Makefile.PL --bootstrap=~/foo
415   $ make test && make install
416
417   $ echo 'eval $(perl -I$HOME/foo/lib/perl5 -Mlocal::lib=$HOME/foo)' >>~/.bashrc
418
419 If you want to install multiple Perl module environments, say for application evelopment, 
420 install local::lib globally and then:
421
422     $ cd ~/mydir1
423     $ perl -Mlocal::lib=./
424     $ eval $(perl -Mlocal::lib=./)  ### To set the environment for this shell alone
425     $ printenv  ### You will see that ~/mydir1 is in the PERL5LIB
426     $ perl -MCPAN -e install ...    ### whatever modules you want
427     $ cd ../mydir2
428     ... REPEAT ...
429
430 For multiple environments for multiple apps you may need to include a modified version of 
431 the C<< use FindBin >> instructions in the "In code" sample above. If you did something like
432 the above, you have a set of Perl modules at C<< ~/mydir1/lib >>. If you have a script at
433 C<< ~/mydir1/scripts/myscript.pl >>, you need to tell it where to find the modules you installed 
434 for it at C<< ~/mydir1/lib >>.
435
436 In C<< ~/mydir1/scripts/myscript.pl >>:
437
438     use strict;
439     use warnings;
440     use local::lib "$FindBin::Bin/..";  ### points to ~/mydir1 and local::lib finds lib
441     use lib "$FindBin::Bin/../lib";     ### points to ~/mydir1/lib
442
443 Put this before any BEGIN { ... } blocks that require the modules you installed.
444
445 =head2 Differences when using this module under Win32
446
447     C:\>perl -Mlocal::lib
448     set MODULEBUILDRC=C:\DOCUME~1\ADMINI~1\perl5\.modulebuildrc
449     set PERL_MM_OPT=INSTALL_BASE=C:\DOCUME~1\ADMINI~1\perl5
450     set PERL5LIB=C:\DOCUME~1\ADMINI~1\perl5\lib\perl5;C:\DOCUME~1\ADMINI~1\perl5\lib\perl5\MSWin32-x86-multi-thread
451     set PATH=C:\DOCUME~1\ADMINI~1\perl5\bin;%PATH%
452
453        ### To set the environment for this shell alone
454     C:\>perl -Mlocal::lib > %TEMP%\tmp.bat && %TEMP%\tmp.bat && del %TEMP%\temp.bat
455     ### instead of $(perl -Mlocal::lib=./)
456
457 If you want the environment entries to persist, you'll need to add then to the
458 Control Panel's System applet yourself at the moment.
459
460 The "~" is translated to the user's profile directory (the directory named for
461 the user under "Documents and Settings" (Windows XP or earlier) or "Users"
462 (Windows Vista or later) unless $ENV{HOME} exists. After that, the home
463 directory is translated to a short name (which means the directory must exist)
464 and the subdirectories are created.
465
466 =head1 DESCRIPTION
467
468 This module provides a quick, convenient way of bootstrapping a user-local Perl
469 module library located within the user's home directory. It also constructs and
470 prints out for the user the list of environment variables using the syntax
471 appropriate for the user's current shell (as specified by the C<SHELL>
472 environment variable), suitable for directly adding to one's shell configuration
473 file.
474
475 More generally, local::lib allows for the bootstrapping and usage of a directory
476 containing Perl modules outside of Perl's C<@INC>. This makes it easier to ship
477 an application with an app-specific copy of a Perl module, or collection of
478 modules. Useful in cases like when an upstream maintainer hasn't applied a patch
479 to a module of theirs that you need for your application.
480
481 On import, local::lib sets the following environment variables to appropriate
482 values:
483
484 =over 4
485
486 =item MODULEBUILDRC
487
488 =item PERL_MM_OPT
489
490 =item PERL5LIB
491
492 =item PATH
493
494 PATH is appended to, rather than clobbered.
495
496 =back
497
498 These values are then available for reference by any code after import.
499
500 =head1 METHODS
501
502 =head2 ensure_directory_structure_for
503
504 =over 4
505
506 =item Arguments: path
507
508 =back
509
510 Attempts to create the given path, and all required parent directories. Throws
511 an exception on failure.
512
513 =head2 print_environment_vars_for
514
515 =over 4
516
517 =item Arguments: path
518
519 =back
520
521 Prints to standard output the variables listed above, properly set to use the
522 given path as the base directory.
523
524 =head2 setup_env_hash_for
525
526 =over 4
527
528 =item Arguments: path
529
530 =back
531
532 Constructs the C<%ENV> keys for the given path, by calling
533 C<build_environment_vars_for>.
534
535 =head2 install_base_perl_path
536
537 =over 4
538
539 =item Arguments: path
540
541 =back
542
543 Returns a path describing where to install the Perl modules for this local
544 library installation. Appends the directories C<lib> and C<perl5> to the given
545 path.
546
547 =head2 install_base_arch_path
548
549 =over 4
550
551 =item Arguments: path
552
553 =back
554
555 Returns a path describing where to install the architecture-specific Perl
556 modules for this local library installation. Based on the
557 L</install_base_perl_path> method's return value, and appends the value of
558 C<$Config{archname}>.
559
560 =head2 install_base_bin_path
561
562 =over 4
563
564 =item Arguments: path
565
566 =back
567
568 Returns a path describing where to install the executable programs for this
569 local library installation. Based on the L</install_base_perl_path> method's
570 return value, and appends the directory C<bin>.
571
572 =head2 modulebuildrc_path
573
574 =over 4
575
576 =item Arguments: path
577
578 =back
579
580 Returns a path describing where to install the C<.modulebuildrc> file, based on
581 the given path.
582
583 =head2 resolve_empty_path
584
585 =over 4
586
587 =item Arguments: path
588
589 =back
590
591 Builds and returns the base path into which to set up the local module
592 installation. Defaults to C<~/perl5>.
593
594 =head2 resolve_home_path
595
596 =over 4
597
598 =item Arguments: path
599
600 =back
601
602 Attempts to find the user's home directory. If installed, uses C<File::HomeDir>
603 for this purpose. If no definite answer is available, throws an exception.
604
605 =head2 resolve_relative_path
606
607 =over 4
608
609 =item Arguments: path
610
611 =back
612
613 Translates the given path into an absolute path.
614
615 =head2 resolve_path
616
617 =over 4
618
619 =item Arguments: path
620
621 =back
622
623 Calls the following in a pipeline, passing the result from the previous to the
624 next, in an attempt to find where to configure the environment for a local
625 library installation: L</resolve_empty_path>, L</resolve_home_path>,
626 L</resolve_relative_path>. Passes the given path argument to
627 L</resolve_empty_path> which then returns a result that is passed to
628 L</resolve_home_path>, which then has its result passed to
629 L</resolve_relative_path>. The result of this final call is returned from
630 L</resolve_path>.
631
632 =head1 A WARNING ABOUT UNINST=1
633
634 Be careful about using local::lib in combination with "make install UNINST=1".
635 The idea of this feature is that will uninstall an old version of a module
636 before installing a new one. However it lacks a safety check that the old
637 version and the new version will go in the same directory. Used in combination
638 with local::lib, you can potentially delete a globally accessible version of a
639 module while installing the new version in a local place. Only combine "make
640 install UNINST=1" and local::lib if you understand these possible consequences.
641
642 =head1 LIMITATIONS
643
644 Rather basic shell detection. Right now anything with csh in its name is
645 assumed to be a C shell or something compatible, and everything else is assumed
646 to be Bourne, except on Win32 systems. If the C<SHELL> environment variable is
647 not set, a Bourne-compatible shell is assumed.
648
649 Bootstrap is a hack and will use CPAN.pm for ExtUtils::MakeMaker even if you
650 have CPANPLUS installed.
651
652 Kills any existing PERL5LIB, PERL_MM_OPT or MODULEBUILDRC.
653
654 Should probably auto-fixup CPAN config if not already done.
655
656 Patches very much welcome for any of the above.
657
658 On Win32 systems, does not have a way to write the created environment variables
659 to the registry, so that they can persist through a reboot.
660
661 =head1 TROUBLESHOOTING
662
663 If you've configured local::lib to install CPAN modules somewhere in to your
664 home directory, and at some point later you try to install a module with C<cpan
665 -i Foo::Bar>, but it fails with an error like: C<Warning: You do not have
666 permissions to install into /usr/lib64/perl5/site_perl/5.8.8/x86_64-linux at
667 /usr/lib64/perl5/5.8.8/Foo/Bar.pm> and buried within the install log is an
668 error saying C<'INSTALL_BASE' is not a known MakeMaker parameter name>, then
669 you've somehow lost your updated ExtUtils::MakeMaker module.
670
671 To remedy this situation, rerun the bootstrapping procedure documented above.
672
673 Then, run C<rm -r ~/.cpan/build/Foo-Bar*>
674
675 Finally, re-run C<cpan -i Foo::Bar> and it should install without problems.
676
677 =head1 ENVIRONMENT
678
679 =over 4
680
681 =item SHELL
682
683 =item COMSPEC
684
685 local::lib looks at the user's C<SHELL> environment variable when printing out
686 commands to add to the shell configuration file.
687
688 On Win32 systems, C<COMSPEC> is also examined.
689
690 =back
691
692 =head1 AUTHOR
693
694 Matt S Trout <mst@shadowcat.co.uk> http://www.shadowcat.co.uk/
695
696 auto_install fixes kindly sponsored by http://www.takkle.com/
697
698 =head1 CONTRIBUTORS
699
700 Patches to correctly output commands for csh style shells, as well as some
701 documentation additions, contributed by Christopher Nehren <apeiron@cpan.org>.
702
703 '--self-contained' feature contributed by Mark Stosberg <mark@summersault.com>.
704
705 Doc patches for a custom local::lib directory contributed by Torsten Raudssus
706 <torsten@raudssus.de>.
707
708 Hans Dieter Pearcey <hdp@cpan.org> sent in some additional tests for ensuring
709 things will install properly, submitted a fix for the bug causing problems with
710 writing Makefiles during bootstrapping, contributed an example program, and
711 submitted yet another fix to ensure that local::lib can install and bootstrap
712 properly. Many, many thanks!
713
714 pattern of Freenode IRC contributed the beginnings of the Troubleshooting
715 section. Many thanks!
716
717 Patch to add Win32 support contributed by Curtis Jewell <csjewell@cpan.org>.
718
719 =head1 LICENSE
720
721 This library is free software under the same license as perl itself.
722
723 =cut
724
725 1;