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