File Coverage

blib/lib/Portable/Dist.pm
Criterion Covered Total %
statement 127 134 94.7
branch 20 36 55.5
condition 7 21 33.3
subroutine 21 21 100.0
pod 0 8 0.0
total 175 220 79.5


line stmt bran cond sub pod time code
1             package Portable::Dist;
2              
3             =pod
4              
5             =head1 NAME
6              
7             Portable::Dist - Modify a Perl distribution to make it portable
8              
9             =head1 DESCRIPTION
10              
11             The L family of modules provides functionality that allows
12             Perl to operate from arbitrary and variable paths.
13              
14             B is used to apply the necesary modifications to an
15             existing Perl distribution to convert it to a Portable Perl.
16              
17             =head2 Portability Warning
18              
19             This module is designed for use only on a distribution that is not
20             currently in use. Thus, you should not execute the modification
21             process using the distribution you wish to modify.
22              
23             This module is also currently only designed to run on Windows (to
24             support the production of Strawberry Perl Portable and other
25             Perl::Dist-related distributions).
26              
27             If you wish to use this module for other operating systems, please
28             contact the author.
29              
30             =head1 METHODS
31              
32             =cut
33              
34 2     2   119199 use 5.008;
  2         10  
  2         98  
35 2     2   13 use strict;
  2         4  
  2         149  
36 2     2   15 use Carp ();
  2         18  
  2         33  
37 2     2   11 use File::Spec ();
  2         4  
  2         51  
38 2     2   12 use File::Path ();
  2         5  
  2         45  
39 2     2   58841 use File::Slurp qw(read_file write_file);
  2         50603  
  2         156  
40 2     2   2285 use File::Find::Rule ();
  2         19261  
  2         47  
41 2     2   1780 use File::IgnoreReadonly ();
  2         108006  
  2         48  
42 2     2   2131 use Params::Util '_STRING';
  2         15175  
  2         190  
43              
44             our $VERSION = '1.06';
45              
46 2     2   25 use constant MSWin32 => ( $^O eq 'MSWin32' );
  2         4  
  2         143  
47              
48 2         16 use Object::Tiny qw{
49             perl_root
50             perl_bin
51             perl_lib
52             perl_sitelib
53             perl_vendorlib
54             perl_sitebin
55             perl_vendorbin
56             pl2bat
57             config_pm
58             cpan_config
59             file_homedir
60             file_homedir_v
61             minicpan_dir
62             minicpan_conf
63 2     2   2848 };
  2         752  
