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