File Coverage

lib/Module/Build/Tiny.pm
Criterion Covered Total %
statement 73 130 56.1
branch 7 34 20.5
condition 3 12 25.0
subroutine 20 26 76.9
pod 1 10 10.0
total 104 212 49.0


line stmt bran cond sub pod time code
1             package Module::Build::Tiny;
2             $Module::Build::Tiny::VERSION = '0.053';
3 1     1   7 use strict;
  1         1  
  1         53  
4 1     1   6 use warnings;
  1         1  
  1         81  
5 1     1   8 use Exporter 5.57 'import';
  1         21  
  1         59  
6             our @EXPORT = qw/Build Build_PL/;
7              
8 1     1   732 use CPAN::Meta;
  1         47805  
  1         55  
9 1     1   779 use ExtUtils::Config 0.003;
  1         848  
  1         56  
10 1     1   525 use ExtUtils::Helpers 0.020 qw/make_executable split_like_shell man1_pagename man3_pagename detildefy/;
  1         7534  
  1         103  
11 1     1   703 use ExtUtils::Install qw/pm_to_blib install/;
  1         11599  
  1         61  
12 1     1   400 use ExtUtils::InstallPaths 0.002;
  1         2885  
  1         33  
13 1     1   5 use File::Basename qw/basename dirname/;
  1         1  
  1         43  
14 1     1   3 use File::Find ();
  1         9  
  1         15  
15 1     1   3 use File::Path qw/mkpath rmtree/;
  1         1  
  1         57  
16 1     1   3 use File::Spec::Functions qw/catfile catdir rel2abs abs2rel splitdir curdir/;
  1         1  
  1         54  
17 1     1   711 use Getopt::Long 2.36 qw/GetOptionsFromArray/;
  1         10233  
  1         32  
18 1     1   912 use JSON::PP 2 qw/encode_json decode_json/;
  1         13692  
  1         2436  