64              
65              
66              
67              
68              
69             #####################################################################
70             # Constructor and Accessors
71              
72             sub new {
73 1     1 0 53270 my $self = shift->SUPER::new(@_);
74              
75             # Check params
76 1 50       65 unless ( _DIRECTORY($self->perl_root) ) {
77 0         0 Carp::croak("Missing or invalid perl_root directory");
78             }
79              
80 1   33     51 $self->{perl_bin} ||= File::Spec->catdir( $self->perl_root, 'bin' );
81 1 50       103 unless ( _DIRECTORY($self->perl_bin) ) {
82 0         0 Carp::croak("Missing or invalid perl_bin directory");
83             }
84              
85 1   33     24 $self->{perl_lib} ||= File::Spec->catdir( $self->perl_root, 'lib' );
86 1 50       27 unless ( _DIRECTORY($self->perl_lib) ) {
87 0         0 Carp::croak("Missing or invalid perl_lib directory");
88             }
89              
90 1   33     26 $self->{perl_sitelib} ||= File::Spec->catdir( $self->perl_root, 'site', 'lib' );
91 1 50       25 unless ( _DIRECTORY($self->perl_sitelib) ) {
92 0         0 Carp::croak("Missing or invalid perl_sitelib directory");
93             }
94              
95 1   33     22 $self->{perl_vendorlib} ||= File::Spec->catdir( $self->perl_root, 'vendor', 'lib' );
96 1 50       24 unless ( _DIRECTORY($self->perl_sitelib) ) {
97 0         0 Carp::croak("Missing or invalid perl_vendorlib directory");
98             }
99              
100 1   33     23 $self->{perl_sitebin} ||= File::Spec->catdir( $self->perl_root, 'site', 'bin' );
101             #unless ( _DIRECTORY($self->perl_sitebin) ) {
102             # Carp::croak("Missing or invalid perl_sitebin directory");
103             #}
104              
105 1   33     32 $self->{perl_vendorbin} ||= File::Spec->catdir( $self->perl_root, 'vendor', 'bin' );
106             #unless ( _DIRECTORY($self->perl_sitebin) ) {
107             # Carp::croak("Missing or invalid perl_vendorbin directory");
108             #}
109              
110             # Find some particular files
111 1         27 $self->{pl2bat} = File::Spec->catfile( $self->perl_bin, 'pl2bat.bat' );
112 1         31 $self->{config_pm} = File::Spec->catfile( $self->perl_lib, 'Config.pm' );
113 1         25 $self->{cpan_config} = File::Spec->catfile( $self->perl_lib, 'CPAN', 'Config.pm' );
114 1         27 $self->{file_homedir} = File::Spec->catfile( $self->perl_sitelib, 'File', 'HomeDir.pm' );
115 1         27 $self->{file_homedir_v} = File::Spec->catfile( $self->perl_vendorlib, 'File', 'HomeDir.pm' );
116 1         76 $self->{minicpan_dir} = File::Spec->catfile( $self->perl_vendorlib, 'CPAN' );
117 1         32 $self->{minicpan_conf} = File::Spec->catfile( $self->minicpan_dir, 'minicpan.conf' );
118              
119 1         12 return $self;
120             }
121              
122             sub run {
123 1     1 0 3703 my $self = shift;
124              
125             # Modify the files we need to hack
126 1         5 $self->modify_config;
127 1         17 $self->modify_cpan_config;
128 1         8 $self->modify_file_homedir;
129              
130             # Create the minicpan configuration file
131 1         6 $self->create_minicpan_conf;
132              
133             # Convert all existing batch files to portable
134 1         5 $self->modify_batch_files;
135              
136             # Modify pl2bat so new batch files get created properly
137 1         8 $self->modify_pl2bat;
138              
139 1         16 return 1;
140             }
141              
142              
143              
144              
145              
146             #####################################################################
147             # Modification Functions
148              
149             # Apply modifications to Config.pm
150             sub modify_config {
151 1     1 0 3 my $self = shift;
152 1         48 my $file = $self->config_pm;
153 1         6 my $append = <<'END_PERL';
154             eval {
155             require Portable;
156             Portable->import('Config');
157             };
158              
159             1;
160             END_PERL
161              
162             # Apply the change to the file
163 1         11 my $guard = File::IgnoreReadonly->new( $file );
164 1 50       48 my $content = read_file($file, binmode=>':utf8') or die "Couldn't read $file";
165 1         164 $content .= $append;
166 1         8 write_file($file, {binmode=>':utf8'}, $content);
167              
168 1         179 return 1;
169             }
170              
171             # Apply modifications to CPAN::Config
172             sub modify_cpan_config {
173 1     1 0 3 my $self = shift;
174 1         30 my $file = $self->cpan_config;
175 1         6 my $append = <<'END_PERL';
176             eval {
177             require Portable;
178             Portable->import('CPAN');
179             };
180             END_PERL
181              
182             # Apply the change to the file
183 1         9 my $guard = File::IgnoreReadonly->new( $file );
184 1 50       50 my $content = read_file($file, binmode=>':utf8') or die "Couldn't read $file";
185 1         144 $content =~ s/\n1;/$append\n\n1;/;
186 1         5 write_file($file, {binmode=>':utf8'}, $content);
187              
188 1         112 return 1;
189             }
190              
191             # Apply modifications to File::HomeDir
192             sub modify_file_homedir {
193 1     1 0 3 my $self = shift;
194 1         1 my $file;
195 1         3 my $append = <<'END_PERL';
196             eval {
197             require Portable;
198             Portable->import('HomeDir');
199             };
200             END_PERL
201              
202 1 50       28 if (-f $self->file_homedir_v) {
203 0         0 $file = $self->file_homedir_v;
204             } else {
205 1         39 $file = $self->file_homedir;
206             }
207              
208             # Apply the change to the file
209 1         10 my $guard = File::IgnoreReadonly->new( $file );
210 1 50       44 my $content = read_file($file, binmode=>':utf8') or die "Couldn't read $file";
211 1         114 $content =~ s/\n1;/$append\n\n1;/;
212 1         4 write_file($file, {binmode=>':utf8'}, $content);
213              
214 1         108 return 1;
215             }
216              
217             # Create the minicpan configuration file
218             sub create_minicpan_conf {
219 1     1 0 2 my $self = shift;
220 1         49 my $dir = $self->minicpan_dir;
221 1         22 my $file = $self->minicpan_conf;
222              
223             # Create the directory
224 1         338 File::Path::mkpath( $dir, { verbose => 0 } );
225              
226             # Write the file
227 1 50       19 my $guard = -f $file ? File::IgnoreReadonly->new( $file ) : 0;
228 1         5 write_file(
229             $file,
230             "class: CPAN::Mini::Portable\n",
231             "skip_perl: 1\n",
232             "no_conn_cache: 1\n",
233             );
234              
235             # Make the file readonly
236 1         165 if ( MSWin32 ) {
237             require Win32::File::Object;
238             Win32::File::Object->new( $file, 1 )->readonly(1);
239             } else {
240 1         8 require File::chmod;
241 1         6 File::chmod::chmod( 'a-w', $file );
242             }
243              
244 1         135 return 1;
245             }
246              
247             # Modify existing batch files
248             sub modify_batch_files {
249 1     1 0 3 my $self = shift;
250 1         1 my @files;
251 1         14 push @files, File::Find::Rule->name('*.bat')->file->in($self->perl_bin);
252 1 50       2298 push @files, File::Find::Rule->name('*.bat')->file->in($self->perl_sitebin) if -d $self->perl_sitebin;
253 1 50       132 push @files, File::Find::Rule->name('*.bat')->file->in($self->perl_vendorbin) if -d $self->perl_vendorbin;
254 1 50       51 unless ( @files ) {
255 0         0 Carp::croak("Failed to find any batch files");
256             }
257              
258             # Process the files
259 1         6 foreach my $file ( @files ) {
260             # Apply the change to the file
261 4         1994 my $guard = File::IgnoreReadonly->new( $file );
262 4 50       364 my $content = read_file($file, binmode=>':utf8') or die "Couldn't read $file";
263 4         1245 $content =~ s/([\r\n])(perl )(-x[^\r\n]*)/$1 . _perl_cmd($3)/sge;
  4         19  
264 4         143 $content =~ s/(#line )(\d+)/$1 . ($2+14)/e; # we have added extra 14 lines
  2         47  
265 4         30 write_file($file, {binmode=>':utf8'}, $content);
266             }
267              
268 1         208 return 1;
269             }
270              
271             sub modify_pl2bat {
272 1     1 0 3 my $self = shift;
273 1         50 my $file = $self->pl2bat;
274              
275             # Apply the change to the file
276 1         14 my $guard = File::IgnoreReadonly->new( $file );
277 1 50       55 my $content = read_file($file, binmode=>':utf8') or die "Couldn't read $file";
278 1         362 $content =~ s/\bperl \$OPT\{'(a|o|n)'\}/_perl_cmd('$OPT{\'' . $1 .'\'}', 1, 1)/esg;
  3         60  
279 1         10 write_file($file, {binmode=>':utf8'}, $content);
280              
281 1         400 return 1;
282             }
283              
284             #####################################################################
285             # Support Functions
286              
287             sub _DIRECTORY {
288 5 50 33 5   134 (defined _STRING($_[0]) and -d $_[0]) ? $_[0] : undef;
289             }
290              
291             sub _perl_cmd {
292 7     7   21 my ($arg, $tab, $quote) = @_;
293 7         69 my $rv = <<'MARKER';
294             IF EXIST "%~dp0perl.exe" (
295             "%~dp0perl.exe" XXX_XXX
296             ) ELSE IF EXIST "%~dp0..\..\bin\perl.exe" (
297             "%~dp0..\..\bin\perl.exe" XXX_XXX
298             ) ELSE (
299             perl XXX_XXX
300             )
301             MARKER
302 7         49 $rv =~ s/XXX_XXX/$arg/sg;
303 7 100       95 $rv =~ s/([\%\\])/\\$1/sg if $quote;
304 7 100       89 $rv =~ s/([\r\n]+)/$1\t/sg if $tab;
305 7         105 return $rv;
306             }
307              
308             1;
309              
310             =pod
311              
312             =head1 SUPPORT
313              
314             Bugs should be reported via the CPAN bug tracker.
315              
316             L
317              
318             For other issues, or commercial support, contact the author.
319              
320             =head1 AUTHOR
321              
322             Adam Kennedy Eadamk@cpan.orgE
323              
324             =head1 SEE ALSO
325              
326             L, L, L
327              
328             =head1 COPYRIGHT
329              
330             Copyright 2008 - 2011 Adam Kennedy.
331              
332             This program is free software; you can redistribute
333             it and/or modify it under the same terms as Perl itself.
334              
335             The full text of the license can be found in the
336             LICENSE file included with this module.
337              
338             =cut