Fixed up INC untaint procedure to skip/ignore CODE, ARRAY, blessed entries.
[p5sagit/local-lib.git] / lib / local / lib.pm
CommitLineData
b5cc15f7 1use strict;
2use warnings;
3
4package local::lib;
5
c1441fb6 6use 5.008001; # probably works with earlier versions but I'm not supporting them
7 # (patches would, of course, be welcome)
b5cc15f7 8
9use File::Spec ();
10use File::Path ();
11use Carp ();
12use Config;
13
9a021b2b 14our $VERSION = '1.004001'; # 1.4.1
b5cc15f7 15
16sub import {
0fb70b9a 17 my ($class, @args) = @_;
18
e4892f2b 19 # Remember what PERL5LIB was when we started
20 my $perl5lib = $ENV{PERL5LIB};
21
0fb70b9a 22 # The path is required, but last in the list, so we pop, not shift here.
23 my $path = pop @args;
b5cc15f7 24 $path = $class->resolve_path($path);
25 $class->setup_local_lib_for($path);
0fb70b9a 26
27 # Handle the '--self-contained' option
28 my $flag = shift @args;
29 no warnings 'uninitialized'; # the flag is optional
d4dbe584 30 # make sure fancy dashes cause an error
31 if ($flag =~ /−/) {
32 die <<'DEATH';
33WHOA THERE! It looks like you've got some fancy dashes in your commandline!
34These are *not* the traditional -- dashes that software recognizes. You
35probably got these by copy-pasting from the perldoc for this module as
36rendered by a UTF8-capable formatter. This most typically happens on an OS X
37terminal, but can happen elsewhere too. Please try again after replacing the
38dashes with normal minus signs.
39DEATH
40 }
0fb70b9a 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.
e4892f2b 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;
0fb70b9a 48 }
49 elsif (defined $flag) {
50 die "unrecognized import argument: $flag";
51 }
52
6e5079df 53 for (@INC) { # Untaint @INC
54 next if ref; # Skip entry if it is an ARRAY, CODE, blessed, etc.
55 m/(.*)/ and $_ = $1;
56 }
b5cc15f7 57}
58
5b94dce5 59sub pipeline;
b5cc15f7 60
5b94dce5 61sub pipeline {
b5cc15f7 62 my @methods = @_;
63 my $last = pop(@methods);
64 if (@methods) {
65 \sub {
66 my ($obj, @args) = @_;
5b94dce5 67 $obj->${pipeline @methods}(
b5cc15f7 68 $obj->$last(@args)
69 );
70 };
71 } else {
72 \sub {
73 shift->$last(@_);
74 };
75 }
76}
77
275c9dae 78=begin testing
79
80#:: test pipeline
b5cc15f7 81
82package local::lib;
83
84{ package Foo; sub foo { -$_[1] } sub bar { $_[1]+2 } sub baz { $_[1]+3 } }
85my $foo = bless({}, 'Foo');
4c375968 86Test::More::ok($foo->${pipeline qw(foo bar baz)}(10) == -15);
b5cc15f7 87
275c9dae 88=end testing
89
b5cc15f7 90=cut
91
92sub resolve_path {
93 my ($class, $path) = @_;
5b94dce5 94 $class->${pipeline qw(
b5cc15f7 95 resolve_relative_path
96 resolve_home_path
97 resolve_empty_path
98 )}($path);
99}
100
101sub resolve_empty_path {
102 my ($class, $path) = @_;
103 if (defined $path) {
104 $path;
105 } else {
106 '~/perl5';
107 }
108}
109
275c9dae 110=begin testing
111
112#:: test classmethod setup
b5cc15f7 113
114my $c = 'local::lib';
115
275c9dae 116=end testing
117
118=begin testing
b5cc15f7 119
275c9dae 120#:: test classmethod
b5cc15f7 121
122is($c->resolve_empty_path, '~/perl5');
123is($c->resolve_empty_path('foo'), 'foo');
124
275c9dae 125=end testing
126
b5cc15f7 127=cut
128
129sub 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 {
dc8ddd06 140 File::HomeDir->my_home;
b5cc15f7 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
165sub resolve_relative_path {
166 my ($class, $path) = @_;
167 File::Spec->rel2abs($path);
168}
169
275c9dae 170=begin testing
171
172#:: test classmethod
b5cc15f7 173
174local *File::Spec::rel2abs = sub { shift; 'FOO'.shift; };
175is($c->resolve_relative_path('bar'),'FOObar');
176
275c9dae 177=end testing
178
b5cc15f7 179=cut
180
181sub 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);
f9c6b7ff 189 unshift(@INC, split(':', $ENV{PERL5LIB}));
b5cc15f7 190 }
191}
192
193sub modulebuildrc_path {
194 my ($class, $path) = @_;
195 File::Spec->catfile($path, '.modulebuildrc');
196}
197
198sub install_base_bin_path {
199 my ($class, $path) = @_;
200 File::Spec->catdir($path, 'bin');
201}
202
203sub install_base_perl_path {
204 my ($class, $path) = @_;
205 File::Spec->catdir($path, 'lib', 'perl5');
206}
207
208sub install_base_arch_path {
209 my ($class, $path) = @_;
210 File::Spec->catdir($class->install_base_perl_path($path), $Config{archname});
211}
212
213sub 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: $!");
18bb63e0 228 print MODULEBUILDRC qq{install --install_base ${path}\n}
b5cc15f7 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
c2447f35 235sub INTERPOLATE_ENV () { 1 }
236sub LITERAL_ENV () { 0 }
b5cc15f7 237
238sub print_environment_vars_for {
239 my ($class, $path) = @_;
c2447f35 240 my @envs = $class->build_environment_vars_for($path, LITERAL_ENV);
b5cc15f7 241 my $out = '';
1bc71e56 242
0353dbc0 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 }
1bc71e56 253 my $shelltype = do {
254 local $_ = $shellbin;
b42496e0 255 if(/csh/) {
1bc71e56 256 'csh'
b42496e0 257 } else {
1bc71e56 258 'bourne'
259 }
260 };
261
b5cc15f7 262 while (@envs) {
263 my ($name, $value) = (shift(@envs), shift(@envs));
264 $value =~ s/(\\")/\\$1/g;
1bc71e56 265 $out .= $class->${\"build_${shelltype}_env_declaration"}($name, $value);
b5cc15f7 266 }
267 print $out;
268}
269
1bc71e56 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.
273sub build_bourne_env_declaration {
274 my $class = shift;
275 my($name, $value) = @_;
276 return qq{export ${name}="${value}"\n};
277}
278
279sub build_csh_env_declaration {
280 my $class = shift;
281 my($name, $value) = @_;
282 return qq{setenv ${name} "${value}"\n};
283}
284
b5cc15f7 285sub setup_env_hash_for {
286 my ($class, $path) = @_;
c2447f35 287 my %envs = $class->build_environment_vars_for($path, INTERPOLATE_ENV);
b5cc15f7 288 @ENV{keys %envs} = values %envs;
289}
290
291sub 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),
c2447f35 299 ($ENV{PERL5LIB} ?
300 ($interpolate == INTERPOLATE_ENV
301 ? ($ENV{PERL5LIB})
302 : ('$PERL5LIB'))
303 : ())
b5cc15f7 304 ),
305 PATH => join(':',
306 $class->install_base_bin_path($path),
c2447f35 307 ($interpolate == INTERPOLATE_ENV
b5cc15f7 308 ? $ENV{PATH}
309 : '$PATH')
310 ),
311 )
312}
313
275c9dae 314=begin testing
315
316#:: test classmethod
b5cc15f7 317
318File::Path::rmtree('t/var/splat');
319
4c375968 320$c->ensure_dir_structure_for('t/var/splat');
b5cc15f7 321
322ok(-d 't/var/splat');
323
324ok(-f 't/var/splat/.modulebuildrc');
325
275c9dae 326=end testing
327
b5cc15f7 328=head1 NAME
329
330local::lib - create and use a local lib/ for perl modules with PERL5LIB
331
332=head1 SYNOPSIS
333
334In code -
335
336 use local::lib; # sets up a local lib at ~/perl5
337
338 use local::lib '~/foo'; # same, but ~/foo
339
1bc71e56 340 # Or...
341 use FindBin;
342 use local::lib "$FindBin::Bin/../support"; # app-local support library
343
b5cc15f7 344From the shell -
345
0fb70b9a 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
b5cc15f7 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
bc30e1d5 359To bootstrap if you don't have local::lib itself installed -
360
e423efce 361 <download local::lib tarball from CPAN, unpack and cd into dir>
715c31a0 362
bc30e1d5 363 $ perl Makefile.PL --bootstrap
364 $ make test && make install
715c31a0 365
dc8ddd06 366 $ echo 'eval $(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)' >>~/.bashrc
715c31a0 367
618272fe 368 # Or for C shells...
715c31a0 369
618272fe 370 $ /bin/csh
371 % echo $SHELL
372 /bin/csh
373 % perl -I$HOME/perl5/lib/perl5 -Mlocal::lib >> ~/.cshrc
dc8ddd06 374
8b1e8e69 375You 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
618272fe 381
977a9ca3 382If you want to install multiple Perl module environments, say for application evelopment,
383install 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
393For multiple environments for multiple apps you may need to include a modified version of
394the C<< use FindBin >> instructions in the "In code" sample above. If you did something like
395the above, you have a set of Perl modules at C<< ~/mydir1/lib >>. If you have a script at
396C<< ~/mydir1/scripts/myscript.pl >>, you need to tell it where to find the modules you installed
c4dbb66c 397for it at C<< ~/mydir1/lib >>.
977a9ca3 398
399In 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
406Put this before any BEGIN { ... } blocks that require the modules you installed.
407
618272fe 408=head1 DESCRIPTION
409
410This module provides a quick, convenient way of bootstrapping a user-local Perl
411module library located within the user's home directory. It also constructs and
412prints out for the user the list of environment variables using the syntax
413appropriate for the user's current shell (as specified by the C<SHELL>
414environment variable), suitable for directly adding to one's shell configuration
415file.
dc8ddd06 416
1bc71e56 417More generally, local::lib allows for the bootstrapping and usage of a directory
418containing Perl modules outside of Perl's C<@INC>. This makes it easier to ship
419an application with an app-specific copy of a Perl module, or collection of
420modules. Useful in cases like when an upstream maintainer hasn't applied a patch
421to a module of theirs that you need for your application.
422
423On import, local::lib sets the following environment variables to appropriate
424values:
425
426=over 4
427
428=item MODULEBUILDRC
429
430=item PERL_MM_OPT
431
432=item PERL5LIB
433
434=item PATH
435
436PATH is appended to, rather than clobbered.
437
438=back
439
440These values are then available for reference by any code after import.
441
480e6e85 442=head1 METHODS
443
444=head2 ensure_directory_structure_for
445
446=over 4
447
448=item Arguments: path
449
450=back
451
452Attempts to create the given path, and all required parent directories. Throws
453an exception on failure.
454
455=head2 print_environment_vars_for
456
457=over 4
458
459=item Arguments: path
460
461=back
462
463Prints to standard output the variables listed above, properly set to use the
464given 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
474Constructs the C<%ENV> keys for the given path, by calling
475C<build_environment_vars_for>.
476
477=head2 install_base_perl_path
478
479=over 4
480
481=item Arguments: path
482
483=back
484
485Returns a path describing where to install the Perl modules for this local
486library installation. Appends the directories C<lib> and C<perl5> to the given
487path.
488
489=head2 install_base_arch_path
490
491=over 4
492
493=item Arguments: path
494
495=back
496
497Returns a path describing where to install the architecture-specific Perl
498modules for this local library installation. Based on the
499L</install_base_perl_path> method's return value, and appends the value of
500C<$Config{archname}>.
501
502=head2 install_base_bin_path
503
504=over 4
505
506=item Arguments: path
507
508=back
509
510Returns a path describing where to install the executable programs for this
511local library installation. Based on the L</install_base_perl_path> method's
512return 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
522Returns a path describing where to install the C<.modulebuildrc> file, based on
523the given path.
524
525=head2 resolve_empty_path
526
527=over 4
528
529=item Arguments: path
530
531=back
532
533Builds and returns the base path into which to set up the local module
534installation. Defaults to C<~/perl5>.
535
536=head2 resolve_home_path
537
538=over 4
539
540=item Arguments: path
541
542=back
543
544Attempts to find the user's home directory. If installed, uses C<File::HomeDir>
545for 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
555Translates 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
565Calls the following in a pipeline, passing the result from the previous to the
566next, in an attempt to find where to configure the environment for a local
567library installation: L</resolve_empty_path>, L</resolve_home_path>,
568L</resolve_relative_path>. Passes the given path argument to
569L</resolve_empty_path> which then returns a result that is passed to
570L</resolve_home_path>, which then has its result passed to
571L</resolve_relative_path>. The result of this final call is returned from
572L</resolve_path>.
573
0fb70b9a 574=head1 A WARNING ABOUT UNINST=1
575
576Be careful about using local::lib in combination with "make install UNINST=1".
577The idea of this feature is that will uninstall an old version of a module
578before installing a new one. However it lacks a safety check that the old
579version and the new version will go in the same directory. Used in combination
580with local::lib, you can potentially delete a globally accessible version of a
381738d7 581module while installing the new version in a local place. Only combine "make
0fb70b9a 582install UNINST=1" and local::lib if you understand these possible consequences.
583
dc8ddd06 584=head1 LIMITATIONS
585
618272fe 586Rather basic shell detection. Right now anything with csh in its name is
587assumed to be a C shell or something compatible, and everything else is assumed
1bc71e56 588to be Bourne. If the C<SHELL> environment variable is not set, a
589Bourne-compatible shell is assumed.
dc8ddd06 590
591Bootstrap is a hack and will use CPAN.pm for ExtUtils::MakeMaker even if you
592have CPANPLUS installed.
593
594Kills any existing PERL5LIB, PERL_MM_OPT or MODULEBUILDRC.
595
e423efce 596Should probably auto-fixup CPAN config if not already done.
597
dc8ddd06 598Patches very much welcome for any of the above.
bc30e1d5 599
9a021b2b 600=head1 TROUBLESHOOTING
601
602If you've configured local::lib to install CPAN modules somewhere in to your
603home 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
605permissions 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
607error saying C<'INSTALL_BASE' is not a known MakeMaker parameter name>, then
608you've somehow lost your updated ExtUtils::MakeMaker module.
609
610To remedy this situation, rerun the bootstrapping procedure documented above.
611
612Then, run C<rm -r ~/.cpan/build/Foo-Bar*>
613
614Finally, re-run C<cpan -i Foo::Bar> and it should install without problems.
615
618272fe 616=head1 ENVIRONMENT
617
618=over 4
619
620=item SHELL
621
622local::lib looks at the user's C<SHELL> environment variable when printing out
623commands to add to the shell configuration file.
624
625=back
626
b5cc15f7 627=head1 AUTHOR
628
629Matt S Trout <mst@shadowcat.co.uk> http://www.shadowcat.co.uk/
630
d6b71a2d 631auto_install fixes kindly sponsored by http://www.takkle.com/
632
b5c1154d 633=head1 CONTRIBUTORS
634
635Patches to correctly output commands for csh style shells, as well as some
636documentation additions, contributed by Christopher Nehren <apeiron@cpan.org>.
637
0fb70b9a 638'--self-contained' feature contributed by Mark Stosberg <mark@summersault.com>.
639
c4dbb66c 640Doc patches for a custom local::lib directory contributed by Torsten Raudssus
8b1e8e69 641<torsten@raudssus.de>.
642
be160790 643Hans Dieter Pearcey <hdp@cpan.org> sent in some additional tests for ensuring
9a021b2b 644things will install properly, submitted a fix for the bug causing problems with
645writing Makefiles during bootstrapping, contributed an example program, and
646submitted yet another fix to ensure that local::lib can install and bootstrap
647properly. Many, many thanks!
648
649pattern of Freenode IRC contributed the beginnings of the Troubleshooting
650section. Many thanks!
be160790 651
b5cc15f7 652=head1 LICENSE
653
654This library is free software under the same license as perl itself
655
656=cut
657
6581;