File Coverage

blib/lib/App/Virtualenv.pm
Criterion Covered Total %
statement 33 35 94.2
branch n/a
condition n/a
subroutine 12 12 100.0
pod n/a
total 45 47 95.7


line stmt bran cond sub pod time code
1             package App::Virtualenv;
2             =head1 NAME
3              
4             App::Virtualenv - Perl virtual environment
5              
6             =head1 VERSION
7              
8             version 2.07
9              
10             =head1 SYNOPSIS
11              
12             #!/bin/sh
13             perl -MApp::Virtualenv -erun -- environment_path
14              
15             =head1 DESCRIPTION
16              
17             App::Virtualenv is a Perl package to create isolated Perl virtual environments, like Python virtual environment.
18              
19             See also: L
20              
21             =cut
22 1     1   14282 use strict;
  1         2  
  1         27  
23 1     1   4 use warnings;
  1         2  
  1         21  
24 1     1   12 use v5.10.1;
  1         6  
25 1     1   5 use feature qw(switch);
  1         2  
  1         101  
26 1     1   525 no if ($] >= 5.018), 'warnings' => 'experimental';
  1         11  
  1         5  
27 1     1   63 use Config;
  1         2  
  1         28  
28 1     1   490 use FindBin;
  1         848  
  1         38  
29 1     1   6 use File::Basename;
  1         2  
  1         38  
30 1     1   447 use File::Copy;
  1         3532  
  1         61  
31 1     1   7 use Cwd;
  1         2  
  1         47  
32 1     1   528 use ExtUtils::Installed;
  1         88798  
  1         35  
33 1     1   437 use Lazy::Utils;
  0            
  0            
