heinous bootstrapping code
[p5sagit/local-lib.git] / lib / local / lib.pm
CommitLineData
b5cc15f7 1use strict;
2use warnings;
3
4package local::lib;
5
6use 5.8.1; # probably works with earlier versions but I'm not supporting them
7 # (patches would, of course, be welcome)
8
9use File::Spec ();
10use File::Path ();
11use Carp ();
12use Config;
13
14our $VERSION = '1.000000'; # 1.0.0
15
16sub import {
17 my ($class, $path) = @_;
18 $path = $class->resolve_path($path);
19 $class->setup_local_lib_for($path);
20}
21
5b94dce5 22sub pipeline;
b5cc15f7 23
5b94dce5 24sub pipeline {
b5cc15f7 25 my @methods = @_;
26 my $last = pop(@methods);
27 if (@methods) {
28 \sub {
29 my ($obj, @args) = @_;
5b94dce5 30 $obj->${pipeline @methods}(
b5cc15f7 31 $obj->$last(@args)
32 );
33 };
34 } else {
35 \sub {
36 shift->$last(@_);
37 };
38 }
39}
40
4c375968 41=for test pipeline
b5cc15f7 42
43package local::lib;
44
45{ package Foo; sub foo { -$_[1] } sub bar { $_[1]+2 } sub baz { $_[1]+3 } }
46my $foo = bless({}, 'Foo');
4c375968 47Test::More::ok($foo->${pipeline qw(foo bar baz)}(10) == -15);
b5cc15f7 48
49=cut
50
51sub resolve_path {
52 my ($class, $path) = @_;
5b94dce5 53 $class->${pipeline qw(
b5cc15f7 54 resolve_relative_path
55 resolve_home_path
56 resolve_empty_path
57 )}($path);
58}
59
60sub resolve_empty_path {
61 my ($class, $path) = @_;
62 if (defined $path) {
63 $path;
64 } else {
65 '~/perl5';
66 }
67}
68
69=for test classmethod setup
70
71my $c = 'local::lib';
72
73=cut
74
75=for test classmethod
76
77is($c->resolve_empty_path, '~/perl5');
78is($c->resolve_empty_path('foo'), 'foo');
79
80=cut
81
82sub resolve_home_path {
83 my ($class, $path) = @_;
84 return $path unless ($path =~ /^~/);
85 my ($user) = ($path =~ /^~([^\/]+)/); # can assume ^~ so undef for 'us'
86 my $tried_file_homedir;
87 my $homedir = do {
88 if (eval { require File::HomeDir } && $File::HomeDir::VERSION >= 0.65) {
89 $tried_file_homedir = 1;
90 if (defined $user) {
91 File::HomeDir->users_home($user);
92 } else {
93 File::HomeDir->my_homedir;
94 }
95 } else {
96 if (defined $user) {
97 (getpwnam $user)[7];
98 } else {
99 if (defined $ENV{HOME}) {
100 $ENV{HOME};
101 } else {
102 (getpwuid $<)[7];
103 }
104 }
105 }
106 };
107 unless (defined $homedir) {
108 Carp::croak(
109 "Couldn't resolve homedir for "
110 .(defined $user ? $user : 'current user')
111 .($tried_file_homedir ? '' : ' - consider installing File::HomeDir')
112 );
113 }
114 $path =~ s/^~[^\/]*/$homedir/;
115 $path;
116}
117
118sub resolve_relative_path {
119 my ($class, $path) = @_;
120 File::Spec->rel2abs($path);
121}
122
123=for test classmethod
124
125local *File::Spec::rel2abs = sub { shift; 'FOO'.shift; };
126is($c->resolve_relative_path('bar'),'FOObar');
127
128=cut
129
130sub setup_local_lib_for {
131 my ($class, $path) = @_;
132 $class->ensure_dir_structure_for($path);
133 if ($0 eq '-') {
134 $class->print_environment_vars_for($path);
135 exit 0;
136 } else {
137 $class->setup_env_hash_for($path);
f9c6b7ff 138 unshift(@INC, split(':', $ENV{PERL5LIB}));
b5cc15f7 139 }
140}
141
142sub modulebuildrc_path {
143 my ($class, $path) = @_;
144 File::Spec->catfile($path, '.modulebuildrc');
145}
146
147sub install_base_bin_path {
148 my ($class, $path) = @_;
149 File::Spec->catdir($path, 'bin');
150}
151
152sub install_base_perl_path {
153 my ($class, $path) = @_;
154 File::Spec->catdir($path, 'lib', 'perl5');
155}
156
157sub install_base_arch_path {
158 my ($class, $path) = @_;
159 File::Spec->catdir($class->install_base_perl_path($path), $Config{archname});
160}
161
162sub ensure_dir_structure_for {
163 my ($class, $path) = @_;
164 unless (-d $path) {
165 warn "Attempting to create directory ${path}\n";
166 }
167 File::Path::mkpath($path);
168 my $modulebuildrc_path = $class->modulebuildrc_path($path);
169 if (-e $modulebuildrc_path) {
170 unless (-f _) {
171 Carp::croak("${modulebuildrc_path} exists but is not a plain file");
172 }
173 } else {
174 warn "Attempting to create file ${modulebuildrc_path}\n";
175 open MODULEBUILDRC, '>', $modulebuildrc_path
176 || Carp::croak("Couldn't open ${modulebuildrc_path} for writing: $!");
18bb63e0 177 print MODULEBUILDRC qq{install --install_base ${path}\n}
b5cc15f7 178 || Carp::croak("Couldn't write line to ${modulebuildrc_path}: $!");
179 close MODULEBUILDRC
180 || Carp::croak("Couldn't close file ${modulebuildrc_path}: $@");
181 }
182}
183
184sub INTERPOLATE_PATH () { 1 }
185sub LITERAL_PATH () { 0 }
186
187sub print_environment_vars_for {
188 my ($class, $path) = @_;
189 my @envs = $class->build_environment_vars_for($path, LITERAL_PATH);
190 my $out = '';
191 while (@envs) {
192 my ($name, $value) = (shift(@envs), shift(@envs));
193 $value =~ s/(\\")/\\$1/g;
194 $out .= qq{export ${name}="${value}"\n};
195 }
196 print $out;
197}
198
199sub setup_env_hash_for {
200 my ($class, $path) = @_;
201 my %envs = $class->build_environment_vars_for($path, INTERPOLATE_PATH);
202 @ENV{keys %envs} = values %envs;
203}
204
205sub build_environment_vars_for {
206 my ($class, $path, $interpolate) = @_;
207 return (
208 MODULEBUILDRC => $class->modulebuildrc_path($path),
209 PERL_MM_OPT => "INSTALL_BASE=${path}",
210 PERL5LIB => join(':',
211 $class->install_base_perl_path($path),
212 $class->install_base_arch_path($path),
213 ),
214 PATH => join(':',
215 $class->install_base_bin_path($path),
216 ($interpolate == INTERPOLATE_PATH
217 ? $ENV{PATH}
218 : '$PATH')
219 ),
220 )
221}
222
223=for test classmethod
224
225File::Path::rmtree('t/var/splat');
226
4c375968 227$c->ensure_dir_structure_for('t/var/splat');
b5cc15f7 228
229ok(-d 't/var/splat');
230
231ok(-f 't/var/splat/.modulebuildrc');
232
233=head1 NAME
234
235local::lib - create and use a local lib/ for perl modules with PERL5LIB
236
237=head1 SYNOPSIS
238
239In code -
240
241 use local::lib; # sets up a local lib at ~/perl5
242
243 use local::lib '~/foo'; # same, but ~/foo
244
245From the shell -
246
247 $ perl -Mlocal::lib
248 export MODULEBUILDRC=/home/username/perl/.modulebuildrc
249 export PERL_MM_OPT='INSTALL_BASE=/home/username/perl'
250 export PERL5LIB='/home/username/perl/lib/perl5:/home/username/perl/lib/perl5/i386-linux'
251 export PATH="/home/username/perl/bin:$PATH"
252
253=head1 AUTHOR
254
255Matt S Trout <mst@shadowcat.co.uk> http://www.shadowcat.co.uk/
256
257=head1 LICENSE
258
259This library is free software under the same license as perl itself
260
261=cut
262
2631;