19              
20             sub write_file {
21 0     0 0 0 my ($filename, $content) = @_;
22 0 0       0 open my $fh, '>', $filename or die "Could not open $filename: $!\n";
23 0         0 print $fh $content;
24             }
25             sub read_file {
26 1     1 0 3 my ($filename) = @_;
27 1 50       67 open my $fh, '<', $filename or die "Could not open $filename: $!\n";
28 1         4 return do { local $/; <$fh> };
  1         5  
  1         69  
29             }
30              
31             sub get_meta {
32 1 50   1 0 5 my ($metafile) = grep { -e $_ } qw/META.json META.yml/ or die "No META information provided\n";
  2         41  
33 1         9 return CPAN::Meta->load_file($metafile);
34             }
35              
36             sub manify {
37 0     0 0 0 my ($input_file, $output_file, $section, $opts) = @_;
38 0 0 0     0 return if -e $output_file && -M $input_file <= -M $output_file;
39 0         0 my $dirname = dirname($output_file);
40 0 0       0 mkpath($dirname, $opts->{verbose}) if not -d $dirname;
41 0         0 require Pod::Man;
42 0         0 Pod::Man->new(section => $section)->parse_from_file($input_file, $output_file);
43 0 0 0     0 print "Manifying $output_file\n" if $opts->{verbose} && $opts->{verbose} > 0;
44 0         0 return;
45             }
46              
47             sub process_xs {
48 0     0 0 0 my ($source, $options, $c_files) = @_;
49              
50 0 0       0 die "Can't build xs files under --pureperl-only\n" if $options->{'pureperl-only'};
51 0         0 my (undef, @parts) = splitdir(dirname($source));
52 0         0 push @parts, my $file_base = basename($source, '.xs');
53 0         0 my $archdir = catdir(qw/blib arch auto/, @parts);
54 0         0 my $tempdir = 'temp';
55              
56 0         0 my $c_file = catfile($tempdir, "$file_base.c");
57 0         0 require ExtUtils::ParseXS;
58 0         0 mkpath($tempdir, $options->{verbose}, oct '755');
59 0         0 ExtUtils::ParseXS::process_file(filename => $source, prototypes => 0, output => $c_file);
60              
61 0         0 my $version = $options->{meta}->version;
62 0         0 require ExtUtils::CBuilder;
63 0         0 my $builder = ExtUtils::CBuilder->new(config => $options->{config}->values_set);
64 0         0 my @objects = $builder->compile(source => $c_file, defines => { VERSION => qq/"$version"/, XS_VERSION => qq/"$version"/ }, include_dirs => [ curdir, 'include', 'src', dirname($source) ], extra_compiler_flags => $options->{extra_compiler_flags});
65              
66 0         0 my $o = $options->{config}->get('_o');
67 0         0 for my $c_source (@{ $c_files }) {
  0         0  
68 0         0 my $o_file = catfile($tempdir, basename($c_source, '.c') . $o);
69 0         0 push @objects, $builder->compile(source => $c_source, include_dirs => [ curdir, 'include', 'src', dirname($c_source) ], extra_compiler_flags => $options->{extra_compiler_flags});
70             }
71              
72 0         0 require DynaLoader;
73 0 0   0   0 my $mod2fname = defined &DynaLoader::mod2fname ? \&DynaLoader::mod2fname : sub { return $_[0][-1] };
  0         0  
74              
75 0 0       0 mkpath($archdir, $options->{verbose}, oct '755') unless -d $archdir;
76 0         0 my $lib_file = catfile($archdir, $mod2fname->(\@parts) . '.' . $options->{config}->get('dlext'));
77 0         0 return $builder->link(objects => \@objects, lib_file => $lib_file, extra_linker_flags => $options->{extra_linker_flags}, module_name => join '::', @parts);
78             }
79              
80             sub find {
81 1     1 0 4 my ($pattern, $dir) = @_;
82 1         2 my @ret;
83 1 100 66 4   149 File::Find::find(sub { push @ret, $File::Find::name if /$pattern/ && -f }, $dir) if -d $dir;
  4 50       395  
84 1         15 return @ret;
85             }
86              
87             sub contains_pod {
88 0     0 0 0 my ($file) = @_;
89 0 0       0 return unless -T $file;
90 0         0 return read_file($file) =~ /^\=(?:head|pod|item)/m;
91             }
92              
93             my %actions = (
94             build => sub {
95             my %opt = @_;
96             for my $pl_file (find(qr/\.PL$/, 'lib')) {
97             (my $pm = $pl_file) =~ s/\.PL$//;
98             system $^X, $pl_file, $pm and die "$pl_file returned $?\n";
99             }
100             my %modules = map { $_ => catfile('blib', $_) } find(qr/\.pm$/, 'lib');
101             my %docs = map { $_ => catfile('blib', $_) } find(qr/\.pod$/, 'lib');
102             my %scripts = map { $_ => catfile('blib', $_) } find(qr/(?:)/, 'script');
103             my %sdocs = map { $_ => delete $scripts{$_} } grep { /.pod$/ } keys %scripts;
104             my %dist_shared = map { $_ => catfile(qw/blib lib auto share dist/, $opt{meta}->name, abs2rel($_, 'share')) } find(qr/(?:)/, 'share');
105             my %module_shared = map { $_ => catfile(qw/blib lib auto share module/, abs2rel($_, 'module-share')) } find(qr/(?:)/, 'module-share');
106             pm_to_blib({ %modules, %docs, %scripts, %dist_shared, %module_shared }, catdir(qw/blib lib auto/));
107             make_executable($_) for values %scripts;
108             mkpath(catdir(qw/blib arch/), $opt{verbose});
109             my $main_xs = join('/', 'lib', split /-/, $opt{meta}->name) . '.xs';
110             for my $xs (find(qr/.xs$/, 'lib')) {
111             my @c_files = $xs eq $main_xs ? find(qr/\.c$/, 'src') : ();
112             process_xs($xs, \%opt, \@c_files);
113             }
114              
115             if ($opt{install_paths}->install_destination('bindoc') && $opt{install_paths}->is_default_installable('bindoc')) {
116             my $section = $opt{config}->get('man1ext');
117             for my $input (keys %scripts, keys %sdocs) {
118             next unless contains_pod($input);
119             my $output = catfile('blib', 'bindoc', man1_pagename($input));
120             manify($input, $output, $section, \%opt);
121             }
122             }
123             if ($opt{install_paths}->install_destination('libdoc') && $opt{install_paths}->is_default_installable('libdoc')) {
124             my $section = $opt{config}->get('man3ext');
125             for my $input (keys %modules, keys %docs) {
126             next unless contains_pod($input);
127             my $output = catfile('blib', 'libdoc', man3_pagename($input));
128             manify($input, $output, $section, \%opt);
129             }
130             }
131             return 0;
132             },
133             test => sub {
134             my %opt = @_;
135             die "Must run `./Build build` first\n" if not -d 'blib';
136             require TAP::Harness::Env;
137             my %test_args = (
138             (verbosity => $opt{verbose}) x!! exists $opt{verbose},
139             (jobs => $opt{jobs}) x!! exists $opt{jobs},
140             (color => 1) x !!-t STDOUT,
141             lib => [ map { rel2abs(catdir(qw/blib/, $_)) } qw/arch lib/ ],
142             );
143             my $tester = TAP::Harness::Env->create(\%test_args);
144             local $ENV{PERL_DL_NONLAZY} = 1;
145             return $tester->runtests(sort +find(qr/\.t$/, 't'))->has_errors;
146             },
147             install => sub {
148             my %opt = @_;
149             die "Must run `./Build build` first\n" if not -d 'blib';
150             install($opt{install_paths}->install_map, @opt{qw/verbose dry_run uninst/});
151             return 0;
152             },
153             clean => sub {
154             my %opt = @_;
155             rmtree($_, $opt{verbose}) for qw/blib temp/;
156             return 0;
157             },
158             realclean => sub {
159             my %opt = @_;
160             rmtree($_, $opt{verbose}) for qw/blib temp Build _build_params MYMETA.yml MYMETA.json/;
161             return 0;
162             },
163             );
164              
165             my @options = qw/install_base=s install_path=s% installdirs=s destdir=s prefix=s config=s% uninst:1 verbose:1 dry_run:1 pureperl-only:1 create_packlist=i jobs=i extra_compiler_flags=s extra_linker_flags=s/;
166              
167             sub get_arguments {
168 1     1 0 28 my @sources = @_;
169 1         4 my %opt;
170 1         9 GetOptionsFromArray($_, \%opt, @options) for @sources;
171 1         4761 $_ = detildefy($_) for grep { defined } @opt{qw/install_base destdir prefix/}, values %{ $opt{install_path} };
  3         11  
  1         4  
172 1         4 $_ = [ split_like_shell($_) ] for grep { defined } @opt{qw/extra_compiler_flags extra_linker_flags/};
  2         11  
173 1         519 $opt{config} = ExtUtils::Config->new($opt{config});
174 1         35 $opt{meta} = get_meta();
175 1         55552 $opt{install_paths} = ExtUtils::InstallPaths->new(%opt, dist_name => $opt{meta}->name);
176 1         2051 return %opt;
177             }
178              
179             sub Build {
180 1 50 33 1 1 19 my $action = @ARGV && $ARGV[0] =~ /\A\w+\z/ ? shift @ARGV : 'build';
181 1 50       6 die "No such action '$action'\n" if not $actions{$action};
182 1         2 my($env, $bargv) = @{ decode_json(read_file('_build_params')) };
  1         8  
183 1         271 my %opt = get_arguments($env, $bargv, \@ARGV);
184 1         8 exit $actions{$action}->(%opt);
185             }
186              
187             sub Build_PL {
188 0     0 0   my $meta = get_meta();
189 0           printf "Creating new 'Build' script for '%s' version '%s'\n", $meta->name, $meta->version;
190 0 0         my $dir = $meta->name eq 'Module-Build-Tiny' ? "use lib 'lib';" : '';
191 0           write_file('Build', "#!perl\n$dir\nuse Module::Build::Tiny;\nBuild();\n");
192 0           make_executable('Build');
193 0 0         my @env = defined $ENV{PERL_MB_OPT} ? split_like_shell($ENV{PERL_MB_OPT}) : ();
194 0           write_file('_build_params', encode_json([ \@env, \@ARGV ]));
195 0           my %mymeta = %{ $meta->as_struct };
  0            
196 0 0         if (my $dynamic = $meta->custom('x_dynamic_prereqs')) {
197 0           my %opt = get_arguments(\@env, \@ARGV);
198 0           require CPAN::Requirements::Dynamic;
199 0           my $dynamic_parser = CPAN::Requirements::Dynamic->new(%opt);
200 0           my $prereq = $dynamic_parser->evaluate($dynamic);
201 0           $mymeta{prereqs} = $meta->effective_prereqs->with_merged_prereqs($prereq)->as_string_hash;
202             }
203 0           $mymeta{dynamic_config} = 0;
204 0           my $mymeta = CPAN::Meta->new(\%mymeta);
205 0           $mymeta->save(@$_) for ['MYMETA.json'], [ 'MYMETA.yml' => { version => 1.4 } ];
206             }
207              
208             1;
209              
210             #ABSTRACT: A tiny replacement for Module::Build
211              
212              
213             # vi:noet:sts=4:sw=4:ts=4
214              
215             __END__