34              
35              
36             BEGIN
37             {
38             require Exporter;
39             our $VERSION = '2.07';
40             our @ISA = qw(Exporter);
41             our @EXPORT = qw(main run);
42             our @EXPORT_OK = qw();
43             }
44              
45              
46             =head1 Functions
47              
48             =head2 sh(@args)
49              
50             runs shell program defined in SHELL environment variable, otherwise /bin/sh
51              
52             @args: I
53              
54             return value: I
55              
56             =cut
57             sub sh
58             {
59             my (@args) = @_;
60             return system2((defined $ENV{SHELL})? $ENV{SHELL}: "/bin/sh", @args);
61             }
62              
63             =head2 perl(@args)
64              
65             runs Perl interpreter
66              
67             @args: I
68              
69             return value: I
70              
71             =cut
72             sub perl
73             {
74             my (@args) = @_;
75             return system2($Config{perlpath}, @args);
76             }
77              
78             =head2 activate($virtualenv_path)
79              
80             activates Perl virtual environment
81              
82             $virtualenv_path: I
83              
84             return value: I
85              
86             =cut
87             sub activate
88             {
89             my ($virtualenv_path) = @_;
90             return unless defined($virtualenv_path) and length($virtualenv_path) > 0 and -d "$virtualenv_path/lib/perl5";
91             $virtualenv_path = Cwd::realpath($virtualenv_path);
92              
93             deactivate(1);
94              
95             $ENV{_OLD_PERL_VIRTUAL_ENV} = $ENV{PERL_VIRTUAL_ENV};
96             $ENV{PERL_VIRTUAL_ENV} = $virtualenv_path;
97              
98             $ENV{_OLD_PERL_VIRTUAL_PATH} = $ENV{PATH};
99             $ENV{PATH} = "$virtualenv_path/bin".((defined $ENV{PATH})? ":${ENV{PATH}}": "");
100              
101             $ENV{_OLD_PERL_VIRTUAL_PERL5LIB} = $ENV{PERL5LIB};
102             $ENV{PERL5LIB} = "$virtualenv_path/lib/perl5".((defined $ENV{PERL5LIB})? ":${ENV{PERL5LIB}}": "");
103              
104             $ENV{_OLD_PERL_VIRTUAL_PERL_LOCAL_LIB_ROOT} = $ENV{PERL_LOCAL_LIB_ROOT};
105             $ENV{PERL_LOCAL_LIB_ROOT} = "$virtualenv_path";
106              
107             $ENV{_OLD_PERL_VIRTUAL_PERL_MB_OPT} = $ENV{PERL_MB_OPT};
108             $ENV{PERL_MB_OPT} = "--install_base \"$virtualenv_path\"";
109              
110             $ENV{_OLD_PERL_VIRTUAL_PERL_MM_OPT} = $ENV{PERL_MM_OPT};
111             $ENV{PERL_MM_OPT} = "INSTALL_BASE=$virtualenv_path";
112              
113             $ENV{_OLD_PERL_VIRTUAL_PS1} = $ENV{PS1};
114             $ENV{PS1} = "(".basename($virtualenv_path).") ".((defined $ENV{PS1})? $ENV{PS1}: "");
115              
116             return $virtualenv_path;
117             }
118              
119             =head2 deactivate($nondestructive)
120              
121             deactivates Perl virtual environment
122              
123             $nondestructive: I
124              
125             return value: I
126              
127             =cut
128             sub deactivate
129             {
130             my ($nondestructive) = @_;
131              
132             $nondestructive = not defined($ENV{PERL_VIRTUAL_ENV}) if not defined($nondestructive);
133              
134             $ENV{PERL_VIRTUAL_ENV} = $ENV{_OLD_PERL_VIRTUAL_ENV} if defined($ENV{_OLD_PERL_VIRTUAL_ENV}) or not $nondestructive;
135             undef $ENV{_OLD_PERL_VIRTUAL_ENV};
136              
137             $ENV{PATH} = $ENV{_OLD_PERL_VIRTUAL_PATH} if defined($ENV{_OLD_PERL_VIRTUAL_PATH}) or not $nondestructive;
138             undef $ENV{_OLD_PERL_VIRTUAL_PATH};
139              
140             $ENV{PERL5LIB} = $ENV{_OLD_PERL_VIRTUAL_PERL5LIB} if defined($ENV{_OLD_PERL_VIRTUAL_PERL5LIB}) or not $nondestructive;
141             undef $ENV{_OLD_PERL_VIRTUAL_PERL5LIB};
142              
143             $ENV{PERL_LOCAL_LIB_ROOT} = $ENV{_OLD_PERL_VIRTUAL_PERL_LOCAL_LIB_ROOT} if defined($ENV{_OLD_PERL_VIRTUAL_PERL_LOCAL_LIB_ROOT}) or not $nondestructive;
144             undef $ENV{_OLD_PERL_VIRTUAL_PERL_LOCAL_LIB_ROOT};
145              
146             $ENV{PERL_MB_OPT} = $ENV{_OLD_PERL_VIRTUAL_PERL_MB_OPT} if defined($ENV{_OLD_PERL_VIRTUAL_PERL_MB_OPT}) or not $nondestructive;
147             undef $ENV{_OLD_PERL_VIRTUAL_PERL_MB_OPT};
148              
149             $ENV{PERL_MM_OPT} = $ENV{_OLD_PERL_VIRTUAL_PERL_MM_OPT} if defined($ENV{_OLD_PERL_VIRTUAL_PERL_MM_OPT}) or not $nondestructive;
150             undef $ENV{_OLD_PERL_VIRTUAL_PERL_MM_OPT};
151              
152             $ENV{PS1} = $ENV{_OLD_PERL_VIRTUAL_PS1} if defined($ENV{_OLD_PERL_VIRTUAL_PS1}) or not $nondestructive;
153             undef $ENV{_OLD_PERL_VIRTUAL_PS1};
154              
155             return 1;
156             }
157              
158             =head2 create($virtualenv_path, $empty)
159              
160             creates Perl virtual environment
161              
162             $virtualenv_path: I
163              
164             $empty: I
165              
166             return value: I
167              
168             =cut
169             sub create
170             {
171             my ($virtualenv_path, $empty) = @_;
172             return unless defined($virtualenv_path) and length($virtualenv_path) > 0;
173             $virtualenv_path = Cwd::realpath($virtualenv_path);
174             say "Creating Perl virtual environment: $virtualenv_path";
175              
176             deactivate();
177             $ENV{PERL_MM_USE_DEFAULT} = 1;
178             $ENV{NONINTERACTIVE_TESTING} = 1;
179             $ENV{AUTOMATED_TESTING} = 1;
180              
181             require local::lib;
182             local::lib->import($virtualenv_path);
183              
184             activate($virtualenv_path);
185              
186             perl("-MCPAN", "-e exit(defined(CPAN::Shell->force('install', 'CPAN'))? 0: 1);") unless $empty;
187              
188             my $pkg_path = dirname(__FILE__);
189              
190             say "Copying... bin/activate";
191             copy("$pkg_path/Virtualenv/activate", "$virtualenv_path/bin/activate");
192             chmod(0644, "$virtualenv_path/bin/activate");
193              
194             say "Copying... bin/sh.pl";
195             copy("$pkg_path/Virtualenv/sh.pl", "$virtualenv_path/bin/sh.pl");
196             chmod(0755, "$virtualenv_path/bin/sh.pl");
197              
198             say "Copying... bin/perl.pl";
199             file_put_contents("$virtualenv_path/bin/perl.pl", "#!".shellmeta($Config{perlpath})."\n".file_get_contents("$pkg_path/Virtualenv/perl.pl"));
200             chmod(0755, "$virtualenv_path/bin/perl.pl");
201             symlink("perl.pl", "$virtualenv_path/bin/perl");
202              
203             say "Copying... bin/virtualenv.pl";
204             copy("$pkg_path/Virtualenv/virtualenv.pl", "$virtualenv_path/bin/virtualenv.pl");
205             chmod(0755, "$virtualenv_path/bin/virtualenv.pl");
206             symlink("virtualenv.pl", "$virtualenv_path/bin/virtualenv");
207              
208             return $virtualenv_path;
209             }
210              
211             =head2 find_virtualenv_path($virtualenv_path)
212              
213             finds Perl virtual environment path by $virtualenv_path argument or activated virtual environment or running script or PERL5LIB environment variable
214              
215             $virtualenv_path: I
216              
217             return value: I
218              
219             =cut
220             sub find_virtualenv_path
221             {
222             my ($virtualenv_path) = @_;
223             $virtualenv_path = $ENV{PERL_VIRTUAL_ENV} if not (defined($virtualenv_path) and length($virtualenv_path) > 0 and -d "$virtualenv_path/lib/perl5");
224             $virtualenv_path = "${FindBin::Bin}/.." if not (defined($virtualenv_path) and length($virtualenv_path) > 0 and -d "$virtualenv_path/lib/perl5") and ${FindBin::Bin} !~ qr'^(/usr/|/bin/)' and -d "${FindBin::Bin}/../lib/perl5";
225             for (split(":", defined($ENV{PERL5LIB})? $ENV{PERL5LIB}: ""))
226             {
227             last if defined($virtualenv_path) and length($virtualenv_path) > 0 and -d "$virtualenv_path/lib/perl5";
228             $virtualenv_path = "$_/../..";
229             }
230             return if not (defined($virtualenv_path) and length($virtualenv_path) > 0 and -d "$virtualenv_path/lib/perl5");
231             return $virtualenv_path;
232             }
233              
234             =head2 activate2($virtualenv_path, $inform)
235              
236             activates Perl virtual environment by find_virtualenv_path function
237              
238             $virtualenv_path: I
239              
240             $inform: I
241              
242             return value: I
243              
244             =cut
245             sub activate2
246             {
247             my ($virtualenv_path, $inform) = @_;
248             my $old_virtualenv_path = $ENV{PERL_VIRTUAL_ENV};
249             $virtualenv_path = activate(find_virtualenv_path($virtualenv_path));
250             if ($inform)
251             {
252             if (defined($virtualenv_path))
253             {
254             say STDERR "Perl virtual environment path: $virtualenv_path" if not defined $old_virtualenv_path or $old_virtualenv_path ne $virtualenv_path;
255             } else
256             {
257             say STDERR "Perl virtual environment is not activated";
258             }
259             }
260             return $virtualenv_path;
261             }
262              
263             =head2 getinc($virtualenv_path)
264              
265             gets array ref of include paths given virtual environment path or sitelib paths
266              
267             $virtualenv_path: I
268              
269             return value: I
270              
271             =cut
272             sub getinc
273             {
274             my ($virtualenv_path) = @_;
275             my $perl5lib;
276             $perl5lib = "$virtualenv_path/lib/perl5" if defined($virtualenv_path) and length($virtualenv_path) > 0 and -d "$virtualenv_path/lib/perl5";
277             my $inc = [(defined($perl5lib)? ("$perl5lib/$Config{version}/$Config{archname}", "$perl5lib/$Config{version}", "$perl5lib/$Config{archname}", "$perl5lib"): ($Config{sitearch}, $Config{sitelib}))];
278             @$inc = map(((length($_) < 1 or substr($_, -1, 1) ne "/")? "$_/": $_), @$inc);
279             return $inc;
280             }
281              
282             =head2 list(%params)
283              
284             lists packages or modules or files by given %params
285              
286             %params: I
287              
288             =over
289              
290             one: I
291              
292             detail: I
293              
294             =back
295              
296             return value: I
297              
298             =cut
299             sub list
300             {
301             my %params = @_;
302             my $inc = getinc(activate2(undef, 1));
303             my $inst = ExtUtils::Installed->new(inc_override => $inc, extra_libs =>[]);
304             my @packages = sort({lc($a) cmp lc($b)} $inst->modules());
305             for my $package_name (grep({ my $package = $_; not defined($params{packages}) or not @{$params{packages}} or grep($_ eq $package, @{$params{packages}}) } @packages))
306             {
307             next if $package_name eq 'Perl';
308             my $version = $inst->version($package_name);
309             $version = "0" if not $version;
310             if ($params{detail})
311             {
312             say sprintf("%-40s %10s", $package_name, $version) unless $params{one};
313             my @files = sort({lc($a) cmp lc($b)} $inst->files($package_name, "all"));
314             my $packlist_file = $inst->packlist($package_name)->packlist_file();
315             unshift @files, $packlist_file if defined($packlist_file);
316             for my $file (@files)
317             {
318             my $inc_path = (grep($file =~ /^\Q$_\E/, @$inc))[0];
319             my $rel_path = ($file =~ /^\Q$inc_path\E(.*)\.pm$/)[0] if defined($inc_path);
320             given ($params{detail})
321             {
322             when ("module")
323             {
324             if (defined($rel_path))
325             {
326             my $module = $rel_path;
327             $module =~ s/\//::/g;
328             print " " unless $params{one};
329             say $module;
330             }
331             }
332             when ("file")
333             {
334             print " " unless $params{one};
335             say $file;
336             }
337             }
338             }
339             next;
340             }
341             if ($params{one})
342             {
343             say $package_name;
344             next;
345             }
346             say sprintf("%-40s %10s", $package_name, $version);
347             }
348             return 1;
349             }
350              
351             =head2 main(@argv)
352              
353             App::Virtualenv main function to run on command-line
354              
355             See also: L
356              
357             @argv: I
358              
359             return value: I
360              
361             =cut
362             sub main
363             {
364             my (@argv) = @_;
365             my $args = cmdargs({ valuableArgs => 0, noCommand => 1 }, @argv);
366             my $cmd;
367             for my $arg (grep(/^\-/, keys %$args))
368             {
369             my $newcmd;
370             $newcmd = $arg if
371             $arg =~ /^\-(h|\-help)$/ or
372             $arg =~ /^\-(c|\-create)$/ or
373             $arg =~ /^\-(l|\-list)$/ or
374             $arg =~ /^\-(m|\-list-modules)$/ or
375             $arg =~ /^\-(f|\-list-files)$/;
376             if (defined($newcmd))
377             {
378             die "Argument $newcmd doesn't use with $cmd.\n" if defined($cmd);
379             $cmd = $newcmd;
380             }
381             }
382             $cmd = "-c" unless defined($cmd);
383             given ($cmd)
384             {
385             when (/^\-(h|\-help)$/)
386             {
387             my @lines;
388             @lines = get_pod_text(dirname(__FILE__)."/Virtualenv/virtualenv.pl", "SYNOPSIS");
389             @lines = get_pod_text(dirname(__FILE__)."/Virtualenv/virtualenv.pl", "ABSTRACT") unless defined($lines[0]);
390             $lines[0] = "virtualenv.pl";
391             say join("\n", @lines);
392             }
393             when (/^\-(c|\-create)$/)
394             {
395             die "Perl virtual environment path must be specified.\n" unless defined($args->{parameters}->[0]) and length($args->{parameters}->[0]) > 0;
396             create($args->{parameters}->[0], (exists($args->{'-e'}) or exists($args->{'--empty'})));
397             }
398             when (/^\-(l|\-list)$/)
399             {
400             list(one => (exists($args->{'-1'}) or exists($args->{'--one'})), packages => $args->{parameters});
401             }
402             when (/^\-(m|\-list-modules)$/)
403             {
404             list(one => (exists($args->{'-1'}) or exists($args->{'--one'})), packages => $args->{parameters}, detail => 'module');
405             }
406             when (/^\-(f|\-list-files)$/)
407             {
408             list(one => (exists($args->{'-1'}) or exists($args->{'--one'})), packages => $args->{parameters}, detail => 'file');
409             }
410             }
411             return 0;
412             }
413              
414             =head2 run
415              
416             runs App::Virtualenv by main function with command-line arguments by @ARGV
417              
418             return value: I
419              
420             =cut
421             sub run
422             {
423             exit main(@ARGV);
424             }
425              
426              
427             1;
428             __END__