File Coverage

blib/lib/CPAN/Static/Install.pm
Criterion Covered Total %
statement 120 149 80.5
branch 14 44 31.8
condition 4 14 28.5
subroutine 27 30 90.0
pod 7 14 50.0
total 172 251 68.5


line stmt bran cond sub pod time code
1             package CPAN::Static::Install;
2             $CPAN::Static::Install::VERSION = '0.006';
3 3     3   311431 use strict;
  3         10  
  3         101  
4 3     3   13 use warnings;
  3         6  
  3         217  
5              
6 3     3   18 use Exporter 5.57 'import';
  3         48  
  3         195  
7             our @EXPORT_OK = qw/configure build test install supports_static_install opts_from_args_list opts_from_args_string/;
8             our %EXPORT_TAGS = (
9             'all' => \@EXPORT_OK,
10             );
11              
12 3     3   1016 use CPAN::Meta;
  3         62714  
  3         109  
13 3     3   1462 use ExtUtils::Config 0.003;
  3         1673  
  3         121  
14 3     3   839 use ExtUtils::Helpers 0.020 qw/make_executable split_like_shell man1_pagename man3_pagename detildefy/;
  3         9049  
  3         259  
15 3     3   1873 use ExtUtils::Install qw/pm_to_blib/;
  3         29084  
  3         243  
16 3     3   1519 use ExtUtils::InstallPaths 0.002;
  3         11378  
  3         138  
17 3     3   21 use File::Basename qw/dirname/;
  3         9  
  3         179  
18 3     3   18 use File::Find ();
  3         6  
  3         61  
19 3     3   13 use File::Path qw/mkpath/;
  3         5  
  3         192  
20 3     3   16 use File::Spec::Functions qw/catfile catdir rel2abs abs2rel splitdir curdir/;
  3         5  
  3         328  
21 3     3   2453 use Getopt::Long 2.36 qw/GetOptionsFromArray/;
  3         37568  
  3         90  
22 3     3   2268 use JSON::PP 2 qw/encode_json decode_json/;
  3         35847  
  3         262  
23 3     3   28 use Scalar::Util 'blessed';
  3         5  
  3         6865  
24              
25             sub write_file {
26 1     1 0 263 my ($filename, $content) = @_;
27 1 50       207 open my $fh, '>', $filename or die "Could not open $filename: $!\n";
28 1         59 print $fh $content;
29             }
30             sub read_file {
31 3     3 0 12 my ($filename) = @_;
32 3 50       154 open my $fh, '<', $filename or die "Could not open $filename: $!\n";
33 3         8 return do { local $/; <$fh> };
  3         16  
  3         136  
34             }
35              
36             my @getopt_flags = qw/install_base=s install_path=s% installdirs=s destdir=s prefix=s config=s%
37             uninst:1 verbose:1 dry_run:1 pureperl-only:1 create_packlist=i jobs=i/;
38              
39             sub opts_from_args_list {
40 2     2 1 7 my (@args) = @_;
41 2         44 GetOptionsFromArray(\@args, \my %result, @getopt_flags);
42 2         4049 return %result;
43             }
44              
45             sub opts_from_args_string {
46 1     1 1 6 my $arg = shift;
47 1 50       12 my @args = defined $arg ? split_like_shell($arg) : ();
48 1         13 return opts_from_args_list(@args);
49             }
50              
51             sub supports_static_install {
52 0     0 1 0 my $meta = shift;
53 0 0       0 if (!$meta) {
54 0 0       0 return undef unless -f 'META.json';
55 0         0 $meta = CPAN::Meta->load_file('META.json');
56             }
57 0   0     0 my $static_version = $meta->custom('x_static_install') || 0;
58 0 0       0 return $static_version == 1 ? $static_version : undef;
59             }
60              
61             sub configure {
62 1     1 1 149065 my %args = @_;
63 1 50 33     13 die "Unsupported static install version" if defined $args{static_version} and int $args{static_version} != 1;
64 1 50       12 $args{config} = $args{config}->values_set if blessed($args{config});
65 1         45 my $meta = CPAN::Meta->load_file('META.json');
66 1         9016 my %env = opts_from_args_string($ENV{PERL_MB_OPT});
67 1         15 printf "Saving configuration for '%s' version '%s'\n", $meta->name, $meta->version;
68 1         56 write_file('_static_build_params', encode_json([ \%env, \%args ]));
69 1         10 $meta->save('MYMETA.json');
70             }
71              
72             sub manify {
73 0     0 0 0 my ($input_file, $output_file, $section, $opts) = @_;
74 0 0 0     0 return if -e $output_file && -M $input_file <= -M $output_file;
75 0         0 my $dirname = dirname($output_file);
76 0 0       0 mkpath($dirname, $opts->{verbose}) if not -d $dirname;
77 0         0 require Pod::Man;
78 0         0 Pod::Man->new(section => $section)->parse_from_file($input_file, $output_file);
79 0 0 0     0 print "Manifying $output_file\n" if $opts->{verbose} && $opts->{verbose} > 0;
80 0         0 return;
81             }
82              
83             sub find {
84 6     6 0 24 my ($pattern, $dir) = @_;
85 6         8 my @result;
86 6 100 100 18   430 File::Find::find(sub { push @result, $File::Find::name if /$pattern/ && -f }, $dir) if -d $dir;
  18 50       1193  
87 6         41 return @result;
88             }
89              
90             sub contains_pod {
91 0     0 0 0 my ($file) = @_;
92 0 0       0 return unless -T $file;
93 0         0 return read_file($file) =~ /^\=(?:head|pod|item)/m;
94             }
95              
96             sub hash_merge {
97 3     3 0 10 my ($left, @others) = @_;
98 3         5 my %result = %{$left};
  3         10  
99 3         17 for my $right (@others) {
100 6         23 for my $key (keys %$right) {
101 3 50       19 $result{$key} = ref($right->{$key}) eq 'HASH' ? hash_merge($result{key}, $right->{key}) : $right->{$key};
102             }
103             }
104 3         16 return %result;
105             }
106              
107             sub get_opts {
108 3     3 0 10 my %extra_opts = @_;
109 3         5 my ($env, $bargv) = @{ decode_json(read_file('_static_build_params')) };
  3         16  
110 3         1467 my %options = hash_merge($env, $bargv, \%extra_opts);
111 3         12 $_ = detildefy($_) for grep { defined } @options{qw/install_base destdir prefix/}, values %{ $options{install_path} };
  9         38  
  3         20  
112 3         51 $options{meta} = CPAN::Meta->load_file('MYMETA.json');
113 3         62634 $options{config} = ExtUtils::Config->new($options{config});
114 3         47 $options{install_paths} = ExtUtils::InstallPaths->new(%options, dist_name => $options{meta}->name);
115 3         3433 return %options;
116             }
117              
118             sub build {
119 1     1 1 3269 my %extra_opts = @_;
120 1         5 my %opt = get_opts(%extra_opts);
121 1         6 my %modules = map { $_ => catfile('blib', $_) } find(qr/\.pm$/, 'lib');
  1         8  
122 1         14 my %docs = map { $_ => catfile('blib', $_) } find(qr/\.pod$/, 'lib');
  0         0  
123 1         6 my %scripts = map { $_ => catfile('blib', $_) } find(qr/(?:)/, 'script');
  1         8  
124 1         4 my %sdocs = map { $_ => delete $scripts{$_} } grep { /.pod$/ } keys %scripts;
  0         0  
  1         4  
125 1         4 my %dist_shared = map { $_ => catfile(qw/blib lib auto share dist/, $opt{meta}->name, abs2rel($_, 'share')) } find(qr/(?:)/, 'share');
  1         10  
126 1         140 my %module_shared = map { $_ => catfile(qw/blib lib auto share module/, abs2rel($_, 'module-share')) } find(qr/(?:)/, 'module-share');
  1         6  
127 1         98 pm_to_blib({ %modules, %docs, %scripts, %dist_shared, %module_shared }, catdir(qw/blib lib auto/));
128 1         7361 make_executable($_) for values %scripts;
129 1         696 mkpath(catdir(qw/blib arch/), $opt{verbose});
130              
131 1 50       11 if ($opt{install_paths}->is_default_installable('bindoc')) {
132 0         0 my $section = $opt{config}->get('man1ext');
133 0         0 for my $input (keys %scripts, keys %sdocs) {
134 0 0       0 next unless contains_pod($input);
135 0         0 my $output = catfile('blib', 'bindoc', man1_pagename($input));
136 0         0 manify($input, $output, $section, \%opt);
137             }
138             }
139 1 50       80 if ($opt{install_paths}->is_default_installable('libdoc')) {
140 0         0 my $section = $opt{config}->get('man3ext');
141 0         0 for my $input (keys %modules, keys %docs) {
142 0 0       0 next unless contains_pod($input);
143 0         0 my $output = catfile('blib', 'libdoc', man3_pagename($input));
144 0         0 manify($input, $output, $section, \%opt);
145             }
146             }
147             }
148              
149             sub test {
150 1     1 1 4 my %extra_opts = @_;
151 1         4 my %opt = get_opts(%extra_opts);
152 1 50       43 die "Must run `./Build build` first\n" if not -d 'blib';
153 1         4745 require TAP::Harness::Env;
154             my %test_args = (
155             (verbosity => $opt{verbose}) x!! exists $opt{verbose},
156             (jobs => $opt{jobs}) x!! exists $opt{jobs},
157             (color => 1) x !!-t STDOUT,
158 1         2822 lib => [ map { rel2abs(catdir(qw/blib/, $_)) } qw/arch lib/ ],
  2         65  
159             );
160 1         28 my $tester = TAP::Harness::Env->create(\%test_args);
161 1 50       38370 $tester->runtests(sort +find(qr/\.t$/, 't'))->has_errors and die "Tests failed";
162             }
163              
164             sub install {
165 1     1 1 7619 my (%extra_opts) = @_;
166 1         5 my %opt = get_opts(%extra_opts);
167 1 50       22 die "Must run `./Build build` first\n" if not -d 'blib';
168 1         5 ExtUtils::Install::install($opt{install_paths}->install_map, @opt{qw/verbose dry_run uninst/});
169             }
170              
171             1;
172              
173             # ABSTRACT: static CPAN installation reference implementation
174              
175             __END__