Commit | Line | Data |
---|---|---|
bb4e9162 YST |
1 | package Module::Build::Base; |
2 | ||
3 | use strict; | |
4 | BEGIN { require 5.00503 } | |
dc8021d3 SP |
5 | |
6 | use Carp; | |
bb4e9162 YST |
7 | use Config; |
8 | use File::Copy (); | |
9 | use File::Find (); | |
10 | use File::Path (); | |
11 | use File::Basename (); | |
12 | use File::Spec 0.82 (); | |
13 | use File::Compare (); | |
14 | use Data::Dumper (); | |
15 | use IO::File (); | |
16 | use Text::ParseWords (); | |
bb4e9162 YST |
17 | |
18 | use Module::Build::ModuleInfo; | |
19 | use Module::Build::Notes; | |
20 | ||
21 | ||
22 | #################### Constructors ########################### | |
23 | sub new { | |
24 | my $self = shift()->_construct(@_); | |
25 | ||
26 | $self->{invoked_action} = $self->{action} ||= 'Build_PL'; | |
27 | $self->cull_args(@ARGV); | |
28 | ||
29 | die "Too early to specify a build action '$self->{action}'. Do 'Build $self->{action}' instead.\n" | |
30 | if $self->{action} && $self->{action} ne 'Build_PL'; | |
31 | ||
32 | $self->dist_name; | |
33 | $self->dist_version; | |
34 | ||
35 | $self->check_manifest; | |
36 | $self->check_prereq; | |
37 | $self->check_autofeatures; | |
38 | ||
39 | $self->_set_install_paths; | |
40 | $self->_find_nested_builds; | |
41 | ||
42 | return $self; | |
43 | } | |
44 | ||
45 | sub resume { | |
46 | my $package = shift; | |
47 | my $self = $package->_construct(@_); | |
48 | $self->read_config; | |
49 | ||
50 | # If someone called Module::Build->current() or | |
51 | # Module::Build->new_from_context() and the correct class to use is | |
52 | # actually a *subclass* of Module::Build, we may need to load that | |
53 | # subclass here and re-delegate the resume() method to it. | |
54 | unless ( UNIVERSAL::isa($package, $self->build_class) ) { | |
55 | my $build_class = $self->build_class; | |
56 | my $config_dir = $self->config_dir || '_build'; | |
57 | my $build_lib = File::Spec->catdir( $config_dir, 'lib' ); | |
58 | unshift( @INC, $build_lib ); | |
59 | unless ( $build_class->can('new') ) { | |
60 | eval "require $build_class; 1" or die "Failed to re-load '$build_class': $@"; | |
61 | } | |
62 | return $build_class->resume(@_); | |
63 | } | |
64 | ||
65 | unless ($self->_perl_is_same($self->{properties}{perl})) { | |
66 | my $perl = $self->find_perl_interpreter; | |
67 | $self->log_warn(" * WARNING: Configuration was initially created with '$self->{properties}{perl}',\n". | |
68 | " but we are now using '$perl'.\n"); | |
69 | } | |
70 | ||
71 | my $mb_version = $Module::Build::VERSION; | |
72 | die(" * ERROR: Configuration was initially created with Module::Build version '$self->{properties}{mb_version}',\n". | |
73 | " but we are now using version '$mb_version'. Please re-run the Build.PL or Makefile.PL script.\n") | |
74 | unless $mb_version eq $self->{properties}{mb_version}; | |
75 | ||
76 | $self->cull_args(@ARGV); | |
77 | $self->{invoked_action} = $self->{action} ||= 'build'; | |
78 | ||
79 | return $self; | |
80 | } | |
81 | ||
82 | sub new_from_context { | |
83 | my ($package, %args) = @_; | |
84 | ||
85 | # XXX Read the META.yml and see whether we need to run the Build.PL? | |
86 | ||
87 | # Run the Build.PL. We use do() rather than run_perl_script() so | |
88 | # that it runs in this process rather than a subprocess, because we | |
89 | # need to make sure that the environment is the same during Build.PL | |
90 | # as it is during resume() (and thereafter). | |
91 | { | |
92 | local @ARGV = $package->unparse_args(\%args); | |
93 | do 'Build.PL'; | |
94 | die $@ if $@; | |
95 | } | |
96 | return $package->resume; | |
97 | } | |
98 | ||
99 | sub current { | |
100 | # hmm, wonder what the right thing to do here is | |
101 | local @ARGV; | |
102 | return shift()->resume; | |
103 | } | |
104 | ||
105 | sub _construct { | |
106 | my ($package, %input) = @_; | |
107 | ||
108 | my $args = delete $input{args} || {}; | |
109 | my $config = delete $input{config} || {}; | |
110 | ||
111 | my $self = bless { | |
112 | args => {%$args}, | |
113 | config => {%Config, %$config}, | |
114 | properties => { | |
115 | base_dir => $package->cwd, | |
116 | mb_version => $Module::Build::VERSION, | |
117 | %input, | |
118 | }, | |
119 | phash => {}, | |
120 | }, $package; | |
121 | ||
122 | $self->_set_defaults; | |
123 | my ($p, $c, $ph) = ($self->{properties}, $self->{config}, $self->{phash}); | |
124 | ||
125 | foreach (qw(notes config_data features runtime_params cleanup auto_features)) { | |
126 | my $file = File::Spec->catfile($self->config_dir, $_); | |
127 | $ph->{$_} = Module::Build::Notes->new(file => $file); | |
128 | $ph->{$_}->restore if -e $file; | |
129 | if (exists $p->{$_}) { | |
130 | my $vals = delete $p->{$_}; | |
131 | while (my ($k, $v) = each %$vals) { | |
132 | $self->$_($k, $v); | |
133 | } | |
134 | } | |
135 | } | |
136 | ||
137 | # The following warning could be unnecessary if the user is running | |
138 | # an embedded perl, but there aren't too many of those around, and | |
139 | # embedded perls aren't usually used to install modules, and the | |
140 | # installation process sometimes needs to run external scripts | |
141 | # (e.g. to run tests). | |
142 | $p->{perl} = $self->find_perl_interpreter | |
143 | or $self->log_warn("Warning: Can't locate your perl binary"); | |
144 | ||
145 | my $blibdir = sub { File::Spec->catdir($p->{blib}, @_) }; | |
146 | $p->{bindoc_dirs} ||= [ $blibdir->("script") ]; | |
147 | $p->{libdoc_dirs} ||= [ $blibdir->("lib"), $blibdir->("arch") ]; | |
148 | ||
149 | $p->{dist_author} = [ $p->{dist_author} ] if defined $p->{dist_author} and not ref $p->{dist_author}; | |
150 | ||
151 | # Synonyms | |
152 | $p->{requires} = delete $p->{prereq} if defined $p->{prereq}; | |
153 | $p->{script_files} = delete $p->{scripts} if defined $p->{scripts}; | |
154 | ||
155 | # Convert to arrays | |
156 | for ('extra_compiler_flags', 'extra_linker_flags') { | |
157 | $p->{$_} = [ $self->split_like_shell($p->{$_}) ] if exists $p->{$_}; | |
158 | } | |
159 | ||
160 | $self->add_to_cleanup( @{delete $p->{add_to_cleanup}} ) | |
161 | if $p->{add_to_cleanup}; | |
162 | ||
163 | return $self; | |
164 | } | |
165 | ||
166 | ################## End constructors ######################### | |
167 | ||
168 | sub log_info { print @_ unless shift()->quiet } | |
169 | sub log_verbose { shift()->log_info(@_) if $_[0]->verbose } | |
170 | sub log_warn { | |
171 | # Try to make our call stack invisible | |
172 | shift; | |
173 | if (@_ and $_[-1] !~ /\n$/) { | |
174 | my (undef, $file, $line) = caller(); | |
175 | warn @_, " at $file line $line.\n"; | |
176 | } else { | |
177 | warn @_; | |
178 | } | |
179 | } | |
180 | ||
181 | ||
182 | sub _set_install_paths { | |
183 | my $self = shift; | |
184 | my $c = $self->config; | |
185 | my $p = $self->{properties}; | |
186 | ||
187 | my @libstyle = $c->{installstyle} ? | |
188 | File::Spec->splitdir($c->{installstyle}) : qw(lib perl5); | |
189 | my $arch = $c->{archname}; | |
190 | my $version = $c->{version}; | |
191 | ||
192 | my $bindoc = $c->{installman1dir} || undef; | |
193 | my $libdoc = $c->{installman3dir} || undef; | |
194 | ||
195 | my $binhtml = $c->{installhtml1dir} || $c->{installhtmldir} || undef; | |
196 | my $libhtml = $c->{installhtml3dir} || $c->{installhtmldir} || undef; | |
197 | ||
198 | $p->{install_sets} = | |
199 | { | |
200 | core => { | |
201 | lib => $c->{installprivlib}, | |
202 | arch => $c->{installarchlib}, | |
203 | bin => $c->{installbin}, | |
204 | script => $c->{installscript}, | |
205 | bindoc => $bindoc, | |
206 | libdoc => $libdoc, | |
207 | binhtml => $binhtml, | |
208 | libhtml => $libhtml, | |
209 | }, | |
210 | site => { | |
211 | lib => $c->{installsitelib}, | |
212 | arch => $c->{installsitearch}, | |
213 | bin => $c->{installsitebin} || $c->{installbin}, | |
214 | script => $c->{installsitescript} || | |
215 | $c->{installsitebin} || $c->{installscript}, | |
216 | bindoc => $c->{installsiteman1dir} || $bindoc, | |
217 | libdoc => $c->{installsiteman3dir} || $libdoc, | |
218 | binhtml => $c->{installsitehtml1dir} || $binhtml, | |
219 | libhtml => $c->{installsitehtml3dir} || $libhtml, | |
220 | }, | |
221 | vendor => { | |
222 | lib => $c->{installvendorlib}, | |
223 | arch => $c->{installvendorarch}, | |
224 | bin => $c->{installvendorbin} || $c->{installbin}, | |
225 | script => $c->{installvendorscript} || | |
226 | $c->{installvendorbin} || $c->{installscript}, | |
227 | bindoc => $c->{installvendorman1dir} || $bindoc, | |
228 | libdoc => $c->{installvendorman3dir} || $libdoc, | |
229 | binhtml => $c->{installvendorhtml1dir} || $binhtml, | |
230 | libhtml => $c->{installvendorhtml3dir} || $libhtml, | |
231 | }, | |
232 | }; | |
233 | ||
234 | $p->{original_prefix} = | |
235 | { | |
236 | core => $c->{installprefixexp} || $c->{installprefix} || | |
237 | $c->{prefixexp} || $c->{prefix} || '', | |
238 | site => $c->{siteprefixexp}, | |
239 | vendor => $c->{usevendorprefix} ? $c->{vendorprefixexp} : '', | |
240 | }; | |
241 | $p->{original_prefix}{site} ||= $p->{original_prefix}{core}; | |
242 | ||
243 | # Note: you might be tempted to use $Config{installstyle} here | |
244 | # instead of hard-coding lib/perl5, but that's been considered and | |
245 | # (at least for now) rejected. `perldoc Config` has some wisdom | |
246 | # about it. | |
247 | $p->{install_base_relpaths} = | |
248 | { | |
249 | lib => ['lib', 'perl5'], | |
250 | arch => ['lib', 'perl5', $arch], | |
251 | bin => ['bin'], | |
252 | script => ['bin'], | |
253 | bindoc => ['man', 'man1'], | |
254 | libdoc => ['man', 'man3'], | |
255 | binhtml => ['html'], | |
256 | libhtml => ['html'], | |
257 | }; | |
258 | ||
259 | $p->{prefix_relpaths} = | |
260 | { | |
261 | core => { | |
262 | lib => [@libstyle], | |
263 | arch => [@libstyle, $version, $arch], | |
264 | bin => ['bin'], | |
265 | script => ['bin'], | |
266 | bindoc => ['man', 'man1'], | |
267 | libdoc => ['man', 'man3'], | |
268 | binhtml => ['html'], | |
269 | libhtml => ['html'], | |
270 | }, | |
271 | vendor => { | |
272 | lib => [@libstyle], | |
273 | arch => [@libstyle, $version, $arch], | |
274 | bin => ['bin'], | |
275 | script => ['bin'], | |
276 | bindoc => ['man', 'man1'], | |
277 | libdoc => ['man', 'man3'], | |
278 | binhtml => ['html'], | |
279 | libhtml => ['html'], | |
280 | }, | |
281 | site => { | |
282 | lib => [@libstyle, 'site_perl'], | |
283 | arch => [@libstyle, 'site_perl', $version, $arch], | |
284 | bin => ['bin'], | |
285 | script => ['bin'], | |
286 | bindoc => ['man', 'man1'], | |
287 | libdoc => ['man', 'man3'], | |
288 | binhtml => ['html'], | |
289 | libhtml => ['html'], | |
290 | }, | |
291 | }; | |
292 | ||
293 | } | |
294 | ||
295 | sub _find_nested_builds { | |
296 | my $self = shift; | |
297 | my $r = $self->recurse_into or return; | |
298 | ||
299 | my ($file, @r); | |
300 | if (!ref($r) && $r eq 'auto') { | |
301 | local *DH; | |
302 | opendir DH, $self->base_dir | |
303 | or die "Can't scan directory " . $self->base_dir . " for nested builds: $!"; | |
304 | while (defined($file = readdir DH)) { | |
305 | my $subdir = File::Spec->catdir( $self->base_dir, $file ); | |
306 | next unless -d $subdir; | |
307 | push @r, $subdir if -e File::Spec->catfile( $subdir, 'Build.PL' ); | |
308 | } | |
309 | } | |
310 | ||
311 | $self->recurse_into(\@r); | |
312 | } | |
313 | ||
314 | sub cwd { | |
315 | require Cwd; | |
316 | return Cwd::cwd(); | |
317 | } | |
318 | ||
a314697d RS |
319 | sub _quote_args { |
320 | # Returns a string that can become [part of] a command line with | |
321 | # proper quoting so that the subprocess sees this same list of args. | |
322 | my ($self, @args) = @_; | |
323 | ||
324 | my $return_args = ''; | |
325 | my @quoted; | |
326 | ||
327 | for (@args) { | |
328 | if ( /^[^\s*?!$<>;\\|'"\[\]\{\}]+$/ ) { | |
329 | # Looks pretty safe | |
330 | push @quoted, $_; | |
331 | } else { | |
332 | # XXX this will obviously have to improve - is there already a | |
333 | # core module lying around that does proper quoting? | |
334 | s/"/"'"'"/g; | |
335 | push @quoted, qq("$_"); | |
336 | } | |
337 | } | |
338 | ||
339 | return join " ", @quoted; | |
340 | } | |
341 | ||
342 | sub _backticks { | |
a314697d | 343 | my ($self, @cmd) = @_; |
dc8021d3 | 344 | if ($self->have_forkpipe) { |
a314697d | 345 | local *FH; |
dc8021d3 SP |
346 | my $pid = open FH, "-|"; |
347 | if ($pid) { | |
348 | return wantarray ? <FH> : join '', <FH>; | |
349 | } else { | |
350 | die "Can't execute @cmd: $!\n" unless defined $pid; | |
351 | exec { $cmd[0] } @cmd; | |
352 | } | |
a314697d RS |
353 | } else { |
354 | my $cmd = $self->_quote_args(@cmd); | |
355 | return `$cmd`; | |
356 | } | |
357 | } | |
358 | ||
dc8021d3 | 359 | sub have_forkpipe { 1 } |
a314697d RS |
360 | |
361 | # Determine whether a given binary is the same as the perl | |
362 | # (configuration) that started this process. | |
bb4e9162 YST |
363 | sub _perl_is_same { |
364 | my ($self, $perl) = @_; | |
a314697d RS |
365 | |
366 | my @cmd = ($perl); | |
367 | ||
368 | # When run from the perl core, @INC will include the directories | |
369 | # where perl is yet to be installed. We need to reference the | |
370 | # absolute path within the source distribution where it can find | |
371 | # it's Config.pm This also prevents us from picking up a Config.pm | |
372 | # from a different configuration that happens to be already | |
373 | # installed in @INC. | |
374 | if ($ENV{PERL_CORE}) { | |
375 | push @cmd, '-I' . File::Spec->catdir(File::Basename::dirname($perl), 'lib'); | |
376 | } | |
377 | ||
378 | push @cmd, qw(-MConfig=myconfig -e print -e myconfig); | |
379 | return $self->_backticks(@cmd) eq Config->myconfig; | |
bb4e9162 YST |
380 | } |
381 | ||
a314697d RS |
382 | # Returns the absolute path of the perl interperter used to invoke |
383 | # this process. The path is derived from $^X or $Config{perlpath}. On | |
384 | # some platforms $^X contains the complete absolute path of the | |
385 | # interpreter, on other it may contain a relative path, or simply | |
386 | # 'perl'. This can also vary depending on whether a path was supplied | |
387 | # when perl was invoked. Additionally, the value in $^X may omit the | |
388 | # executable extension on platforms that use one. It's a fatal error | |
389 | # if the interpreter can't be found because it can result in undefined | |
390 | # behavior by routines that depend on it (generating errors or | |
391 | # invoking the wrong perl. | |
bb4e9162 | 392 | sub find_perl_interpreter { |
bb4e9162 | 393 | my $proto = shift; |
a314697d | 394 | my $c = ref($proto) ? $proto->config : \%Config::Config; |
bb4e9162 | 395 | |
a314697d RS |
396 | my $perl = $^X; |
397 | my $perl_basename = File::Basename::basename($perl); | |
398 | ||
399 | my @potential_perls; | |
400 | ||
401 | # Try 1, Check $^X for absolute path | |
402 | push( @potential_perls, $perl ) | |
403 | if File::Spec->file_name_is_absolute($perl); | |
404 | ||
405 | # Try 2, Check $^X for a valid relative path | |
406 | my $abs_perl = File::Spec->rel2abs($perl); | |
407 | push( @potential_perls, $abs_perl ); | |
47f13fd5 | 408 | |
a314697d RS |
409 | # Try 3, Last ditch effort: These two option use hackery to try to locate |
410 | # a suitable perl. The hack varies depending on whether we are running | |
411 | # from an installed perl or an uninstalled perl in the perl source dist. | |
47f13fd5 | 412 | if ($ENV{PERL_CORE}) { |
a314697d RS |
413 | |
414 | # Try 3.A, If we are in a perl source tree, running an uninstalled | |
415 | # perl, we can keep moving up the directory tree until we find our | |
416 | # binary. We wouldn't do this under any other circumstances. | |
417 | ||
47f13fd5 SP |
418 | # CBuilder is also in the core, so it should be available here |
419 | require ExtUtils::CBuilder; | |
a314697d RS |
420 | my $perl_src = ExtUtils::CBuilder->perl_src; |
421 | if ( defined($perl_src) && length($perl_src) ) { | |
422 | my $uninstperl = | |
423 | File::Spec->rel2abs(File::Spec->catfile( $perl_src, $perl_basename )); | |
424 | push( @potential_perls, $uninstperl ); | |
425 | } | |
426 | ||
427 | } else { | |
428 | ||
429 | # Try 3.B, First look in $Config{perlpath}, then search the users | |
430 | # PATH. We do not want to do either if we are running from an | |
431 | # uninstalled perl in a perl source tree. | |
432 | ||
433 | push( @potential_perls, $c->{perlpath} ); | |
434 | ||
435 | push( @potential_perls, | |
436 | map File::Spec->catfile($_, $perl_basename), File::Spec->path() ); | |
47f13fd5 SP |
437 | } |
438 | ||
a314697d RS |
439 | # Now that we've enumerated the potential perls, it's time to test |
440 | # them to see if any of them match our configuration, returning the | |
441 | # absolute path of the first successful match. | |
442 | my $exe = $c->{exe_ext}; | |
443 | foreach my $thisperl ( @potential_perls ) { | |
444 | ||
445 | if ($proto->os_type eq 'VMS') { | |
446 | # VMS might have a file version at the end | |
447 | $thisperl .= $exe unless $thisperl =~ m/$exe(;\d+)?$/i; | |
448 | } elsif (defined $exe) { | |
449 | $thisperl .= $exe unless $thisperl =~ m/$exe$/i; | |
450 | } | |
451 | ||
452 | if ( -f $thisperl && $proto->_perl_is_same($thisperl) ) { | |
453 | return $thisperl; | |
454 | } | |
bb4e9162 | 455 | } |
a314697d RS |
456 | |
457 | # We've tried all alternatives, and didn't find a perl that matches | |
458 | # our configuration. Throw an exception, and list alternatives we tried. | |
459 | my @paths = map File::Basename::dirname($_), @potential_perls; | |
460 | die "Can't locate the perl binary used to run this script " . | |
461 | "in (@paths)\n"; | |
bb4e9162 YST |
462 | } |
463 | ||
464 | sub _is_interactive { | |
465 | return -t STDIN && (-t STDOUT || !(-f STDOUT || -c STDOUT)) ; # Pipe? | |
466 | } | |
467 | ||
dc8021d3 SP |
468 | sub _is_unattended { |
469 | my $self = shift; | |
470 | return $ENV{PERL_MM_USE_DEFAULT} || ( !$self->_is_interactive && eof STDIN ); | |
471 | } | |
472 | ||
473 | sub _readline { | |
474 | my $self = shift; | |
475 | return undef if $self->_is_unattended; | |
476 | ||
477 | my $answer = <STDIN>; | |
478 | chomp $answer if defined $answer; | |
479 | return $answer; | |
480 | } | |
481 | ||
bb4e9162 YST |
482 | sub prompt { |
483 | my $self = shift; | |
dc8021d3 SP |
484 | my $mess = shift |
485 | or die "prompt() called without a prompt message"; | |
486 | ||
487 | my $def; | |
488 | if ( $self->_is_unattended && !@_ ) { | |
489 | die <<EOF; | |
490 | ERROR: This build seems to be unattended, but there is no default value | |
491 | for this question. Aborting. | |
492 | EOF | |
493 | } | |
494 | $def = shift if @_; | |
bb4e9162 YST |
495 | ($def, my $dispdef) = defined $def ? ($def, "[$def] ") : ('', ' '); |
496 | ||
dc8021d3 SP |
497 | local $|=1; |
498 | print "$mess $dispdef"; | |
499 | ||
500 | my $ans = $self->_readline(); | |
501 | ||
b3dfda33 SP |
502 | if ( !defined($ans) # Ctrl-D or unattended |
503 | or !length($ans) ) { # User hit return | |
bb4e9162 YST |
504 | print "$def\n"; |
505 | $ans = $def; | |
506 | } | |
dc8021d3 | 507 | |
bb4e9162 YST |
508 | return $ans; |
509 | } | |
510 | ||
511 | sub y_n { | |
512 | my $self = shift; | |
dc8021d3 SP |
513 | my ($mess, $def) = @_; |
514 | ||
515 | die "y_n() called without a prompt message" unless $mess; | |
516 | die "Invalid default value: y_n() default must be 'y' or 'n'" | |
517 | if $def && $def !~ /^[yn]/i; | |
518 | ||
519 | if ( $self->_is_unattended && !$def ) { | |
520 | die <<EOF; | |
521 | ERROR: This build seems to be unattended, but there is no default value | |
522 | for this question. Aborting. | |
523 | EOF | |
524 | } | |
bb4e9162 | 525 | |
bb4e9162 | 526 | my $answer; |
dc8021d3 | 527 | while (1) { # XXX Infinite or a large number followed by an exception ? |
bb4e9162 YST |
528 | $answer = $self->prompt(@_); |
529 | return 1 if $answer =~ /^y/i; | |
530 | return 0 if $answer =~ /^n/i; | |
dc8021d3 | 531 | local $|=1; |
bb4e9162 YST |
532 | print "Please answer 'y' or 'n'.\n"; |
533 | } | |
534 | } | |
535 | ||
536 | sub current_action { shift->{action} } | |
537 | sub invoked_action { shift->{invoked_action} } | |
538 | ||
539 | sub notes { shift()->{phash}{notes}->access(@_) } | |
540 | sub config_data { shift()->{phash}{config_data}->access(@_) } | |
541 | sub runtime_params { shift->{phash}{runtime_params}->read( @_ ? shift : () ) } # Read-only | |
542 | sub auto_features { shift()->{phash}{auto_features}->access(@_) } | |
543 | ||
544 | sub features { | |
545 | my $self = shift; | |
546 | my $ph = $self->{phash}; | |
547 | ||
548 | if (@_) { | |
549 | my $key = shift; | |
550 | if ($ph->{features}->exists($key)) { | |
551 | return $ph->{features}->access($key, @_); | |
552 | } | |
553 | ||
554 | if (my $info = $ph->{auto_features}->access($key)) { | |
555 | my $failures = $self->prereq_failures($info); | |
556 | my $disabled = grep( /^(?:\w+_)?(?:requires|conflicts)$/, | |
557 | keys %$failures ) ? 1 : 0; | |
558 | return !$disabled; | |
559 | } | |
560 | ||
561 | return $ph->{features}->access($key, @_); | |
562 | } | |
563 | ||
564 | # No args - get the auto_features & overlay the regular features | |
565 | my %features; | |
566 | my %auto_features = $ph->{auto_features}->access(); | |
567 | while (my ($name, $info) = each %auto_features) { | |
568 | my $failures = $self->prereq_failures($info); | |
569 | my $disabled = grep( /^(?:\w+_)?(?:requires|conflicts)$/, | |
570 | keys %$failures ) ? 1 : 0; | |
571 | $features{$name} = $disabled ? 0 : 1; | |
572 | } | |
573 | %features = (%features, $ph->{features}->access()); | |
574 | ||
575 | return wantarray ? %features : \%features; | |
576 | } | |
577 | BEGIN { *feature = \&features } | |
578 | ||
579 | sub _mb_feature { | |
580 | my $self = shift; | |
581 | ||
582 | if (($self->module_name || '') eq 'Module::Build') { | |
583 | # We're building Module::Build itself, so ...::ConfigData isn't | |
584 | # valid, but $self->features() should be. | |
585 | return $self->feature(@_); | |
586 | } else { | |
587 | require Module::Build::ConfigData; | |
588 | return Module::Build::ConfigData->feature(@_); | |
589 | } | |
590 | } | |
591 | ||
592 | ||
593 | sub add_build_element { | |
594 | my ($self, $elem) = @_; | |
595 | my $elems = $self->build_elements; | |
596 | push @$elems, $elem unless grep { $_ eq $elem } @$elems; | |
597 | } | |
598 | ||
599 | sub ACTION_config_data { | |
600 | my $self = shift; | |
601 | return unless $self->has_config_data; | |
602 | ||
603 | my $module_name = $self->module_name | |
604 | or die "The config_data feature requires that 'module_name' be set"; | |
605 | my $notes_name = $module_name . '::ConfigData'; # TODO: Customize name ??? | |
606 | my $notes_pm = File::Spec->catfile($self->blib, 'lib', split /::/, "$notes_name.pm"); | |
607 | ||
608 | return if $self->up_to_date(['Build.PL', | |
609 | $self->config_file('config_data'), | |
610 | $self->config_file('features') | |
611 | ], $notes_pm); | |
612 | ||
613 | $self->log_info("Writing config notes to $notes_pm\n"); | |
614 | File::Path::mkpath(File::Basename::dirname($notes_pm)); | |
615 | ||
616 | Module::Build::Notes->write_config_data | |
617 | ( | |
618 | file => $notes_pm, | |
619 | module => $module_name, | |
620 | config_module => $notes_name, | |
621 | config_data => scalar $self->config_data, | |
622 | feature => scalar $self->{phash}{features}->access(), | |
623 | auto_features => scalar $self->auto_features, | |
624 | ); | |
625 | } | |
626 | ||
627 | { | |
628 | my %valid_properties = ( __PACKAGE__, {} ); | |
629 | my %additive_properties; | |
630 | ||
631 | sub _mb_classes { | |
632 | my $class = ref($_[0]) || $_[0]; | |
633 | return ($class, $class->mb_parents); | |
634 | } | |
635 | ||
636 | sub valid_property { | |
637 | my ($class, $prop) = @_; | |
638 | return grep exists( $valid_properties{$_}{$prop} ), $class->_mb_classes; | |
639 | } | |
640 | ||
641 | sub valid_properties { | |
642 | return keys %{ shift->valid_properties_defaults() }; | |
643 | } | |
644 | ||
645 | sub valid_properties_defaults { | |
646 | my %out; | |
647 | for (reverse shift->_mb_classes) { | |
648 | @out{ keys %{ $valid_properties{$_} } } = values %{ $valid_properties{$_} }; | |
649 | } | |
650 | return \%out; | |
651 | } | |
652 | ||
653 | sub array_properties { | |
654 | for (shift->_mb_classes) { | |
655 | return @{$additive_properties{$_}->{ARRAY}} | |
656 | if exists $additive_properties{$_}->{ARRAY}; | |
657 | } | |
658 | } | |
659 | ||
660 | sub hash_properties { | |
661 | for (shift->_mb_classes) { | |
662 | return @{$additive_properties{$_}->{'HASH'}} | |
663 | if exists $additive_properties{$_}->{'HASH'}; | |
664 | } | |
665 | } | |
666 | ||
667 | sub add_property { | |
668 | my ($class, $property, $default) = @_; | |
669 | die "Property '$property' already exists" if $class->valid_property($property); | |
670 | ||
671 | $valid_properties{$class}{$property} = $default; | |
672 | ||
673 | my $type = ref $default; | |
674 | if ($type) { | |
675 | push @{$additive_properties{$class}->{$type}}, $property; | |
676 | } | |
677 | ||
678 | unless ($class->can($property)) { | |
679 | no strict 'refs'; | |
680 | if ( $type eq 'HASH' ) { | |
681 | *{"$class\::$property"} = sub { | |
682 | my $self = shift; | |
683 | my $x = ( $property eq 'config' ) ? $self : $self->{properties}; | |
684 | return $x->{$property} unless @_; | |
685 | ||
686 | if ( defined($_[0]) && !ref($_[0]) ) { | |
687 | if ( @_ == 1 ) { | |
688 | return exists( $x->{$property}{$_[0]} ) ? | |
689 | $x->{$property}{$_[0]} : undef; | |
690 | } elsif ( @_ % 2 == 0 ) { | |
691 | my %args = @_; | |
692 | while ( my($k, $v) = each %args ) { | |
693 | $x->{$property}{$k} = $v; | |
694 | } | |
695 | } else { | |
696 | die "Unexpected arguments for property '$property'\n"; | |
697 | } | |
698 | } else { | |
699 | $x->{$property} = $_[0]; | |
700 | } | |
701 | }; | |
702 | ||
703 | } else { | |
704 | *{"$class\::$property"} = sub { | |
705 | my $self = shift; | |
706 | $self->{properties}{$property} = shift if @_; | |
707 | return $self->{properties}{$property}; | |
708 | } | |
709 | } | |
710 | ||
711 | } | |
712 | return $class; | |
713 | } | |
714 | ||
715 | sub _set_defaults { | |
716 | my $self = shift; | |
717 | ||
718 | # Set the build class. | |
719 | $self->{properties}{build_class} ||= ref $self; | |
720 | ||
721 | # If there was no orig_dir, set to the same as base_dir | |
722 | $self->{properties}{orig_dir} ||= $self->{properties}{base_dir}; | |
723 | ||
724 | my $defaults = $self->valid_properties_defaults; | |
725 | ||
726 | foreach my $prop (keys %$defaults) { | |
727 | $self->{properties}{$prop} = $defaults->{$prop} | |
728 | unless exists $self->{properties}{$prop}; | |
729 | } | |
730 | ||
731 | # Copy defaults for arrays any arrays. | |
732 | for my $prop ($self->array_properties) { | |
733 | $self->{properties}{$prop} = [@{$defaults->{$prop}}] | |
734 | unless exists $self->{properties}{$prop}; | |
735 | } | |
736 | # Copy defaults for arrays any hashes. | |
737 | for my $prop ($self->hash_properties) { | |
738 | $self->{properties}{$prop} = {%{$defaults->{$prop}}} | |
739 | unless exists $self->{properties}{$prop}; | |
740 | } | |
741 | } | |
742 | ||
743 | } | |
744 | ||
745 | # Add the default properties. | |
746 | __PACKAGE__->add_property(blib => 'blib'); | |
747 | __PACKAGE__->add_property(build_class => 'Module::Build'); | |
748 | __PACKAGE__->add_property(build_elements => [qw(PL support pm xs pod script)]); | |
749 | __PACKAGE__->add_property(build_script => 'Build'); | |
750 | __PACKAGE__->add_property(build_bat => 0); | |
751 | __PACKAGE__->add_property(config_dir => '_build'); | |
752 | __PACKAGE__->add_property(include_dirs => []); | |
753 | __PACKAGE__->add_property(installdirs => 'site'); | |
754 | __PACKAGE__->add_property(metafile => 'META.yml'); | |
755 | __PACKAGE__->add_property(recurse_into => []); | |
756 | __PACKAGE__->add_property(use_rcfile => 1); | |
a314697d | 757 | __PACKAGE__->add_property(create_packlist => 1); |
bb4e9162 YST |
758 | |
759 | { | |
760 | my $Is_ActivePerl = eval {require ActivePerl::DocTools}; | |
761 | __PACKAGE__->add_property(html_css => $Is_ActivePerl ? 'Active.css' : ''); | |
762 | } | |
763 | ||
764 | { | |
765 | my @prereq_action_types = qw(requires build_requires conflicts recommends); | |
766 | foreach my $type (@prereq_action_types) { | |
767 | __PACKAGE__->add_property($type => {}); | |
768 | } | |
769 | __PACKAGE__->add_property(prereq_action_types => \@prereq_action_types); | |
770 | } | |
771 | ||
772 | __PACKAGE__->add_property($_ => {}) for qw( | |
773 | config | |
774 | get_options | |
775 | install_base_relpaths | |
776 | install_path | |
777 | install_sets | |
778 | meta_add | |
779 | meta_merge | |
780 | original_prefix | |
781 | prefix_relpaths | |
782 | ); | |
783 | ||
784 | __PACKAGE__->add_property($_) for qw( | |
785 | PL_files | |
786 | autosplit | |
787 | base_dir | |
788 | bindoc_dirs | |
789 | c_source | |
790 | create_makefile_pl | |
791 | create_readme | |
792 | debugger | |
793 | destdir | |
794 | dist_abstract | |
795 | dist_author | |
796 | dist_name | |
797 | dist_version | |
798 | dist_version_from | |
799 | extra_compiler_flags | |
800 | extra_linker_flags | |
801 | has_config_data | |
802 | install_base | |
803 | libdoc_dirs | |
804 | license | |
805 | magic_number | |
806 | mb_version | |
807 | module_name | |
808 | orig_dir | |
809 | perl | |
810 | pm_files | |
811 | pod_files | |
812 | pollute | |
813 | prefix | |
814 | quiet | |
815 | recursive_test_files | |
816 | script_files | |
817 | scripts | |
818 | test_files | |
819 | verbose | |
820 | xs_files | |
821 | ); | |
822 | ||
823 | ||
824 | sub mb_parents { | |
825 | # Code borrowed from Class::ISA. | |
826 | my @in_stack = (shift); | |
827 | my %seen = ($in_stack[0] => 1); | |
828 | ||
829 | my ($current, @out); | |
830 | while (@in_stack) { | |
831 | next unless defined($current = shift @in_stack) | |
832 | && $current->isa('Module::Build::Base'); | |
833 | push @out, $current; | |
834 | next if $current eq 'Module::Build::Base'; | |
835 | no strict 'refs'; | |
836 | unshift @in_stack, | |
837 | map { | |
838 | my $c = $_; # copy, to avoid being destructive | |
839 | substr($c,0,2) = "main::" if substr($c,0,2) eq '::'; | |
840 | # Canonize the :: -> main::, ::foo -> main::foo thing. | |
841 | # Should I ever canonize the Foo'Bar = Foo::Bar thing? | |
842 | $seen{$c}++ ? () : $c; | |
843 | } @{"$current\::ISA"}; | |
844 | ||
845 | # I.e., if this class has any parents (at least, ones I've never seen | |
846 | # before), push them, in order, onto the stack of classes I need to | |
847 | # explore. | |
848 | } | |
849 | shift @out; | |
850 | return @out; | |
851 | } | |
852 | ||
853 | sub extra_linker_flags { shift->_list_accessor('extra_linker_flags', @_) } | |
854 | sub extra_compiler_flags { shift->_list_accessor('extra_compiler_flags', @_) } | |
855 | ||
856 | sub _list_accessor { | |
857 | (my $self, local $_) = (shift, shift); | |
858 | my $p = $self->{properties}; | |
859 | $p->{$_} = [@_] if @_; | |
860 | $p->{$_} = [] unless exists $p->{$_}; | |
861 | return ref($p->{$_}) ? $p->{$_} : [$p->{$_}]; | |
862 | } | |
863 | ||
864 | # XXX Problem - if Module::Build is loaded from a different directory, | |
865 | # it'll look for (and perhaps destroy/create) a _build directory. | |
866 | sub subclass { | |
867 | my ($pack, %opts) = @_; | |
868 | ||
869 | my $build_dir = '_build'; # XXX The _build directory is ostensibly settable by the user. Shouldn't hard-code here. | |
870 | $pack->delete_filetree($build_dir) if -e $build_dir; | |
871 | ||
872 | die "Must provide 'code' or 'class' option to subclass()\n" | |
873 | unless $opts{code} or $opts{class}; | |
874 | ||
875 | $opts{code} ||= ''; | |
876 | $opts{class} ||= 'MyModuleBuilder'; | |
877 | ||
878 | my $filename = File::Spec->catfile($build_dir, 'lib', split '::', $opts{class}) . '.pm'; | |
879 | my $filedir = File::Basename::dirname($filename); | |
880 | $pack->log_info("Creating custom builder $filename in $filedir\n"); | |
881 | ||
882 | File::Path::mkpath($filedir); | |
883 | die "Can't create directory $filedir: $!" unless -d $filedir; | |
884 | ||
885 | my $fh = IO::File->new("> $filename") or die "Can't create $filename: $!"; | |
886 | print $fh <<EOF; | |
887 | package $opts{class}; | |
888 | use $pack; | |
889 | \@ISA = qw($pack); | |
890 | $opts{code} | |
891 | 1; | |
892 | EOF | |
893 | close $fh; | |
894 | ||
895 | unshift @INC, File::Spec->catdir(File::Spec->rel2abs($build_dir), 'lib'); | |
896 | eval "use $opts{class}"; | |
897 | die $@ if $@; | |
898 | ||
899 | return $opts{class}; | |
900 | } | |
901 | ||
902 | sub dist_name { | |
903 | my $self = shift; | |
904 | my $p = $self->{properties}; | |
905 | return $p->{dist_name} if defined $p->{dist_name}; | |
906 | ||
907 | die "Can't determine distribution name, must supply either 'dist_name' or 'module_name' parameter" | |
47f13fd5 | 908 | unless $self->module_name; |
bb4e9162 | 909 | |
47f13fd5 | 910 | ($p->{dist_name} = $self->module_name) =~ s/::/-/g; |
bb4e9162 YST |
911 | |
912 | return $p->{dist_name}; | |
913 | } | |
914 | ||
47f13fd5 | 915 | sub dist_version_from { |
bb4e9162 YST |
916 | my ($self) = @_; |
917 | my $p = $self->{properties}; | |
918 | if ($self->module_name) { | |
47f13fd5 | 919 | $p->{dist_version_from} ||= |
bb4e9162 | 920 | join( '/', 'lib', split(/::/, $self->module_name) ) . '.pm'; |
bb4e9162 | 921 | } |
47f13fd5 | 922 | return $p->{dist_version_from} || undef; |
bb4e9162 YST |
923 | } |
924 | ||
925 | sub dist_version { | |
926 | my ($self) = @_; | |
927 | my $p = $self->{properties}; | |
928 | ||
929 | return $p->{dist_version} if defined $p->{dist_version}; | |
bb4e9162 | 930 | |
47f13fd5 SP |
931 | if ( my $dist_version_from = $self->dist_version_from ) { |
932 | my $version_from = File::Spec->catfile( split( qr{/}, $dist_version_from ) ); | |
bb4e9162 YST |
933 | my $pm_info = Module::Build::ModuleInfo->new_from_file( $version_from ) |
934 | or die "Can't find file $version_from to determine version"; | |
935 | $p->{dist_version} = $pm_info->version(); | |
936 | } | |
937 | ||
938 | die ("Can't determine distribution version, must supply either 'dist_version',\n". | |
939 | "'dist_version_from', or 'module_name' parameter") | |
940 | unless $p->{dist_version}; | |
941 | ||
942 | return $p->{dist_version}; | |
943 | } | |
944 | ||
945 | sub dist_author { shift->_pod_parse('author') } | |
946 | sub dist_abstract { shift->_pod_parse('abstract') } | |
947 | ||
948 | sub _pod_parse { | |
949 | my ($self, $part) = @_; | |
950 | my $p = $self->{properties}; | |
951 | my $member = "dist_$part"; | |
952 | return $p->{$member} if defined $p->{$member}; | |
953 | ||
954 | my $docfile = $self->_main_docfile | |
955 | or return; | |
956 | my $fh = IO::File->new($docfile) | |
957 | or return; | |
958 | ||
959 | require Module::Build::PodParser; | |
960 | my $parser = Module::Build::PodParser->new(fh => $fh); | |
961 | my $method = "get_$part"; | |
962 | return $p->{$member} = $parser->$method(); | |
963 | } | |
964 | ||
965 | sub version_from_file { # Method provided for backwards compatability | |
966 | return Module::Build::ModuleInfo->new_from_file($_[1])->version(); | |
967 | } | |
968 | ||
969 | sub find_module_by_name { # Method provided for backwards compatability | |
970 | return Module::Build::ModuleInfo->find_module_by_name(@_[1,2]); | |
971 | } | |
972 | ||
973 | sub add_to_cleanup { | |
974 | my $self = shift; | |
975 | my %files = map {$self->localize_file_path($_), 1} @_; | |
976 | $self->{phash}{cleanup}->write(\%files); | |
977 | } | |
978 | ||
979 | sub cleanup { | |
980 | my $self = shift; | |
981 | my $all = $self->{phash}{cleanup}->read; | |
982 | return keys %$all; | |
983 | } | |
984 | ||
985 | sub config_file { | |
986 | my $self = shift; | |
987 | return unless -d $self->config_dir; | |
988 | return File::Spec->catfile($self->config_dir, @_); | |
989 | } | |
990 | ||
991 | sub read_config { | |
992 | my ($self) = @_; | |
993 | ||
994 | my $file = $self->config_file('build_params') | |
995 | or die "No build_params?"; | |
996 | my $fh = IO::File->new($file) or die "Can't read '$file': $!"; | |
997 | my $ref = eval do {local $/; <$fh>}; | |
998 | die if $@; | |
999 | ($self->{args}, $self->{config}, $self->{properties}) = @$ref; | |
1000 | close $fh; | |
1001 | } | |
1002 | ||
1003 | sub has_config_data { | |
1004 | my $self = shift; | |
1005 | return scalar grep $self->{phash}{$_}->has_data(), qw(config_data features auto_features); | |
1006 | } | |
1007 | ||
1008 | sub _write_data { | |
1009 | my ($self, $filename, $data) = @_; | |
1010 | ||
1011 | my $file = $self->config_file($filename); | |
1012 | my $fh = IO::File->new("> $file") or die "Can't create '$file': $!"; | |
1013 | local $Data::Dumper::Terse = 1; | |
1014 | print $fh ref($data) ? Data::Dumper::Dumper($data) : $data; | |
1015 | } | |
1016 | ||
1017 | sub write_config { | |
1018 | my ($self) = @_; | |
1019 | ||
1020 | File::Path::mkpath($self->{properties}{config_dir}); | |
1021 | -d $self->{properties}{config_dir} or die "Can't mkdir $self->{properties}{config_dir}: $!"; | |
1022 | ||
1023 | my @items = @{ $self->prereq_action_types }; | |
1024 | $self->_write_data('prereqs', { map { $_, $self->$_() } @items }); | |
1025 | $self->_write_data('build_params', [$self->{args}, $self->{config}, $self->{properties}]); | |
1026 | ||
1027 | # Set a new magic number and write it to a file | |
1028 | $self->_write_data('magicnum', $self->magic_number(int rand 1_000_000)); | |
1029 | ||
1030 | $self->{phash}{$_}->write() foreach qw(notes cleanup features auto_features config_data runtime_params); | |
1031 | } | |
1032 | ||
1033 | sub check_autofeatures { | |
1034 | my ($self) = @_; | |
1035 | my $features = $self->auto_features; | |
1036 | ||
1037 | return unless %$features; | |
1038 | ||
1039 | $self->log_info("Checking features:\n"); | |
1040 | ||
1041 | my $max_name_len; | |
1042 | $max_name_len = ( length($_) > $max_name_len ) ? | |
1043 | length($_) : $max_name_len | |
1044 | for keys %$features; | |
1045 | ||
1046 | while (my ($name, $info) = each %$features) { | |
1047 | $self->log_info(" $name" . '.' x ($max_name_len - length($name) + 4)); | |
1048 | ||
1049 | if ( my $failures = $self->prereq_failures($info) ) { | |
1050 | my $disabled = grep( /^(?:\w+_)?(?:requires|conflicts)$/, | |
1051 | keys %$failures ) ? 1 : 0; | |
1052 | $self->log_info( $disabled ? "disabled\n" : "enabled\n" ); | |
1053 | ||
1054 | my $log_text; | |
1055 | while (my ($type, $prereqs) = each %$failures) { | |
1056 | while (my ($module, $status) = each %$prereqs) { | |
1057 | my $required = | |
1058 | ($type =~ /^(?:\w+_)?(?:requires|conflicts)$/) ? 1 : 0; | |
1059 | my $prefix = ($required) ? '-' : '*'; | |
1060 | $log_text .= " $prefix $status->{message}\n"; | |
1061 | } | |
1062 | } | |
1063 | $self->log_warn("$log_text") unless $self->quiet; | |
1064 | } else { | |
1065 | $self->log_info("enabled\n"); | |
1066 | } | |
1067 | } | |
1068 | ||
1069 | $self->log_warn("\n"); | |
1070 | } | |
1071 | ||
1072 | sub prereq_failures { | |
1073 | my ($self, $info) = @_; | |
1074 | ||
1075 | my @types = @{ $self->prereq_action_types }; | |
1076 | $info ||= {map {$_, $self->$_()} @types}; | |
1077 | ||
1078 | my $out; | |
1079 | ||
1080 | foreach my $type (@types) { | |
1081 | my $prereqs = $info->{$type}; | |
1082 | while ( my ($modname, $spec) = each %$prereqs ) { | |
1083 | my $status = $self->check_installed_status($modname, $spec); | |
1084 | ||
1085 | if ($type =~ /^(?:\w+_)?conflicts$/) { | |
1086 | next if !$status->{ok}; | |
1087 | $status->{conflicts} = delete $status->{need}; | |
1088 | $status->{message} = "$modname ($status->{have}) conflicts with this distribution"; | |
1089 | ||
1090 | } elsif ($type =~ /^(?:\w+_)?recommends$/) { | |
1091 | next if $status->{ok}; | |
1092 | $status->{message} = ($status->{have} eq '<none>' | |
1093 | ? "Optional prerequisite $modname is not installed" | |
1094 | : "$modname ($status->{have}) is installed, but we prefer to have $spec"); | |
1095 | } else { | |
1096 | next if $status->{ok}; | |
1097 | } | |
1098 | ||
1099 | $out->{$type}{$modname} = $status; | |
1100 | } | |
1101 | } | |
1102 | ||
1103 | return $out; | |
1104 | } | |
1105 | ||
1106 | # returns a hash of defined prerequisites; i.e. only prereq types with values | |
1107 | sub _enum_prereqs { | |
1108 | my $self = shift; | |
1109 | my %prereqs; | |
1110 | foreach my $type ( @{ $self->prereq_action_types } ) { | |
1111 | if ( $self->can( $type ) ) { | |
1112 | my $prereq = $self->$type() || {}; | |
1113 | $prereqs{$type} = $prereq if %$prereq; | |
1114 | } | |
1115 | } | |
1116 | return \%prereqs; | |
1117 | } | |
1118 | ||
1119 | sub check_prereq { | |
1120 | my $self = shift; | |
1121 | ||
1122 | # If we have XS files, make sure we can process them. | |
1123 | my $xs_files = $self->find_xs_files; | |
1124 | if (keys %$xs_files && !$self->_mb_feature('C_support')) { | |
1125 | $self->log_warn("Warning: this distribution contains XS files, ". | |
1126 | "but Module::Build is not configured with C_support"); | |
1127 | } | |
1128 | ||
1129 | # Check to see if there are any prereqs to check | |
1130 | my $info = $self->_enum_prereqs; | |
1131 | return 1 unless $info; | |
1132 | ||
1133 | $self->log_info("Checking prerequisites...\n"); | |
1134 | ||
1135 | my $failures = $self->prereq_failures($info); | |
1136 | ||
1137 | if ( $failures ) { | |
1138 | ||
1139 | while (my ($type, $prereqs) = each %$failures) { | |
1140 | while (my ($module, $status) = each %$prereqs) { | |
1141 | my $prefix = ($type =~ /^(?:\w+_)?recommends$/) ? '*' : '- ERROR:'; | |
1142 | $self->log_warn(" $prefix $status->{message}\n"); | |
1143 | } | |
1144 | } | |
1145 | ||
1146 | $self->log_warn(<<EOF); | |
1147 | ||
1148 | ERRORS/WARNINGS FOUND IN PREREQUISITES. You may wish to install the versions | |
1149 | of the modules indicated above before proceeding with this installation | |
1150 | ||
1151 | EOF | |
1152 | return 0; | |
1153 | ||
1154 | } else { | |
1155 | ||
1156 | $self->log_info("Looks good\n\n"); | |
1157 | return 1; | |
1158 | ||
1159 | } | |
1160 | } | |
1161 | ||
1162 | sub perl_version { | |
1163 | my ($self) = @_; | |
1164 | # Check the current perl interpreter | |
1165 | # It's much more convenient to use $] here than $^V, but 'man | |
1166 | # perlvar' says I'm not supposed to. Bloody tyrant. | |
1167 | return $^V ? $self->perl_version_to_float(sprintf "%vd", $^V) : $]; | |
1168 | } | |
1169 | ||
1170 | sub perl_version_to_float { | |
1171 | my ($self, $version) = @_; | |
1172 | $version =~ s/\./../; | |
1173 | $version =~ s/\.(\d+)/sprintf '%03d', $1/eg; | |
1174 | return $version; | |
1175 | } | |
1176 | ||
1177 | sub _parse_conditions { | |
1178 | my ($self, $spec) = @_; | |
1179 | ||
1180 | if ($spec =~ /^\s*([\w.]+)\s*$/) { # A plain number, maybe with dots, letters, and underscores | |
1181 | return (">= $spec"); | |
1182 | } else { | |
1183 | return split /\s*,\s*/, $spec; | |
1184 | } | |
1185 | } | |
1186 | ||
1187 | sub check_installed_status { | |
1188 | my ($self, $modname, $spec) = @_; | |
1189 | my %status = (need => $spec); | |
1190 | ||
1191 | if ($modname eq 'perl') { | |
1192 | $status{have} = $self->perl_version; | |
1193 | ||
1194 | } elsif (eval { no strict; $status{have} = ${"${modname}::VERSION"} }) { | |
1195 | # Don't try to load if it's already loaded | |
1196 | ||
1197 | } else { | |
1198 | my $pm_info = Module::Build::ModuleInfo->new_from_module( $modname ); | |
1199 | unless (defined( $pm_info )) { | |
1200 | @status{ qw(have message) } = ('<none>', "$modname is not installed"); | |
1201 | return \%status; | |
1202 | } | |
1203 | ||
1204 | $status{have} = $pm_info->version(); | |
1205 | if ($spec and !$status{have}) { | |
1206 | @status{ qw(have message) } = (undef, "Couldn't find a \$VERSION in prerequisite $modname"); | |
1207 | return \%status; | |
1208 | } | |
1209 | } | |
1210 | ||
1211 | my @conditions = $self->_parse_conditions($spec); | |
1212 | ||
1213 | foreach (@conditions) { | |
1214 | my ($op, $version) = /^\s* (<=?|>=?|==|!=) \s* ([\w.]+) \s*$/x | |
1215 | or die "Invalid prerequisite condition '$_' for $modname"; | |
1216 | ||
1217 | $version = $self->perl_version_to_float($version) | |
1218 | if $modname eq 'perl'; | |
1219 | ||
1220 | next if $op eq '>=' and !$version; # Module doesn't have to actually define a $VERSION | |
1221 | ||
1222 | unless ($self->compare_versions( $status{have}, $op, $version )) { | |
1223 | $status{message} = "$modname ($status{have}) is installed, but we need version $op $version"; | |
1224 | return \%status; | |
1225 | } | |
1226 | } | |
1227 | ||
1228 | $status{ok} = 1; | |
1229 | return \%status; | |
1230 | } | |
1231 | ||
1232 | sub compare_versions { | |
1233 | my $self = shift; | |
1234 | my ($v1, $op, $v2) = @_; | |
b3dfda33 SP |
1235 | $v1 = Module::Build::Version->new($v1) |
1236 | unless UNIVERSAL::isa($v1,'Module::Build::Version'); | |
bb4e9162 YST |
1237 | |
1238 | my $eval_str = "\$v1 $op \$v2"; | |
1239 | my $result = eval $eval_str; | |
1240 | $self->log_warn("error comparing versions: '$eval_str' $@") if $@; | |
1241 | ||
1242 | return $result; | |
1243 | } | |
1244 | ||
1245 | # I wish I could set $! to a string, but I can't, so I use $@ | |
1246 | sub check_installed_version { | |
1247 | my ($self, $modname, $spec) = @_; | |
1248 | ||
1249 | my $status = $self->check_installed_status($modname, $spec); | |
1250 | ||
1251 | if ($status->{ok}) { | |
1252 | return $status->{have} if $status->{have} and $status->{have} ne '<none>'; | |
1253 | return '0 but true'; | |
1254 | } | |
1255 | ||
1256 | $@ = $status->{message}; | |
1257 | return 0; | |
1258 | } | |
1259 | ||
1260 | sub make_executable { | |
1261 | # Perl's chmod() is mapped to useful things on various non-Unix | |
1262 | # platforms, so we use it in the base class even though it looks | |
1263 | # Unixish. | |
1264 | ||
1265 | my $self = shift; | |
1266 | foreach (@_) { | |
1267 | my $current_mode = (stat $_)[2]; | |
1268 | chmod $current_mode | 0111, $_; | |
1269 | } | |
1270 | } | |
1271 | ||
f943a5bf SP |
1272 | sub is_executable { |
1273 | # We assume this does the right thing on generic platforms, though | |
1274 | # we do some other more specific stuff on Unixish platforms. | |
1275 | my ($self, $file) = @_; | |
1276 | return -x $file; | |
1277 | } | |
1278 | ||
bb4e9162 YST |
1279 | sub _startperl { shift()->config('startperl') } |
1280 | ||
1281 | # Return any directories in @INC which are not in the default @INC for | |
1282 | # this perl. For example, stuff passed in with -I or loaded with "use lib". | |
1283 | sub _added_to_INC { | |
1284 | my $self = shift; | |
1285 | ||
1286 | my %seen; | |
1287 | $seen{$_}++ foreach $self->_default_INC; | |
1288 | return grep !$seen{$_}++, @INC; | |
1289 | } | |
1290 | ||
1291 | # Determine the default @INC for this Perl | |
1292 | { | |
1293 | my @default_inc; # Memoize | |
1294 | sub _default_INC { | |
1295 | my $self = shift; | |
1296 | return @default_inc if @default_inc; | |
1297 | ||
1298 | local $ENV{PERL5LIB}; # this is not considered part of the default. | |
1299 | ||
1300 | my $perl = ref($self) ? $self->perl : $self->find_perl_interpreter; | |
1301 | ||
a314697d | 1302 | my @inc = $self->_backticks($perl, '-le', 'print for @INC'); |
bb4e9162 YST |
1303 | chomp @inc; |
1304 | ||
1305 | return @default_inc = @inc; | |
1306 | } | |
1307 | } | |
1308 | ||
1309 | sub print_build_script { | |
1310 | my ($self, $fh) = @_; | |
1311 | ||
1312 | my $build_package = $self->build_class; | |
1313 | ||
1314 | my $closedata=""; | |
1315 | ||
1316 | my %q = map {$_, $self->$_()} qw(config_dir base_dir); | |
1317 | ||
1318 | my $case_tolerant = 0+(File::Spec->can('case_tolerant') | |
1319 | && File::Spec->case_tolerant); | |
1320 | $q{base_dir} = uc $q{base_dir} if $case_tolerant; | |
1321 | $q{base_dir} = Win32::GetShortPathName($q{base_dir}) if $^O eq 'MSWin32'; | |
1322 | ||
1323 | $q{magic_numfile} = $self->config_file('magicnum'); | |
1324 | ||
1325 | my @myINC = $self->_added_to_INC; | |
1326 | for (@myINC, values %q) { | |
1327 | $_ = File::Spec->canonpath( $_ ); | |
1328 | s/([\\\'])/\\$1/g; | |
1329 | } | |
1330 | ||
1331 | my $quoted_INC = join ",\n", map " '$_'", @myINC; | |
1332 | my $shebang = $self->_startperl; | |
1333 | my $magic_number = $self->magic_number; | |
1334 | ||
1335 | print $fh <<EOF; | |
1336 | $shebang | |
1337 | ||
1338 | use strict; | |
1339 | use Cwd; | |
1340 | use File::Basename; | |
1341 | use File::Spec; | |
1342 | ||
1343 | sub magic_number_matches { | |
1344 | return 0 unless -e '$q{magic_numfile}'; | |
1345 | local *FH; | |
1346 | open FH, '$q{magic_numfile}' or return 0; | |
1347 | my \$filenum = <FH>; | |
1348 | close FH; | |
1349 | return \$filenum == $magic_number; | |
1350 | } | |
1351 | ||
1352 | my \$progname; | |
1353 | my \$orig_dir; | |
1354 | BEGIN { | |
1355 | \$^W = 1; # Use warnings | |
1356 | \$progname = basename(\$0); | |
1357 | \$orig_dir = Cwd::cwd(); | |
1358 | my \$base_dir = '$q{base_dir}'; | |
1359 | if (!magic_number_matches()) { | |
1360 | unless (chdir(\$base_dir)) { | |
1361 | die ("Couldn't chdir(\$base_dir), aborting\\n"); | |
1362 | } | |
1363 | unless (magic_number_matches()) { | |
1364 | die ("Configuration seems to be out of date, please re-run 'perl Build.PL' again.\\n"); | |
1365 | } | |
1366 | } | |
1367 | unshift \@INC, | |
1368 | ( | |
1369 | $quoted_INC | |
1370 | ); | |
1371 | } | |
1372 | ||
1373 | close(*DATA) unless eof(*DATA); # ensure no open handles to this script | |
1374 | ||
1375 | use $build_package; | |
1376 | ||
1377 | # Some platforms have problems setting \$^X in shebang contexts, fix it up here | |
a314697d | 1378 | \$^X = Module::Build->find_perl_interpreter; |
bb4e9162 YST |
1379 | |
1380 | if (-e 'Build.PL' and not $build_package->up_to_date('Build.PL', \$progname)) { | |
1381 | warn "Warning: Build.PL has been altered. You may need to run 'perl Build.PL' again.\\n"; | |
1382 | } | |
1383 | ||
1384 | # This should have just enough arguments to be able to bootstrap the rest. | |
1385 | my \$build = $build_package->resume ( | |
1386 | properties => { | |
1387 | config_dir => '$q{config_dir}', | |
1388 | orig_dir => \$orig_dir, | |
1389 | }, | |
1390 | ); | |
1391 | ||
1392 | \$build->dispatch; | |
1393 | EOF | |
1394 | } | |
1395 | ||
1396 | sub create_build_script { | |
1397 | my ($self) = @_; | |
1398 | $self->write_config; | |
1399 | ||
1400 | my ($build_script, $dist_name, $dist_version) | |
1401 | = map $self->$_(), qw(build_script dist_name dist_version); | |
1402 | ||
1403 | if ( $self->delete_filetree($build_script) ) { | |
1404 | $self->log_info("Removed previous script '$build_script'\n\n"); | |
1405 | } | |
1406 | ||
1407 | $self->log_info("Creating new '$build_script' script for ", | |
1408 | "'$dist_name' version '$dist_version'\n"); | |
1409 | my $fh = IO::File->new(">$build_script") or die "Can't create '$build_script': $!"; | |
1410 | $self->print_build_script($fh); | |
1411 | close $fh; | |
1412 | ||
1413 | $self->make_executable($build_script); | |
1414 | ||
1415 | return 1; | |
1416 | } | |
1417 | ||
1418 | sub check_manifest { | |
1419 | my $self = shift; | |
1420 | return unless -e 'MANIFEST'; | |
1421 | ||
1422 | # Stolen nearly verbatim from MakeMaker. But ExtUtils::Manifest | |
1423 | # could easily be re-written into a modern Perl dialect. | |
1424 | ||
1425 | require ExtUtils::Manifest; # ExtUtils::Manifest is not warnings clean. | |
1426 | local ($^W, $ExtUtils::Manifest::Quiet) = (0,1); | |
1427 | ||
1428 | $self->log_info("Checking whether your kit is complete...\n"); | |
1429 | if (my @missed = ExtUtils::Manifest::manicheck()) { | |
1430 | $self->log_warn("WARNING: the following files are missing in your kit:\n", | |
1431 | "\t", join("\n\t", @missed), "\n", | |
1432 | "Please inform the author.\n\n"); | |
1433 | } else { | |
1434 | $self->log_info("Looks good\n\n"); | |
1435 | } | |
1436 | } | |
1437 | ||
1438 | sub dispatch { | |
1439 | my $self = shift; | |
1440 | local $self->{_completed_actions} = {}; | |
1441 | ||
1442 | if (@_) { | |
1443 | my ($action, %p) = @_; | |
1444 | my $args = $p{args} ? delete($p{args}) : {}; | |
1445 | ||
1446 | local $self->{invoked_action} = $action; | |
1447 | local $self->{args} = {%{$self->{args}}, %$args}; | |
1448 | local $self->{properties} = {%{$self->{properties}}, %p}; | |
1449 | return $self->_call_action($action); | |
1450 | } | |
1451 | ||
1452 | die "No build action specified" unless $self->{action}; | |
1453 | local $self->{invoked_action} = $self->{action}; | |
1454 | $self->_call_action($self->{action}); | |
1455 | } | |
1456 | ||
1457 | sub _call_action { | |
1458 | my ($self, $action) = @_; | |
1459 | ||
1460 | return if $self->{_completed_actions}{$action}++; | |
1461 | ||
1462 | local $self->{action} = $action; | |
1463 | my $method = "ACTION_$action"; | |
1464 | die "No action '$action' defined, try running the 'help' action.\n" unless $self->can($method); | |
1465 | return $self->$method(); | |
1466 | } | |
1467 | ||
1468 | sub cull_options { | |
1469 | my $self = shift; | |
1470 | my $specs = $self->get_options or return ({}, @_); | |
1471 | require Getopt::Long; | |
1472 | # XXX Should we let Getopt::Long handle M::B's options? That would | |
1473 | # be easy-ish to add to @specs right here, but wouldn't handle options | |
1474 | # passed without "--" as M::B currently allows. We might be able to | |
1475 | # get around this by setting the "prefix_pattern" Configure option. | |
1476 | my @specs; | |
1477 | my $args = {}; | |
1478 | # Construct the specifications for GetOptions. | |
1479 | while (my ($k, $v) = each %$specs) { | |
1480 | # Throw an error if specs conflict with our own. | |
1481 | die "Option specification '$k' conflicts with a " . ref $self | |
1482 | . " option of the same name" | |
1483 | if $self->valid_property($k); | |
1484 | push @specs, $k . (defined $v->{type} ? $v->{type} : ''); | |
1485 | push @specs, $v->{store} if exists $v->{store}; | |
1486 | $args->{$k} = $v->{default} if exists $v->{default}; | |
1487 | } | |
1488 | ||
1489 | local @ARGV = @_; # No other way to dupe Getopt::Long | |
1490 | ||
1491 | # Get the options values and return them. | |
1492 | # XXX Add option to allow users to set options? | |
1493 | if ( @specs ) { | |
1494 | Getopt::Long::Configure('pass_through'); | |
1495 | Getopt::Long::GetOptions($args, @specs); | |
1496 | } | |
1497 | ||
1498 | return $args, @ARGV; | |
1499 | } | |
1500 | ||
1501 | sub unparse_args { | |
1502 | my ($self, $args) = @_; | |
1503 | my @out; | |
1504 | while (my ($k, $v) = each %$args) { | |
1505 | push @out, (UNIVERSAL::isa($v, 'HASH') ? map {+"--$k", "$_=$v->{$_}"} keys %$v : | |
1506 | UNIVERSAL::isa($v, 'ARRAY') ? map {+"--$k", $_} @$v : | |
1507 | ("--$k", $v)); | |
1508 | } | |
1509 | return @out; | |
1510 | } | |
1511 | ||
1512 | sub args { | |
1513 | my $self = shift; | |
1514 | return wantarray ? %{ $self->{args} } : $self->{args} unless @_; | |
1515 | my $key = shift; | |
1516 | $self->{args}{$key} = shift if @_; | |
1517 | return $self->{args}{$key}; | |
1518 | } | |
1519 | ||
1520 | sub _translate_option { | |
1521 | my $self = shift; | |
1522 | my $opt = shift; | |
1523 | ||
1524 | (my $tr_opt = $opt) =~ tr/-/_/; | |
1525 | ||
1526 | return $tr_opt if grep $tr_opt =~ /^(?:no_?)?$_$/, qw( | |
1527 | create_makefile_pl | |
1528 | create_readme | |
1529 | extra_compiler_flags | |
1530 | extra_linker_flags | |
1531 | html_css | |
1532 | install_base | |
1533 | install_path | |
1534 | meta_add | |
1535 | meta_merge | |
1536 | test_files | |
1537 | use_rcfile | |
1538 | ); # normalize only selected option names | |
1539 | ||
1540 | return $opt; | |
1541 | } | |
1542 | ||
1543 | sub _read_arg { | |
1544 | my ($self, $args, $key, $val) = @_; | |
1545 | ||
1546 | $key = $self->_translate_option($key); | |
1547 | ||
1548 | if ( exists $args->{$key} ) { | |
1549 | $args->{$key} = [ $args->{$key} ] unless ref $args->{$key}; | |
1550 | push @{$args->{$key}}, $val; | |
1551 | } else { | |
1552 | $args->{$key} = $val; | |
1553 | } | |
1554 | } | |
1555 | ||
1556 | sub _optional_arg { | |
1557 | my $self = shift; | |
1558 | my $opt = shift; | |
1559 | my $argv = shift; | |
1560 | ||
1561 | $opt = $self->_translate_option($opt); | |
1562 | ||
1563 | my @bool_opts = qw( | |
1564 | build_bat | |
1565 | create_readme | |
1566 | pollute | |
1567 | quiet | |
1568 | uninst | |
1569 | use_rcfile | |
1570 | verbose | |
1571 | ); | |
1572 | ||
1573 | # inverted boolean options; eg --noverbose or --no-verbose | |
1574 | # converted to proper name & returned with false value (verbose, 0) | |
1575 | if ( grep $opt =~ /^no[-_]?$_$/, @bool_opts ) { | |
1576 | $opt =~ s/^no-?//; | |
1577 | return ($opt, 0); | |
1578 | } | |
1579 | ||
1580 | # non-boolean option; return option unchanged along with its argument | |
1581 | return ($opt, shift(@$argv)) unless grep $_ eq $opt, @bool_opts; | |
1582 | ||
1583 | # we're punting a bit here, if an option appears followed by a digit | |
1584 | # we take the digit as the argument for the option. If there is | |
1585 | # nothing that looks like a digit, we pretent the option is a flag | |
1586 | # that is being set and has no argument. | |
1587 | my $arg = 1; | |
1588 | $arg = shift(@$argv) if @$argv && $argv->[0] =~ /^\d+$/; | |
1589 | ||
1590 | return ($opt, $arg); | |
1591 | } | |
1592 | ||
1593 | sub read_args { | |
1594 | my $self = shift; | |
1595 | my ($action, @argv); | |
1596 | (my $args, @_) = $self->cull_options(@_); | |
1597 | my %args = %$args; | |
1598 | ||
1599 | my $opt_re = qr/[\w\-]+/; | |
1600 | ||
1601 | while (@_) { | |
1602 | local $_ = shift; | |
1603 | if ( /^(?:--)?($opt_re)=(.*)$/ ) { | |
1604 | $self->_read_arg(\%args, $1, $2); | |
1605 | } elsif ( /^--($opt_re)$/ ) { | |
1606 | my($opt, $arg) = $self->_optional_arg($1, \@_); | |
1607 | $self->_read_arg(\%args, $opt, $arg); | |
1608 | } elsif ( /^($opt_re)$/ and !defined($action)) { | |
1609 | $action = $1; | |
1610 | } else { | |
1611 | push @argv, $_; | |
1612 | } | |
1613 | } | |
1614 | $args{ARGV} = \@argv; | |
1615 | ||
1616 | # Hashify these parameters | |
1617 | for ($self->hash_properties) { | |
1618 | next unless exists $args{$_}; | |
1619 | my %hash; | |
1620 | $args{$_} ||= []; | |
1621 | $args{$_} = [ $args{$_} ] unless ref $args{$_}; | |
1622 | foreach my $arg ( @{$args{$_}} ) { | |
1623 | $arg =~ /(\w+)=(.*)/ | |
1624 | or die "Malformed '$_' argument: '$arg' should be something like 'foo=bar'"; | |
1625 | $hash{$1} = $2; | |
1626 | } | |
1627 | $args{$_} = \%hash; | |
1628 | } | |
1629 | ||
1630 | # De-tilde-ify any path parameters | |
1631 | for my $key (qw(prefix install_base destdir)) { | |
1632 | next if !defined $args{$key}; | |
1633 | $args{$key} = _detildefy($args{$key}); | |
1634 | } | |
1635 | ||
1636 | for my $key (qw(install_path)) { | |
1637 | next if !defined $args{$key}; | |
1638 | ||
1639 | for my $subkey (keys %{$args{$key}}) { | |
1640 | next if !defined $args{$key}{$subkey}; | |
1641 | my $subkey_ext = _detildefy($args{$key}{$subkey}); | |
1642 | if ( $subkey eq 'html' ) { # translate for compatability | |
1643 | $args{$key}{binhtml} = $subkey_ext; | |
1644 | $args{$key}{libhtml} = $subkey_ext; | |
1645 | } else { | |
1646 | $args{$key}{$subkey} = $subkey_ext; | |
1647 | } | |
1648 | } | |
1649 | } | |
1650 | ||
1651 | if ($args{makefile_env_macros}) { | |
1652 | require Module::Build::Compat; | |
1653 | %args = (%args, Module::Build::Compat->makefile_to_build_macros); | |
1654 | } | |
1655 | ||
1656 | return \%args, $action; | |
1657 | } | |
1658 | ||
1659 | ||
1660 | sub _detildefy { | |
1661 | my $arg = shift; | |
1662 | ||
1663 | my($new_arg) = glob($arg) if $arg =~ /^~/; | |
1664 | ||
1665 | return defined($new_arg) ? $new_arg : $arg; | |
1666 | } | |
1667 | ||
1668 | ||
1669 | # merge Module::Build argument lists that have already been parsed | |
1670 | # by read_args(). Takes two references to option hashes and merges | |
1671 | # the contents, giving priority to the first. | |
1672 | sub _merge_arglist { | |
1673 | my( $self, $opts1, $opts2 ) = @_; | |
1674 | ||
1675 | my %new_opts = %$opts1; | |
1676 | while (my ($key, $val) = each %$opts2) { | |
1677 | if ( exists( $opts1->{$key} ) ) { | |
1678 | if ( ref( $val ) eq 'HASH' ) { | |
1679 | while (my ($k, $v) = each %$val) { | |
1680 | $new_opts{$key}{$k} = $v unless exists( $opts1->{$key}{$k} ); | |
1681 | } | |
1682 | } | |
1683 | } else { | |
1684 | $new_opts{$key} = $val | |
1685 | } | |
1686 | } | |
1687 | ||
1688 | return %new_opts; | |
1689 | } | |
1690 | ||
dc8021d3 | 1691 | # Look for a home directory on various systems. |
bb4e9162 | 1692 | sub _home_dir { |
dc8021d3 SP |
1693 | my @home_dirs; |
1694 | push( @home_dirs, $ENV{HOME} ) if $ENV{HOME}; | |
1695 | ||
1696 | push( @home_dirs, File::Spec->catpath($ENV{HOMEDRIVE}, $ENV{HOMEPATH}, '') ) | |
1697 | if $ENV{HOMEDRIVE} && $ENV{HOMEPATH}; | |
1698 | ||
1699 | my @other_home_envs = qw( USERPROFILE APPDATA WINDIR SYS$LOGIN ); | |
1700 | push( @home_dirs, map $ENV{$_}, grep $ENV{$_}, @other_home_envs ); | |
1701 | ||
1702 | my @real_home_dirs = grep -d, @home_dirs; | |
1703 | ||
1704 | return wantarray ? @real_home_dirs : shift( @real_home_dirs ); | |
1705 | } | |
1706 | ||
1707 | sub _find_user_config { | |
1708 | my $self = shift; | |
1709 | my $file = shift; | |
1710 | foreach my $dir ( $self->_home_dir ) { | |
1711 | my $path = File::Spec->catfile( $dir, $file ); | |
1712 | return $path if -e $path; | |
bb4e9162 | 1713 | } |
dc8021d3 | 1714 | return undef; |
bb4e9162 YST |
1715 | } |
1716 | ||
1717 | # read ~/.modulebuildrc returning global options '*' and | |
1718 | # options specific to the currently executing $action. | |
1719 | sub read_modulebuildrc { | |
1720 | my( $self, $action ) = @_; | |
1721 | ||
1722 | return () unless $self->use_rcfile; | |
1723 | ||
1724 | my $modulebuildrc; | |
1725 | if ( exists($ENV{MODULEBUILDRC}) && $ENV{MODULEBUILDRC} eq 'NONE' ) { | |
1726 | return (); | |
1727 | } elsif ( exists($ENV{MODULEBUILDRC}) && -e $ENV{MODULEBUILDRC} ) { | |
1728 | $modulebuildrc = $ENV{MODULEBUILDRC}; | |
1729 | } elsif ( exists($ENV{MODULEBUILDRC}) ) { | |
1730 | $self->log_warn("WARNING: Can't find resource file " . | |
1731 | "'$ENV{MODULEBUILDRC}' defined in environment.\n" . | |
1732 | "No options loaded\n"); | |
1733 | return (); | |
1734 | } else { | |
dc8021d3 SP |
1735 | $modulebuildrc = $self->_find_user_config( '.modulebuildrc' ); |
1736 | return () unless $modulebuildrc; | |
bb4e9162 YST |
1737 | } |
1738 | ||
1739 | my $fh = IO::File->new( $modulebuildrc ) | |
1740 | or die "Can't open $modulebuildrc: $!"; | |
1741 | ||
1742 | my %options; my $buffer = ''; | |
1743 | while (defined( my $line = <$fh> )) { | |
1744 | chomp( $line ); | |
1745 | $line =~ s/#.*$//; | |
1746 | next unless length( $line ); | |
1747 | ||
1748 | if ( $line =~ /^\S/ ) { | |
1749 | if ( $buffer ) { | |
1750 | my( $action, $options ) = split( /\s+/, $buffer, 2 ); | |
1751 | $options{$action} .= $options . ' '; | |
1752 | $buffer = ''; | |
1753 | } | |
1754 | $buffer = $line; | |
1755 | } else { | |
1756 | $buffer .= $line; | |
1757 | } | |
1758 | } | |
1759 | ||
1760 | if ( $buffer ) { # anything left in $buffer ? | |
1761 | my( $action, $options ) = split( /\s+/, $buffer, 2 ); | |
1762 | $options{$action} .= $options . ' '; # merge if more than one line | |
1763 | } | |
1764 | ||
1765 | my ($global_opts) = | |
1766 | $self->read_args( $self->split_like_shell( $options{'*'} || '' ) ); | |
1767 | my ($action_opts) = | |
1768 | $self->read_args( $self->split_like_shell( $options{$action} || '' ) ); | |
1769 | ||
1770 | # specific $action options take priority over global options '*' | |
1771 | return $self->_merge_arglist( $action_opts, $global_opts ); | |
1772 | } | |
1773 | ||
1774 | # merge the relevant options in ~/.modulebuildrc into Module::Build's | |
1775 | # option list where they do not conflict with commandline options. | |
1776 | sub merge_modulebuildrc { | |
1777 | my( $self, $action, %cmdline_opts ) = @_; | |
1778 | my %rc_opts = $self->read_modulebuildrc( $action || $self->{action} || 'build' ); | |
1779 | my %new_opts = $self->_merge_arglist( \%cmdline_opts, \%rc_opts ); | |
1780 | $self->merge_args( $action, %new_opts ); | |
1781 | } | |
1782 | ||
1783 | sub merge_args { | |
1784 | my ($self, $action, %args) = @_; | |
1785 | $self->{action} = $action if defined $action; | |
1786 | ||
1787 | my %additive = map { $_ => 1 } $self->hash_properties; | |
1788 | ||
1789 | # Extract our 'properties' from $cmd_args, the rest are put in 'args'. | |
1790 | while (my ($key, $val) = each %args) { | |
1791 | $self->{phash}{runtime_params}->access( $key => $val ) | |
1792 | if $self->valid_property($key); | |
1793 | my $add_to = ( $key eq 'config' ? $self->{config} | |
1794 | : $additive{$key} ? $self->{properties}{$key} | |
1795 | : $self->valid_property($key) ? $self->{properties} | |
1796 | : $self->{args}); | |
1797 | ||
1798 | if ($additive{$key}) { | |
1799 | $add_to->{$_} = $val->{$_} foreach keys %$val; | |
1800 | } else { | |
1801 | $add_to->{$key} = $val; | |
1802 | } | |
1803 | } | |
1804 | } | |
1805 | ||
1806 | sub cull_args { | |
1807 | my $self = shift; | |
1808 | my ($args, $action) = $self->read_args(@_); | |
1809 | $self->merge_args($action, %$args); | |
1810 | $self->merge_modulebuildrc( $action, %$args ); | |
1811 | } | |
1812 | ||
1813 | sub super_classes { | |
1814 | my ($self, $class, $seen) = @_; | |
1815 | $class ||= ref($self) || $self; | |
1816 | $seen ||= {}; | |
1817 | ||
1818 | no strict 'refs'; | |
1819 | my @super = grep {not $seen->{$_}++} $class, @{ $class . '::ISA' }; | |
1820 | return @super, map {$self->super_classes($_,$seen)} @super; | |
1821 | } | |
1822 | ||
1823 | sub known_actions { | |
1824 | my ($self) = @_; | |
1825 | ||
1826 | my %actions; | |
1827 | no strict 'refs'; | |
1828 | ||
1829 | foreach my $class ($self->super_classes) { | |
1830 | foreach ( keys %{ $class . '::' } ) { | |
1831 | $actions{$1}++ if /^ACTION_(\w+)/; | |
1832 | } | |
1833 | } | |
1834 | ||
1835 | return wantarray ? sort keys %actions : \%actions; | |
1836 | } | |
1837 | ||
1838 | sub get_action_docs { | |
1839 | my ($self, $action, $actions) = @_; | |
1840 | $actions ||= $self->known_actions; | |
1841 | $@ = ''; | |
1842 | ($@ = "No known action '$action'\n"), return | |
1843 | unless $actions->{$action}; | |
1844 | ||
1845 | my ($files_found, @docs) = (0); | |
1846 | foreach my $class ($self->super_classes) { | |
1847 | (my $file = $class) =~ s{::}{/}g; | |
1848 | $file = $INC{$file . '.pm'} or next; | |
1849 | my $fh = IO::File->new("< $file") or next; | |
1850 | $files_found++; | |
1851 | ||
1852 | # Code below modified from /usr/bin/perldoc | |
1853 | ||
1854 | # Skip to ACTIONS section | |
1855 | local $_; | |
1856 | while (<$fh>) { | |
1857 | last if /^=head1 ACTIONS\s/; | |
1858 | } | |
1859 | ||
1860 | # Look for our action | |
1861 | my ($found, $inlist) = (0, 0); | |
1862 | while (<$fh>) { | |
1863 | if (/^=item\s+\Q$action\E\b/) { | |
1864 | $found = 1; | |
1865 | } elsif (/^=(item|back)/) { | |
1866 | last if $found > 1 and not $inlist; | |
1867 | } | |
1868 | next unless $found; | |
1869 | push @docs, $_; | |
1870 | ++$inlist if /^=over/; | |
1871 | --$inlist if /^=back/; | |
1872 | ++$found if /^\w/; # Found descriptive text | |
1873 | } | |
1874 | } | |
1875 | ||
1876 | unless ($files_found) { | |
1877 | $@ = "Couldn't find any documentation to search"; | |
1878 | return; | |
1879 | } | |
1880 | unless (@docs) { | |
1881 | $@ = "Couldn't find any docs for action '$action'"; | |
1882 | return; | |
1883 | } | |
1884 | ||
1885 | return join '', @docs; | |
1886 | } | |
1887 | ||
1888 | sub ACTION_prereq_report { | |
1889 | my $self = shift; | |
1890 | $self->log_info( $self->prereq_report ); | |
1891 | } | |
1892 | ||
1893 | sub prereq_report { | |
1894 | my $self = shift; | |
1895 | my @types = @{ $self->prereq_action_types }; | |
1896 | my $info = { map { $_ => $self->$_() } @types }; | |
1897 | ||
1898 | my $output = ''; | |
1899 | foreach my $type (@types) { | |
1900 | my $prereqs = $info->{$type}; | |
1901 | next unless %$prereqs; | |
1902 | $output .= "\n$type:\n"; | |
1903 | my $mod_len = 2; | |
1904 | my $ver_len = 4; | |
1905 | my %mods; | |
1906 | while ( my ($modname, $spec) = each %$prereqs ) { | |
1907 | my $len = length $modname; | |
1908 | $mod_len = $len if $len > $mod_len; | |
1909 | $spec ||= '0'; | |
1910 | $len = length $spec; | |
1911 | $ver_len = $len if $len > $ver_len; | |
1912 | ||
1913 | my $mod = $self->check_installed_status($modname, $spec); | |
1914 | $mod->{name} = $modname; | |
1915 | $mod->{ok} ||= 0; | |
1916 | $mod->{ok} = ! $mod->{ok} if $type =~ /^(\w+_)?conflicts$/; | |
1917 | ||
1918 | $mods{lc $modname} = $mod; | |
1919 | } | |
1920 | ||
1921 | my $space = q{ } x ($mod_len - 3); | |
1922 | my $vspace = q{ } x ($ver_len - 3); | |
1923 | my $sline = q{-} x ($mod_len - 3); | |
1924 | my $vline = q{-} x ($ver_len - 3); | |
1925 | my $disposition = ($type =~ /^(\w+_)?conflicts$/) ? | |
1926 | 'Clash' : 'Need'; | |
1927 | $output .= | |
1928 | " Module $space $disposition $vspace Have\n". | |
1929 | " ------$sline+------$vline-+----------\n"; | |
1930 | ||
1931 | ||
1932 | for my $k (sort keys %mods) { | |
1933 | my $mod = $mods{$k}; | |
1934 | my $space = q{ } x ($mod_len - length $k); | |
1935 | my $vspace = q{ } x ($ver_len - length $mod->{need}); | |
1936 | my $f = $mod->{ok} ? ' ' : '!'; | |
1937 | $output .= | |
1938 | " $f $mod->{name} $space $mod->{need} $vspace $mod->{have}\n"; | |
1939 | } | |
1940 | } | |
1941 | return $output; | |
1942 | } | |
1943 | ||
1944 | sub ACTION_help { | |
1945 | my ($self) = @_; | |
1946 | my $actions = $self->known_actions; | |
1947 | ||
1948 | if (@{$self->{args}{ARGV}}) { | |
1949 | my $msg = $self->get_action_docs($self->{args}{ARGV}[0], $actions) || "$@\n"; | |
1950 | print $msg; | |
1951 | return; | |
1952 | } | |
1953 | ||
1954 | print <<EOF; | |
1955 | ||
1956 | Usage: $0 <action> arg1=value arg2=value ... | |
1957 | Example: $0 test verbose=1 | |
1958 | ||
1959 | Actions defined: | |
1960 | EOF | |
1961 | ||
1962 | print $self->_action_listing($actions); | |
1963 | ||
1964 | print "\nRun `Build help <action>` for details on an individual action.\n"; | |
1965 | print "See `perldoc Module::Build` for complete documentation.\n"; | |
1966 | } | |
1967 | ||
1968 | sub _action_listing { | |
1969 | my ($self, $actions) = @_; | |
1970 | ||
1971 | # Flow down columns, not across rows | |
1972 | my @actions = sort keys %$actions; | |
1973 | @actions = map $actions[($_ + ($_ % 2) * @actions) / 2], 0..$#actions; | |
1974 | ||
1975 | my $out = ''; | |
1976 | while (my ($one, $two) = splice @actions, 0, 2) { | |
1977 | $out .= sprintf(" %-12s %-12s\n", $one, $two||''); | |
1978 | } | |
1979 | return $out; | |
1980 | } | |
1981 | ||
1982 | sub ACTION_test { | |
1983 | my ($self) = @_; | |
1984 | my $p = $self->{properties}; | |
1985 | require Test::Harness; | |
1986 | ||
1987 | $self->depends_on('code'); | |
1988 | ||
1989 | # Do everything in our power to work with all versions of Test::Harness | |
1990 | my @harness_switches = $p->{debugger} ? qw(-w -d) : (); | |
1991 | local $Test::Harness::switches = join ' ', grep defined, $Test::Harness::switches, @harness_switches; | |
1992 | local $Test::Harness::Switches = join ' ', grep defined, $Test::Harness::Switches, @harness_switches; | |
1993 | local $ENV{HARNESS_PERL_SWITCHES} = join ' ', grep defined, $ENV{HARNESS_PERL_SWITCHES}, @harness_switches; | |
1994 | ||
1995 | $Test::Harness::switches = undef unless length $Test::Harness::switches; | |
1996 | $Test::Harness::Switches = undef unless length $Test::Harness::Switches; | |
1997 | delete $ENV{HARNESS_PERL_SWITCHES} unless length $ENV{HARNESS_PERL_SWITCHES}; | |
1998 | ||
1999 | local ($Test::Harness::verbose, | |
2000 | $Test::Harness::Verbose, | |
2001 | $ENV{TEST_VERBOSE}, | |
2002 | $ENV{HARNESS_VERBOSE}) = ($p->{verbose} || 0) x 4; | |
2003 | ||
2004 | # Make sure we test the module in blib/ | |
2005 | local @INC = (File::Spec->catdir($p->{base_dir}, $self->blib, 'lib'), | |
2006 | File::Spec->catdir($p->{base_dir}, $self->blib, 'arch'), | |
2007 | @INC); | |
2008 | ||
2009 | # Filter out nonsensical @INC entries - some versions of | |
2010 | # Test::Harness will really explode the number of entries here | |
2011 | @INC = grep {ref() || -d} @INC if @INC > 100; | |
2012 | ||
2013 | my $tests = $self->find_test_files; | |
2014 | ||
2015 | if (@$tests) { | |
2016 | # Work around a Test::Harness bug that loses the particular perl | |
2017 | # we're running under. $self->perl is trustworthy, but $^X isn't. | |
2018 | local $^X = $self->perl; | |
2019 | Test::Harness::runtests(@$tests); | |
2020 | } else { | |
2021 | $self->log_info("No tests defined.\n"); | |
2022 | } | |
2023 | ||
2024 | # This will get run and the user will see the output. It doesn't | |
2025 | # emit Test::Harness-style output. | |
2026 | if (-e 'visual.pl') { | |
2027 | $self->run_perl_script('visual.pl', '-Mblib='.$self->blib); | |
2028 | } | |
2029 | } | |
2030 | ||
2031 | sub test_files { | |
2032 | my $self = shift; | |
2033 | my $p = $self->{properties}; | |
2034 | if (@_) { | |
2035 | return $p->{test_files} = (@_ == 1 ? shift : [@_]); | |
2036 | } | |
2037 | return $self->find_test_files; | |
2038 | } | |
2039 | ||
2040 | sub expand_test_dir { | |
2041 | my ($self, $dir) = @_; | |
2042 | return sort @{$self->rscan_dir($dir, qr{^[^.].*\.t$})} if $self->recursive_test_files; | |
2043 | return sort glob File::Spec->catfile($dir, "*.t"); | |
2044 | } | |
2045 | ||
2046 | sub ACTION_testdb { | |
2047 | my ($self) = @_; | |
2048 | local $self->{properties}{debugger} = 1; | |
2049 | $self->depends_on('test'); | |
2050 | } | |
2051 | ||
2052 | sub ACTION_testcover { | |
2053 | my ($self) = @_; | |
2054 | ||
2055 | unless (Module::Build::ModuleInfo->find_module_by_name('Devel::Cover')) { | |
2056 | warn("Cannot run testcover action unless Devel::Cover is installed.\n"); | |
2057 | return; | |
2058 | } | |
2059 | ||
2060 | $self->add_to_cleanup('coverage', 'cover_db'); | |
2061 | $self->depends_on('code'); | |
2062 | ||
2063 | # See whether any of the *.pm files have changed since last time | |
2064 | # testcover was run. If so, start over. | |
2065 | if (-e 'cover_db') { | |
2066 | my $pm_files = $self->rscan_dir(File::Spec->catdir($self->blib, 'lib'), qr{\.pm$} ); | |
2067 | my $cover_files = $self->rscan_dir('cover_db', sub {-f $_ and not /\.html$/}); | |
2068 | ||
2069 | $self->do_system(qw(cover -delete)) | |
2070 | unless $self->up_to_date($pm_files, $cover_files); | |
2071 | } | |
2072 | ||
2073 | local $Test::Harness::switches = | |
2074 | local $Test::Harness::Switches = | |
2075 | local $ENV{HARNESS_PERL_SWITCHES} = "-MDevel::Cover"; | |
2076 | ||
2077 | $self->depends_on('test'); | |
2078 | $self->do_system('cover'); | |
2079 | } | |
2080 | ||
2081 | sub ACTION_code { | |
2082 | my ($self) = @_; | |
2083 | ||
2084 | # All installable stuff gets created in blib/ . | |
2085 | # Create blib/arch to keep blib.pm happy | |
2086 | my $blib = $self->blib; | |
2087 | $self->add_to_cleanup($blib); | |
2088 | File::Path::mkpath( File::Spec->catdir($blib, 'arch') ); | |
2089 | ||
2090 | if (my $split = $self->autosplit) { | |
2091 | $self->autosplit_file($_, $blib) for ref($split) ? @$split : ($split); | |
2092 | } | |
2093 | ||
2094 | foreach my $element (@{$self->build_elements}) { | |
2095 | my $method = "process_${element}_files"; | |
2096 | $method = "process_files_by_extension" unless $self->can($method); | |
2097 | $self->$method($element); | |
2098 | } | |
2099 | ||
2100 | $self->depends_on('config_data'); | |
2101 | } | |
2102 | ||
2103 | sub ACTION_build { | |
2104 | my $self = shift; | |
2105 | $self->depends_on('code'); | |
2106 | $self->depends_on('docs'); | |
2107 | } | |
2108 | ||
2109 | sub process_files_by_extension { | |
2110 | my ($self, $ext) = @_; | |
2111 | ||
2112 | my $method = "find_${ext}_files"; | |
2113 | my $files = $self->can($method) ? $self->$method() : $self->_find_file_by_type($ext, 'lib'); | |
2114 | ||
2115 | while (my ($file, $dest) = each %$files) { | |
2116 | $self->copy_if_modified(from => $file, to => File::Spec->catfile($self->blib, $dest) ); | |
2117 | } | |
2118 | } | |
2119 | ||
2120 | sub process_support_files { | |
2121 | my $self = shift; | |
2122 | my $p = $self->{properties}; | |
2123 | return unless $p->{c_source}; | |
2124 | ||
2125 | push @{$p->{include_dirs}}, $p->{c_source}; | |
2126 | ||
2127 | my $files = $self->rscan_dir($p->{c_source}, qr{\.c(pp)?$}); | |
2128 | foreach my $file (@$files) { | |
2129 | push @{$p->{objects}}, $self->compile_c($file); | |
2130 | } | |
2131 | } | |
2132 | ||
2133 | sub process_PL_files { | |
2134 | my ($self) = @_; | |
2135 | my $files = $self->find_PL_files; | |
2136 | ||
2137 | while (my ($file, $to) = each %$files) { | |
2138 | unless ($self->up_to_date( $file, $to )) { | |
2139 | $self->run_perl_script($file, [], [@$to]); | |
2140 | $self->add_to_cleanup(@$to); | |
2141 | } | |
2142 | } | |
2143 | } | |
2144 | ||
2145 | sub process_xs_files { | |
2146 | my $self = shift; | |
2147 | my $files = $self->find_xs_files; | |
2148 | while (my ($from, $to) = each %$files) { | |
2149 | unless ($from eq $to) { | |
2150 | $self->add_to_cleanup($to); | |
2151 | $self->copy_if_modified( from => $from, to => $to ); | |
2152 | } | |
2153 | $self->process_xs($to); | |
2154 | } | |
2155 | } | |
2156 | ||
2157 | sub process_pod_files { shift()->process_files_by_extension(shift()) } | |
2158 | sub process_pm_files { shift()->process_files_by_extension(shift()) } | |
2159 | ||
2160 | sub process_script_files { | |
2161 | my $self = shift; | |
2162 | my $files = $self->find_script_files; | |
2163 | return unless keys %$files; | |
2164 | ||
2165 | my $script_dir = File::Spec->catdir($self->blib, 'script'); | |
2166 | File::Path::mkpath( $script_dir ); | |
2167 | ||
2168 | foreach my $file (keys %$files) { | |
2169 | my $result = $self->copy_if_modified($file, $script_dir, 'flatten') or next; | |
a314697d | 2170 | $self->fix_shebang_line($result) unless $self->os_type eq 'VMS'; |
bb4e9162 YST |
2171 | $self->make_executable($result); |
2172 | } | |
2173 | } | |
2174 | ||
2175 | sub find_PL_files { | |
2176 | my $self = shift; | |
2177 | if (my $files = $self->{properties}{PL_files}) { | |
2178 | # 'PL_files' is given as a Unix file spec, so we localize_file_path(). | |
2179 | ||
2180 | if (UNIVERSAL::isa($files, 'ARRAY')) { | |
2181 | return { map {$_, [/^(.*)\.PL$/]} | |
2182 | map $self->localize_file_path($_), | |
2183 | @$files }; | |
2184 | ||
2185 | } elsif (UNIVERSAL::isa($files, 'HASH')) { | |
2186 | my %out; | |
2187 | while (my ($file, $to) = each %$files) { | |
2188 | $out{ $self->localize_file_path($file) } = [ map $self->localize_file_path($_), | |
2189 | ref $to ? @$to : ($to) ]; | |
2190 | } | |
2191 | return \%out; | |
2192 | ||
2193 | } else { | |
2194 | die "'PL_files' must be a hash reference or array reference"; | |
2195 | } | |
2196 | } | |
2197 | ||
2198 | return unless -d 'lib'; | |
2199 | return { map {$_, [/^(.*)\.PL$/]} @{ $self->rscan_dir('lib', qr{\.PL$}) } }; | |
2200 | } | |
2201 | ||
2202 | sub find_pm_files { shift->_find_file_by_type('pm', 'lib') } | |
2203 | sub find_pod_files { shift->_find_file_by_type('pod', 'lib') } | |
2204 | sub find_xs_files { shift->_find_file_by_type('xs', 'lib') } | |
2205 | ||
2206 | sub find_script_files { | |
2207 | my $self = shift; | |
2208 | if (my $files = $self->script_files) { | |
2209 | # Always given as a Unix file spec. Values in the hash are | |
2210 | # meaningless, but we preserve if present. | |
2211 | return { map {$self->localize_file_path($_), $files->{$_}} keys %$files }; | |
2212 | } | |
2213 | ||
2214 | # No default location for script files | |
2215 | return {}; | |
2216 | } | |
2217 | ||
2218 | sub find_test_files { | |
2219 | my $self = shift; | |
2220 | my $p = $self->{properties}; | |
2221 | ||
2222 | if (my $files = $p->{test_files}) { | |
2223 | $files = [keys %$files] if UNIVERSAL::isa($files, 'HASH'); | |
2224 | $files = [map { -d $_ ? $self->expand_test_dir($_) : $_ } | |
2225 | map glob, | |
2226 | $self->split_like_shell($files)]; | |
2227 | ||
2228 | # Always given as a Unix file spec. | |
2229 | return [ map $self->localize_file_path($_), @$files ]; | |
2230 | ||
2231 | } else { | |
2232 | # Find all possible tests in t/ or test.pl | |
2233 | my @tests; | |
2234 | push @tests, 'test.pl' if -e 'test.pl'; | |
2235 | push @tests, $self->expand_test_dir('t') if -e 't' and -d _; | |
2236 | return \@tests; | |
2237 | } | |
2238 | } | |
2239 | ||
2240 | sub _find_file_by_type { | |
2241 | my ($self, $type, $dir) = @_; | |
2242 | ||
2243 | if (my $files = $self->{properties}{"${type}_files"}) { | |
2244 | # Always given as a Unix file spec | |
2245 | return { map $self->localize_file_path($_), %$files }; | |
2246 | } | |
2247 | ||
2248 | return {} unless -d $dir; | |
2249 | return { map {$_, $_} | |
2250 | map $self->localize_file_path($_), | |
2251 | grep !/\.\#/, | |
2252 | @{ $self->rscan_dir($dir, qr{\.$type$}) } }; | |
2253 | } | |
2254 | ||
2255 | sub localize_file_path { | |
2256 | my ($self, $path) = @_; | |
a314697d | 2257 | $path =~ s/\.\z// if $self->os_type eq 'VMS'; |
bb4e9162 YST |
2258 | return File::Spec->catfile( split m{/}, $path ); |
2259 | } | |
2260 | ||
2261 | sub localize_dir_path { | |
2262 | my ($self, $path) = @_; | |
2263 | return File::Spec->catdir( split m{/}, $path ); | |
2264 | } | |
2265 | ||
2266 | sub fix_shebang_line { # Adapted from fixin() in ExtUtils::MM_Unix 1.35 | |
2267 | my ($self, @files) = @_; | |
2268 | my $c = $self->config; | |
2269 | ||
2270 | my ($does_shbang) = $c->{sharpbang} =~ /^\s*\#\!/; | |
2271 | for my $file (@files) { | |
2272 | my $FIXIN = IO::File->new($file) or die "Can't process '$file': $!"; | |
2273 | local $/ = "\n"; | |
2274 | chomp(my $line = <$FIXIN>); | |
2275 | next unless $line =~ s/^\s*\#!\s*//; # Not a shbang file. | |
2276 | ||
2277 | my ($cmd, $arg) = (split(' ', $line, 2), ''); | |
2278 | next unless $cmd =~ /perl/i; | |
2279 | my $interpreter = $self->{properties}{perl}; | |
2280 | ||
2281 | $self->log_verbose("Changing sharpbang in $file to $interpreter"); | |
2282 | my $shb = ''; | |
2283 | $shb .= "$c->{sharpbang}$interpreter $arg\n" if $does_shbang; | |
2284 | ||
2285 | # I'm not smart enough to know the ramifications of changing the | |
2286 | # embedded newlines here to \n, so I leave 'em in. | |
2287 | $shb .= qq{ | |
2288 | eval 'exec $interpreter $arg -S \$0 \${1+"\$\@"}' | |
2289 | if 0; # not running under some shell | |
2290 | } unless $self->os_type eq 'Windows'; # this won't work on win32, so don't | |
2291 | ||
2292 | my $FIXOUT = IO::File->new(">$file.new") | |
2293 | or die "Can't create new $file: $!\n"; | |
2294 | ||
2295 | # Print out the new #! line (or equivalent). | |
2296 | local $\; | |
2297 | undef $/; # Was localized above | |
2298 | print $FIXOUT $shb, <$FIXIN>; | |
2299 | close $FIXIN; | |
2300 | close $FIXOUT; | |
2301 | ||
2302 | rename($file, "$file.bak") | |
2303 | or die "Can't rename $file to $file.bak: $!"; | |
2304 | ||
2305 | rename("$file.new", $file) | |
2306 | or die "Can't rename $file.new to $file: $!"; | |
2307 | ||
2308 | unlink "$file.bak" | |
2309 | or $self->log_warn("Couldn't clean up $file.bak, leaving it there"); | |
2310 | ||
2311 | $self->do_system($c->{eunicefix}, $file) if $c->{eunicefix} ne ':'; | |
2312 | } | |
2313 | } | |
2314 | ||
2315 | ||
2316 | sub ACTION_testpod { | |
2317 | my $self = shift; | |
2318 | $self->depends_on('docs'); | |
2319 | ||
2320 | eval q{use Test::Pod 0.95; 1} | |
2321 | or die "The 'testpod' action requires Test::Pod version 0.95"; | |
2322 | ||
2323 | my @files = sort keys %{$self->_find_pods($self->libdoc_dirs)}, | |
2324 | keys %{$self->_find_pods($self->bindoc_dirs, exclude => [ qr/\.bat$/ ])} | |
2325 | or die "Couldn't find any POD files to test\n"; | |
2326 | ||
2327 | { package Module::Build::PodTester; # Don't want to pollute the main namespace | |
2328 | Test::Pod->import( tests => scalar @files ); | |
2329 | pod_file_ok($_) foreach @files; | |
2330 | } | |
2331 | } | |
2332 | ||
47f13fd5 SP |
2333 | sub ACTION_testpodcoverage { |
2334 | my $self = shift; | |
2335 | ||
2336 | $self->depends_on('docs'); | |
2337 | ||
2338 | eval q{use Test::Pod::Coverage 1.00; 1} | |
2339 | or die "The 'testpodcoverage' action requires ", | |
2340 | "Test::Pod::Coverage version 1.00"; | |
2341 | ||
2342 | all_pod_coverage_ok(); | |
2343 | } | |
2344 | ||
bb4e9162 YST |
2345 | sub ACTION_docs { |
2346 | my $self = shift; | |
2347 | ||
2348 | $self->depends_on('code'); | |
2349 | $self->depends_on('manpages', 'html'); | |
2350 | } | |
2351 | ||
2352 | # Given a file type, will return true if the file type would normally | |
2353 | # be installed when neither install-base nor prefix has been set. | |
2354 | # I.e. it will be true only if the path is set from Config.pm or | |
2355 | # set explicitly by the user via install-path. | |
2356 | sub _is_default_installable { | |
2357 | my $self = shift; | |
2358 | my $type = shift; | |
2359 | return ( $self->install_destination($type) && | |
2360 | ( $self->install_path($type) || | |
2361 | $self->install_sets($self->installdirs)->{$type} ) | |
2362 | ) ? 1 : 0; | |
2363 | } | |
2364 | ||
2365 | sub ACTION_manpages { | |
2366 | my $self = shift; | |
2367 | ||
2368 | return unless $self->_mb_feature('manpage_support'); | |
2369 | ||
2370 | $self->depends_on('code'); | |
2371 | ||
2372 | foreach my $type ( qw(bin lib) ) { | |
2373 | my $files = $self->_find_pods( $self->{properties}{"${type}doc_dirs"}, | |
2374 | exclude => [ qr/\.bat$/ ] ); | |
2375 | next unless %$files; | |
2376 | ||
2377 | my $sub = $self->can("manify_${type}_pods"); | |
2378 | next unless defined( $sub ); | |
2379 | ||
2380 | if ( $self->invoked_action eq 'manpages' ) { | |
2381 | $self->$sub(); | |
2382 | } elsif ( $self->_is_default_installable("${type}doc") ) { | |
2383 | $self->$sub(); | |
2384 | } | |
2385 | } | |
2386 | ||
2387 | } | |
2388 | ||
2389 | sub manify_bin_pods { | |
2390 | my $self = shift; | |
2391 | ||
2392 | my $files = $self->_find_pods( $self->{properties}{bindoc_dirs}, | |
2393 | exclude => [ qr/\.bat$/ ] ); | |
2394 | return unless keys %$files; | |
2395 | ||
2396 | my $mandir = File::Spec->catdir( $self->blib, 'bindoc' ); | |
2397 | File::Path::mkpath( $mandir, 0, 0777 ); | |
2398 | ||
2399 | require Pod::Man; | |
2400 | foreach my $file (keys %$files) { | |
2401 | # Pod::Simple based parsers only support one document per instance. | |
2402 | # This is expected to change in a future version (Pod::Simple > 3.03). | |
2403 | my $parser = Pod::Man->new( section => 1 ); # binaries go in section 1 | |
2404 | my $manpage = $self->man1page_name( $file ) . '.' . | |
2405 | $self->config( 'man1ext' ); | |
2406 | my $outfile = File::Spec->catfile($mandir, $manpage); | |
2407 | next if $self->up_to_date( $file, $outfile ); | |
2408 | $self->log_info("Manifying $file -> $outfile\n"); | |
2409 | $parser->parse_from_file( $file, $outfile ); | |
2410 | $files->{$file} = $outfile; | |
2411 | } | |
2412 | } | |
2413 | ||
2414 | sub manify_lib_pods { | |
2415 | my $self = shift; | |
2416 | ||
2417 | my $files = $self->_find_pods($self->{properties}{libdoc_dirs}); | |
2418 | return unless keys %$files; | |
2419 | ||
2420 | my $mandir = File::Spec->catdir( $self->blib, 'libdoc' ); | |
2421 | File::Path::mkpath( $mandir, 0, 0777 ); | |
2422 | ||
2423 | require Pod::Man; | |
2424 | while (my ($file, $relfile) = each %$files) { | |
2425 | # Pod::Simple based parsers only support one document per instance. | |
2426 | # This is expected to change in a future version (Pod::Simple > 3.03). | |
2427 | my $parser = Pod::Man->new( section => 3 ); # libraries go in section 3 | |
2428 | my $manpage = $self->man3page_name( $relfile ) . '.' . | |
2429 | $self->config( 'man3ext' ); | |
2430 | my $outfile = File::Spec->catfile( $mandir, $manpage); | |
2431 | next if $self->up_to_date( $file, $outfile ); | |
2432 | $self->log_info("Manifying $file -> $outfile\n"); | |
2433 | $parser->parse_from_file( $file, $outfile ); | |
2434 | $files->{$file} = $outfile; | |
2435 | } | |
2436 | } | |
2437 | ||
2438 | sub _find_pods { | |
2439 | my ($self, $dirs, %args) = @_; | |
2440 | my %files; | |
2441 | foreach my $spec (@$dirs) { | |
2442 | my $dir = $self->localize_dir_path($spec); | |
2443 | next unless -e $dir; | |
b3dfda33 | 2444 | |
bb4e9162 YST |
2445 | FILE: foreach my $file ( @{ $self->rscan_dir( $dir ) } ) { |
2446 | foreach my $regexp ( @{ $args{exclude} } ) { | |
2447 | next FILE if $file =~ $regexp; | |
2448 | } | |
2449 | $files{$file} = File::Spec->abs2rel($file, $dir) if $self->contains_pod( $file ) | |
2450 | } | |
2451 | } | |
2452 | return \%files; | |
2453 | } | |
2454 | ||
2455 | sub contains_pod { | |
2456 | my ($self, $file) = @_; | |
2457 | return '' unless -T $file; # Only look at text files | |
2458 | ||
2459 | my $fh = IO::File->new( $file ) or die "Can't open $file: $!"; | |
2460 | while (my $line = <$fh>) { | |
2461 | return 1 if $line =~ /^\=(?:head|pod|item)/; | |
2462 | } | |
2463 | ||
2464 | return ''; | |
2465 | } | |
2466 | ||
2467 | sub ACTION_html { | |
2468 | my $self = shift; | |
2469 | ||
2470 | return unless $self->_mb_feature('HTML_support'); | |
2471 | ||
2472 | $self->depends_on('code'); | |
2473 | ||
2474 | foreach my $type ( qw(bin lib) ) { | |
2475 | my $files = $self->_find_pods( $self->{properties}{"${type}doc_dirs"}, | |
2476 | exclude => [ qr/\.(?:bat|com|html)$/ ] ); | |
2477 | next unless %$files; | |
2478 | ||
2479 | if ( $self->invoked_action eq 'html' ) { | |
2480 | $self->htmlify_pods( $type ); | |
2481 | } elsif ( $self->_is_default_installable("${type}html") ) { | |
2482 | $self->htmlify_pods( $type ); | |
2483 | } | |
2484 | } | |
2485 | ||
2486 | } | |
2487 | ||
2488 | ||
2489 | # 1) If it's an ActiveState perl install, we need to run | |
2490 | # ActivePerl::DocTools->UpdateTOC; | |
2491 | # 2) Links to other modules are not being generated | |
2492 | sub htmlify_pods { | |
2493 | my $self = shift; | |
2494 | my $type = shift; | |
2495 | my $htmldir = shift || File::Spec->catdir($self->blib, "${type}html"); | |
2496 | ||
2497 | require Module::Build::PodParser; | |
2498 | require Pod::Html; | |
2499 | ||
2500 | $self->add_to_cleanup('pod2htm*'); | |
2501 | ||
2502 | my $pods = $self->_find_pods( $self->{properties}{"${type}doc_dirs"}, | |
2503 | exclude => [ qr/\.(?:bat|com|html)$/ ] ); | |
f943a5bf | 2504 | return unless %$pods; # nothing to do |
bb4e9162 YST |
2505 | |
2506 | unless ( -d $htmldir ) { | |
2507 | File::Path::mkpath($htmldir, 0, 0755) | |
2508 | or die "Couldn't mkdir $htmldir: $!"; | |
2509 | } | |
2510 | ||
2511 | my @rootdirs = ($type eq 'bin') ? qw(bin) : | |
2512 | $self->installdirs eq 'core' ? qw(lib) : qw(site lib); | |
2513 | ||
2514 | my $podpath = join ':', | |
2515 | map $_->[1], | |
2516 | grep -e $_->[0], | |
2517 | map [File::Spec->catdir($self->blib, $_), $_], | |
2518 | qw( script lib ); | |
2519 | ||
2520 | foreach my $pod ( keys %$pods ) { | |
2521 | ||
2522 | my ($name, $path) = File::Basename::fileparse($pods->{$pod}, | |
2523 | qr{\.(?:pm|plx?|pod)$}); | |
2524 | my @dirs = File::Spec->splitdir( File::Spec->canonpath( $path ) ); | |
2525 | pop( @dirs ) if $dirs[-1] eq File::Spec->curdir; | |
2526 | ||
2527 | my $fulldir = File::Spec->catfile($htmldir, @rootdirs, @dirs); | |
2528 | my $outfile = File::Spec->catfile($fulldir, "${name}.html"); | |
2529 | my $infile = File::Spec->abs2rel($pod); | |
2530 | ||
2531 | next if $self->up_to_date($infile, $outfile); | |
2532 | ||
2533 | unless ( -d $fulldir ){ | |
2534 | File::Path::mkpath($fulldir, 0, 0755) | |
2535 | or die "Couldn't mkdir $fulldir: $!"; | |
2536 | } | |
2537 | ||
2538 | my $path2root = join( '/', ('..') x (@rootdirs+@dirs) ); | |
2539 | my $htmlroot = join( '/', | |
2540 | ($path2root, | |
2541 | $self->installdirs eq 'core' ? () : qw(site) ) ); | |
2542 | ||
47f13fd5 | 2543 | my $fh = IO::File->new($infile) or die "Can't read $infile: $!"; |
bb4e9162 YST |
2544 | my $abstract = Module::Build::PodParser->new(fh => $fh)->get_abstract(); |
2545 | ||
2546 | my $title = join( '::', (@dirs, $name) ); | |
2547 | $title .= " - $abstract" if $abstract; | |
2548 | ||
2549 | my @opts = ( | |
2550 | '--flush', | |
2551 | "--title=$title", | |
2552 | "--podpath=$podpath", | |
2553 | "--infile=$infile", | |
2554 | "--outfile=$outfile", | |
2555 | '--podroot=' . $self->blib, | |
2556 | "--htmlroot=$htmlroot", | |
2557 | ); | |
2558 | ||
2559 | if ( eval{Pod::Html->VERSION(1.03)} ) { | |
2560 | push( @opts, ('--header', '--backlink=Back to Top') ); | |
2561 | push( @opts, "--css=$path2root/" . $self->html_css) if $self->html_css; | |
2562 | } | |
2563 | ||
2564 | $self->log_info("HTMLifying $infile -> $outfile\n"); | |
2565 | $self->log_verbose("pod2html @opts\n"); | |
2566 | Pod::Html::pod2html(@opts); # or warn "pod2html @opts failed: $!"; | |
2567 | } | |
2568 | ||
2569 | } | |
2570 | ||
2571 | # Adapted from ExtUtils::MM_Unix | |
2572 | sub man1page_name { | |
2573 | my $self = shift; | |
2574 | return File::Basename::basename( shift ); | |
2575 | } | |
2576 | ||
2577 | # Adapted from ExtUtils::MM_Unix and Pod::Man | |
2578 | # Depending on M::B's dependency policy, it might make more sense to refactor | |
2579 | # Pod::Man::begin_pod() to extract a name() methods, and use them... | |
2580 | # -spurkis | |
2581 | sub man3page_name { | |
2582 | my $self = shift; | |
2583 | my ($vol, $dirs, $file) = File::Spec->splitpath( shift ); | |
2584 | my @dirs = File::Spec->splitdir( File::Spec->canonpath($dirs) ); | |
2585 | ||
2586 | # Remove known exts from the base name | |
2587 | $file =~ s/\.p(?:od|m|l)\z//i; | |
2588 | ||
2589 | return join( $self->manpage_separator, @dirs, $file ); | |
2590 | } | |
2591 | ||
2592 | sub manpage_separator { | |
2593 | return '::'; | |
2594 | } | |
2595 | ||
2596 | # For systems that don't have 'diff' executable, should use Algorithm::Diff | |
2597 | sub ACTION_diff { | |
2598 | my $self = shift; | |
2599 | $self->depends_on('build'); | |
2600 | my $local_lib = File::Spec->rel2abs('lib'); | |
2601 | my @myINC = grep {$_ ne $local_lib} @INC; | |
2602 | ||
2603 | # The actual install destination might not be in @INC, so check there too. | |
2604 | push @myINC, map $self->install_destination($_), qw(lib arch); | |
2605 | ||
2606 | my @flags = @{$self->{args}{ARGV}}; | |
2607 | @flags = $self->split_like_shell($self->{args}{flags} || '') unless @flags; | |
2608 | ||
2609 | my $installmap = $self->install_map; | |
2610 | delete $installmap->{read}; | |
2611 | delete $installmap->{write}; | |
2612 | ||
2613 | my $text_suffix = qr{\.(pm|pod)$}; | |
2614 | ||
2615 | while (my $localdir = each %$installmap) { | |
2616 | my @localparts = File::Spec->splitdir($localdir); | |
2617 | my $files = $self->rscan_dir($localdir, sub {-f}); | |
2618 | ||
2619 | foreach my $file (@$files) { | |
2620 | my @parts = File::Spec->splitdir($file); | |
2621 | @parts = @parts[@localparts .. $#parts]; # Get rid of blib/lib or similar | |
2622 | ||
2623 | my $installed = Module::Build::ModuleInfo->find_module_by_name( | |
2624 | join('::', @parts), \@myINC ); | |
2625 | if (not $installed) { | |
2626 | print "Only in lib: $file\n"; | |
2627 | next; | |
2628 | } | |
2629 | ||
2630 | my $status = File::Compare::compare($installed, $file); | |
2631 | next if $status == 0; # Files are the same | |
2632 | die "Can't compare $installed and $file: $!" if $status == -1; | |
2633 | ||
2634 | if ($file =~ $text_suffix) { | |
2635 | $self->do_system('diff', @flags, $installed, $file); | |
2636 | } else { | |
2637 | print "Binary files $file and $installed differ\n"; | |
2638 | } | |
2639 | } | |
2640 | } | |
2641 | } | |
2642 | ||
2643 | sub ACTION_pure_install { | |
2644 | shift()->depends_on('install'); | |
2645 | } | |
2646 | ||
2647 | sub ACTION_install { | |
2648 | my ($self) = @_; | |
2649 | require ExtUtils::Install; | |
2650 | $self->depends_on('build'); | |
2651 | ExtUtils::Install::install($self->install_map, !$self->quiet, 0, $self->{args}{uninst}||0); | |
2652 | } | |
2653 | ||
2654 | sub ACTION_fakeinstall { | |
2655 | my ($self) = @_; | |
2656 | require ExtUtils::Install; | |
2657 | $self->depends_on('build'); | |
2658 | ExtUtils::Install::install($self->install_map, !$self->quiet, 1, $self->{args}{uninst}||0); | |
2659 | } | |
2660 | ||
2661 | sub ACTION_versioninstall { | |
2662 | my ($self) = @_; | |
2663 | ||
2664 | die "You must have only.pm 0.25 or greater installed for this operation: $@\n" | |
2665 | unless eval { require only; 'only'->VERSION(0.25); 1 }; | |
2666 | ||
2667 | $self->depends_on('build'); | |
2668 | ||
2669 | my %onlyargs = map {exists($self->{args}{$_}) ? ($_ => $self->{args}{$_}) : ()} | |
2670 | qw(version versionlib); | |
2671 | only::install::install(%onlyargs); | |
2672 | } | |
2673 | ||
2674 | sub ACTION_clean { | |
2675 | my ($self) = @_; | |
2676 | foreach my $item (map glob($_), $self->cleanup) { | |
2677 | $self->delete_filetree($item); | |
2678 | } | |
2679 | } | |
2680 | ||
2681 | sub ACTION_realclean { | |
2682 | my ($self) = @_; | |
2683 | $self->depends_on('clean'); | |
2684 | $self->delete_filetree($self->config_dir, $self->build_script); | |
2685 | } | |
2686 | ||
2687 | sub ACTION_ppd { | |
2688 | my ($self) = @_; | |
2689 | require Module::Build::PPMMaker; | |
2690 | my $ppd = Module::Build::PPMMaker->new(); | |
2691 | my $file = $ppd->make_ppd(%{$self->{args}}, build => $self); | |
2692 | $self->add_to_cleanup($file); | |
2693 | } | |
2694 | ||
2695 | sub ACTION_ppmdist { | |
2696 | my ($self) = @_; | |
2697 | ||
2698 | $self->depends_on( 'build' ); | |
2699 | ||
2700 | my $ppm = $self->ppm_name; | |
2701 | $self->delete_filetree( $ppm ); | |
2702 | $self->log_info( "Creating $ppm\n" ); | |
2703 | $self->add_to_cleanup( $ppm, "$ppm.tar.gz" ); | |
2704 | ||
2705 | my %types = ( # translate types/dirs to those expected by ppm | |
2706 | lib => 'lib', | |
2707 | arch => 'arch', | |
2708 | bin => 'bin', | |
2709 | script => 'script', | |
2710 | bindoc => 'man1', | |
2711 | libdoc => 'man3', | |
2712 | binhtml => undef, | |
2713 | libhtml => undef, | |
2714 | ); | |
2715 | ||
2716 | foreach my $type ($self->install_types) { | |
2717 | next if exists( $types{$type} ) && !defined( $types{$type} ); | |
2718 | ||
2719 | my $dir = File::Spec->catdir( $self->blib, $type ); | |
2720 | next unless -e $dir; | |
2721 | ||
2722 | my $files = $self->rscan_dir( $dir ); | |
2723 | foreach my $file ( @$files ) { | |
2724 | next unless -f $file; | |
2725 | my $rel_file = | |
2726 | File::Spec->abs2rel( File::Spec->rel2abs( $file ), | |
2727 | File::Spec->rel2abs( $dir ) ); | |
2728 | my $to_file = | |
2729 | File::Spec->catdir( $ppm, 'blib', | |
2730 | exists( $types{$type} ) ? $types{$type} : $type, | |
2731 | $rel_file ); | |
2732 | $self->copy_if_modified( from => $file, to => $to_file ); | |
2733 | } | |
2734 | } | |
2735 | ||
2736 | foreach my $type ( qw(bin lib) ) { | |
2737 | local $self->{properties}{html_css} = 'Active.css'; | |
2738 | $self->htmlify_pods( $type, File::Spec->catdir($ppm, 'blib', 'html') ); | |
2739 | } | |
2740 | ||
2741 | # create a tarball; | |
2742 | # the directory tar'ed must be blib so we need to do a chdir first | |
2743 | my $start_wd = $self->cwd; | |
2744 | chdir( $ppm ) or die "Can't chdir to $ppm"; | |
2745 | $self->make_tarball( 'blib', File::Spec->catfile( $start_wd, $ppm ) ); | |
2746 | chdir( $start_wd ) or die "Can't chdir to $start_wd"; | |
2747 | ||
2748 | $self->depends_on( 'ppd' ); | |
2749 | ||
2750 | $self->delete_filetree( $ppm ); | |
2751 | } | |
2752 | ||
2753 | sub ACTION_dist { | |
2754 | my ($self) = @_; | |
2755 | ||
2756 | $self->depends_on('distdir'); | |
2757 | ||
2758 | my $dist_dir = $self->dist_dir; | |
2759 | ||
2760 | $self->make_tarball($dist_dir); | |
2761 | $self->delete_filetree($dist_dir); | |
2762 | } | |
2763 | ||
2764 | sub ACTION_distcheck { | |
2765 | my ($self) = @_; | |
a314697d | 2766 | |
bb4e9162 YST |
2767 | require ExtUtils::Manifest; |
2768 | local $^W; # ExtUtils::Manifest is not warnings clean. | |
2769 | my ($missing, $extra) = ExtUtils::Manifest::fullcheck(); | |
a314697d RS |
2770 | |
2771 | return unless @$missing || @$extra; | |
2772 | ||
2773 | my $msg = "MANIFEST appears to be out of sync with the distribution\n"; | |
2774 | if ( $self->invoked_action eq 'distcheck' ) { | |
2775 | die $msg; | |
2776 | } else { | |
2777 | warn $msg; | |
2778 | } | |
bb4e9162 YST |
2779 | } |
2780 | ||
2781 | sub _add_to_manifest { | |
2782 | my ($self, $manifest, $lines) = @_; | |
2783 | $lines = [$lines] unless ref $lines; | |
2784 | ||
2785 | my $existing_files = $self->_read_manifest($manifest); | |
2786 | return unless defined( $existing_files ); | |
2787 | ||
2788 | @$lines = grep {!exists $existing_files->{$_}} @$lines | |
2789 | or return; | |
2790 | ||
2791 | my $mode = (stat $manifest)[2]; | |
2792 | chmod($mode | 0222, $manifest) or die "Can't make $manifest writable: $!"; | |
2793 | ||
2794 | my $fh = IO::File->new("< $manifest") or die "Can't read $manifest: $!"; | |
2795 | my $last_line = (<$fh>)[-1] || "\n"; | |
2796 | my $has_newline = $last_line =~ /\n$/; | |
2797 | $fh->close; | |
2798 | ||
2799 | $fh = IO::File->new(">> $manifest") or die "Can't write to $manifest: $!"; | |
2800 | print $fh "\n" unless $has_newline; | |
2801 | print $fh map "$_\n", @$lines; | |
2802 | close $fh; | |
2803 | chmod($mode, $manifest); | |
2804 | ||
2805 | $self->log_info(map "Added to $manifest: $_\n", @$lines); | |
2806 | } | |
2807 | ||
2808 | sub _sign_dir { | |
2809 | my ($self, $dir) = @_; | |
2810 | ||
2811 | unless (eval { require Module::Signature; 1 }) { | |
2812 | $self->log_warn("Couldn't load Module::Signature for 'distsign' action:\n $@\n"); | |
2813 | return; | |
2814 | } | |
2815 | ||
2816 | # Add SIGNATURE to the MANIFEST | |
2817 | { | |
2818 | my $manifest = File::Spec->catfile($dir, 'MANIFEST'); | |
2819 | die "Signing a distribution requires a MANIFEST file" unless -e $manifest; | |
2820 | $self->_add_to_manifest($manifest, "SIGNATURE Added here by Module::Build"); | |
2821 | } | |
2822 | ||
2823 | # We protect the signing with an eval{} to make sure we get back to | |
2824 | # the right directory after a signature failure. Would be nice if | |
2825 | # Module::Signature took a directory argument. | |
2826 | ||
2827 | my $start_dir = $self->cwd; | |
2828 | chdir $dir or die "Can't chdir() to $dir: $!"; | |
2829 | eval {local $Module::Signature::Quiet = 1; Module::Signature::sign()}; | |
2830 | my @err = $@ ? ($@) : (); | |
2831 | chdir $start_dir or push @err, "Can't chdir() back to $start_dir: $!"; | |
2832 | die join "\n", @err if @err; | |
2833 | } | |
2834 | ||
2835 | sub ACTION_distsign { | |
2836 | my ($self) = @_; | |
2837 | { | |
2838 | local $self->{properties}{sign} = 0; # We'll sign it ourselves | |
2839 | $self->depends_on('distdir') unless -d $self->dist_dir; | |
2840 | } | |
2841 | $self->_sign_dir($self->dist_dir); | |
2842 | } | |
2843 | ||
2844 | sub ACTION_skipcheck { | |
2845 | my ($self) = @_; | |
2846 | ||
2847 | require ExtUtils::Manifest; | |
2848 | local $^W; # ExtUtils::Manifest is not warnings clean. | |
2849 | ExtUtils::Manifest::skipcheck(); | |
2850 | } | |
2851 | ||
2852 | sub ACTION_distclean { | |
2853 | my ($self) = @_; | |
2854 | ||
2855 | $self->depends_on('realclean'); | |
2856 | $self->depends_on('distcheck'); | |
2857 | } | |
2858 | ||
2859 | sub do_create_makefile_pl { | |
2860 | my $self = shift; | |
2861 | require Module::Build::Compat; | |
2862 | $self->delete_filetree('Makefile.PL'); | |
2863 | $self->log_info("Creating Makefile.PL\n"); | |
2864 | Module::Build::Compat->create_makefile_pl($self->create_makefile_pl, $self, @_); | |
2865 | $self->_add_to_manifest('MANIFEST', 'Makefile.PL'); | |
2866 | } | |
2867 | ||
2868 | sub do_create_readme { | |
2869 | my $self = shift; | |
2870 | $self->delete_filetree('README'); | |
2871 | ||
2872 | my $docfile = $self->_main_docfile; | |
2873 | unless ( $docfile ) { | |
2874 | $self->log_warn(<<EOF); | |
2875 | Cannot create README: can't determine which file contains documentation; | |
2876 | Must supply either 'dist_version_from', or 'module_name' parameter. | |
2877 | EOF | |
2878 | return; | |
2879 | } | |
2880 | ||
2881 | if ( eval {require Pod::Readme; 1} ) { | |
2882 | $self->log_info("Creating README using Pod::Readme\n"); | |
2883 | ||
2884 | my $parser = Pod::Readme->new; | |
2885 | $parser->parse_from_file($docfile, 'README', @_); | |
2886 | ||
2887 | } elsif ( eval {require Pod::Text; 1} ) { | |
2888 | $self->log_info("Creating README using Pod::Text\n"); | |
2889 | ||
2890 | my $fh = IO::File->new('> README'); | |
2891 | if ( defined($fh) ) { | |
2892 | local $^W = 0; | |
2893 | no strict "refs"; | |
2894 | ||
2895 | # work around bug in Pod::Text 3.01, which expects | |
2896 | # Pod::Simple::parse_file to take input and output filehandles | |
2897 | # when it actually only takes an input filehandle | |
2898 | ||
2899 | my $old_parse_file; | |
2900 | $old_parse_file = \&{"Pod::Simple::parse_file"} | |
2901 | and | |
2902 | local *{"Pod::Simple::parse_file"} = sub { | |
2903 | my $self = shift; | |
2904 | $self->output_fh($_[1]) if $_[1]; | |
2905 | $self->$old_parse_file($_[0]); | |
2906 | } | |
2907 | if $Pod::Text::VERSION | |
2908 | == 3.01; # Split line to avoid evil version-finder | |
2909 | ||
2910 | Pod::Text::pod2text( $docfile, $fh ); | |
2911 | ||
2912 | $fh->close; | |
2913 | } else { | |
2914 | $self->log_warn( | |
2915 | "Cannot create 'README' file: Can't open file for writing\n" ); | |
2916 | return; | |
2917 | } | |
2918 | ||
2919 | } else { | |
2920 | $self->log_warn("Can't load Pod::Readme or Pod::Text to create README\n"); | |
2921 | return; | |
2922 | } | |
2923 | ||
2924 | $self->_add_to_manifest('MANIFEST', 'README'); | |
2925 | } | |
2926 | ||
2927 | sub _main_docfile { | |
2928 | my $self = shift; | |
bb4e9162 YST |
2929 | if ( my $pm_file = $self->dist_version_from ) { |
2930 | (my $pod_file = $pm_file) =~ s/.pm$/.pod/; | |
2931 | return (-e $pod_file ? $pod_file : $pm_file); | |
2932 | } else { | |
2933 | return undef; | |
2934 | } | |
2935 | } | |
2936 | ||
2937 | sub ACTION_distdir { | |
2938 | my ($self) = @_; | |
2939 | ||
2940 | $self->depends_on('distmeta'); | |
2941 | ||
2942 | my $dist_files = $self->_read_manifest('MANIFEST') | |
2943 | or die "Can't create distdir without a MANIFEST file - run 'manifest' action first"; | |
2944 | delete $dist_files->{SIGNATURE}; # Don't copy, create a fresh one | |
2945 | die "No files found in MANIFEST - try running 'manifest' action?\n" | |
2946 | unless ($dist_files and keys %$dist_files); | |
2947 | my $metafile = $self->metafile; | |
2948 | $self->log_warn("*** Did you forget to add $metafile to the MANIFEST?\n") | |
2949 | unless exists $dist_files->{$metafile}; | |
2950 | ||
2951 | my $dist_dir = $self->dist_dir; | |
2952 | $self->delete_filetree($dist_dir); | |
2953 | $self->log_info("Creating $dist_dir\n"); | |
2954 | $self->add_to_cleanup($dist_dir); | |
2955 | ||
2956 | foreach my $file (keys %$dist_files) { | |
2957 | my $new = $self->copy_if_modified(from => $file, to_dir => $dist_dir, verbose => 0); | |
bb4e9162 YST |
2958 | } |
2959 | ||
2960 | $self->_sign_dir($dist_dir) if $self->{properties}{sign}; | |
2961 | } | |
2962 | ||
2963 | sub ACTION_disttest { | |
2964 | my ($self) = @_; | |
2965 | ||
2966 | $self->depends_on('distdir'); | |
2967 | ||
2968 | my $start_dir = $self->cwd; | |
2969 | my $dist_dir = $self->dist_dir; | |
2970 | chdir $dist_dir or die "Cannot chdir to $dist_dir: $!"; | |
2971 | # XXX could be different names for scripts | |
2972 | ||
2973 | $self->run_perl_script('Build.PL') # XXX Should this be run w/ --nouse-rcfile | |
2974 | or die "Error executing 'Build.PL' in dist directory: $!"; | |
2975 | $self->run_perl_script('Build') | |
2976 | or die "Error executing 'Build' in dist directory: $!"; | |
2977 | $self->run_perl_script('Build', [], ['test']) | |
2978 | or die "Error executing 'Build test' in dist directory"; | |
2979 | chdir $start_dir; | |
2980 | } | |
2981 | ||
2982 | sub _write_default_maniskip { | |
2983 | my $self = shift; | |
2984 | my $file = shift || 'MANIFEST.SKIP'; | |
2985 | my $fh = IO::File->new("> $file") | |
2986 | or die "Can't open $file: $!"; | |
2987 | ||
2988 | # This is derived from MakeMaker's default MANIFEST.SKIP file with | |
2989 | # some new entries | |
2990 | ||
2991 | print $fh <<'EOF'; | |
2992 | # Avoid version control files. | |
2993 | \bRCS\b | |
2994 | \bCVS\b | |
2995 | ,v$ | |
2996 | \B\.svn\b | |
2997 | \B\.cvsignore$ | |
2998 | ||
2999 | # Avoid Makemaker generated and utility files. | |
3000 | \bMakefile$ | |
3001 | \bblib | |
3002 | \bMakeMaker-\d | |
3003 | \bpm_to_blib$ | |
3004 | \bblibdirs$ | |
3005 | ^MANIFEST\.SKIP$ | |
3006 | ||
3007 | # Avoid Module::Build generated and utility files. | |
3008 | \bBuild$ | |
a314697d | 3009 | \bBuild.bat$ |
bb4e9162 YST |
3010 | \b_build |
3011 | ||
3012 | # Avoid Devel::Cover generated files | |
3013 | \bcover_db | |
3014 | ||
3015 | # Avoid temp and backup files. | |
3016 | ~$ | |
3017 | \.tmp$ | |
3018 | \.old$ | |
3019 | \.bak$ | |
3020 | \#$ | |
3021 | \.# | |
3022 | \.rej$ | |
3023 | ||
3024 | # Avoid OS-specific files/dirs | |
3025 | # Mac OSX metadata | |
3026 | \B\.DS_Store | |
3027 | # Mac OSX SMB mount metadata files | |
3028 | \B\._ | |
3029 | # Avoid archives of this distribution | |
3030 | EOF | |
3031 | ||
3032 | # Skip, for example, 'Module-Build-0.27.tar.gz' | |
3033 | print $fh '\b'.$self->dist_name.'-[\d\.\_]+'."\n"; | |
3034 | ||
3035 | $fh->close(); | |
3036 | } | |
3037 | ||
3038 | sub ACTION_manifest { | |
3039 | my ($self) = @_; | |
3040 | ||
3041 | my $maniskip = 'MANIFEST.SKIP'; | |
3042 | unless ( -e 'MANIFEST' || -e $maniskip ) { | |
3043 | $self->log_warn("File '$maniskip' does not exist: Creating a default '$maniskip'\n"); | |
3044 | $self->_write_default_maniskip($maniskip); | |
3045 | } | |
3046 | ||
3047 | require ExtUtils::Manifest; # ExtUtils::Manifest is not warnings clean. | |
3048 | local ($^W, $ExtUtils::Manifest::Quiet) = (0,1); | |
3049 | ExtUtils::Manifest::mkmanifest(); | |
3050 | } | |
3051 | ||
3052 | sub dist_dir { | |
3053 | my ($self) = @_; | |
3054 | return "$self->{properties}{dist_name}-$self->{properties}{dist_version}"; | |
3055 | } | |
3056 | ||
3057 | sub ppm_name { | |
3058 | my $self = shift; | |
3059 | return 'PPM-' . $self->dist_dir; | |
3060 | } | |
3061 | ||
3062 | sub _files_in { | |
3063 | my ($self, $dir) = @_; | |
3064 | return unless -d $dir; | |
3065 | ||
3066 | local *DH; | |
3067 | opendir DH, $dir or die "Can't read directory $dir: $!"; | |
3068 | ||
3069 | my @files; | |
3070 | while (defined (my $file = readdir DH)) { | |
3071 | my $full_path = File::Spec->catfile($dir, $file); | |
3072 | next if -d $full_path; | |
3073 | push @files, $full_path; | |
3074 | } | |
3075 | return @files; | |
3076 | } | |
3077 | ||
3078 | sub script_files { | |
3079 | my $self = shift; | |
3080 | ||
3081 | for ($self->{properties}{script_files}) { | |
3082 | $_ = shift if @_; | |
3083 | next unless $_; | |
3084 | ||
3085 | # Always coerce into a hash | |
3086 | return $_ if UNIVERSAL::isa($_, 'HASH'); | |
3087 | return $_ = { map {$_,1} @$_ } if UNIVERSAL::isa($_, 'ARRAY'); | |
3088 | ||
3089 | die "'script_files' must be a hashref, arrayref, or string" if ref(); | |
3090 | ||
3091 | return $_ = { map {$_,1} $self->_files_in( $_ ) } if -d $_; | |
3092 | return $_ = {$_ => 1}; | |
3093 | } | |
3094 | ||
3095 | return $_ = { map {$_,1} $self->_files_in( File::Spec->catdir( $self->base_dir, 'bin' ) ) }; | |
3096 | } | |
3097 | BEGIN { *scripts = \&script_files; } | |
3098 | ||
3099 | { | |
3100 | my %licenses = | |
3101 | ( | |
3102 | perl => 'http://dev.perl.org/licenses/', | |
3103 | gpl => 'http://www.opensource.org/licenses/gpl-license.php', | |
3104 | apache => 'http://apache.org/licenses/LICENSE-2.0', | |
3105 | artistic => 'http://opensource.org/licenses/artistic-license.php', | |
3106 | lgpl => 'http://opensource.org/licenses/artistic-license.php', | |
3107 | bsd => 'http://www.opensource.org/licenses/bsd-license.php', | |
3108 | gpl => 'http://www.opensource.org/licenses/gpl-license.php', | |
3109 | mit => 'http://opensource.org/licenses/mit-license.php', | |
3110 | mozilla => 'http://opensource.org/licenses/mozilla1.1.php', | |
3111 | open_source => undef, | |
3112 | unrestricted => undef, | |
3113 | restrictive => undef, | |
3114 | unknown => undef, | |
3115 | ); | |
3116 | sub valid_licenses { | |
3117 | return \%licenses; | |
3118 | } | |
3119 | } | |
3120 | ||
3121 | sub _hash_merge { | |
3122 | my ($self, $h, $k, $v) = @_; | |
3123 | if (ref $h->{$k} eq 'ARRAY') { | |
3124 | push @{$h->{$k}}, ref $v ? @$v : $v; | |
3125 | } elsif (ref $h->{$k} eq 'HASH') { | |
3126 | $h->{$k}{$_} = $v->{$_} foreach keys %$v; | |
3127 | } else { | |
3128 | $h->{$k} = $v; | |
3129 | } | |
3130 | } | |
3131 | ||
bb4e9162 YST |
3132 | sub ACTION_distmeta { |
3133 | my ($self) = @_; | |
3134 | ||
3135 | $self->do_create_makefile_pl if $self->create_makefile_pl; | |
3136 | $self->do_create_readme if $self->create_readme; | |
3137 | $self->do_create_metafile; | |
3138 | } | |
3139 | ||
3140 | sub do_create_metafile { | |
3141 | my $self = shift; | |
3142 | return if $self->{wrote_metadata}; | |
3143 | ||
3144 | my $p = $self->{properties}; | |
3145 | my $metafile = $self->metafile; | |
3146 | ||
3147 | unless ($p->{license}) { | |
3148 | $self->log_warn("No license specified, setting license = 'unknown'\n"); | |
3149 | $p->{license} = 'unknown'; | |
3150 | } | |
3151 | unless (exists $self->valid_licenses->{ $p->{license} }) { | |
3152 | die "Unknown license type '$p->{license}'"; | |
3153 | } | |
3154 | ||
3155 | # If we're in the distdir, the metafile may exist and be non-writable. | |
3156 | $self->delete_filetree($metafile); | |
3157 | $self->log_info("Creating $metafile\n"); | |
3158 | ||
3159 | # Since we're building ourself, we have to do some special stuff | |
3160 | # here: the ConfigData module is found in blib/lib. | |
3161 | local @INC = @INC; | |
3162 | if (($self->module_name || '') eq 'Module::Build') { | |
3163 | $self->depends_on('config_data'); | |
3164 | push @INC, File::Spec->catdir($self->blib, 'lib'); | |
3165 | } | |
3166 | ||
3167 | $self->write_metafile; | |
3168 | } | |
3169 | ||
3170 | sub write_metafile { | |
3171 | my $self = shift; | |
3172 | my $metafile = $self->metafile; | |
3173 | ||
3174 | if ($self->_mb_feature('YAML_support')) { | |
3175 | require YAML; | |
3176 | require YAML::Node; | |
3177 | ||
3178 | # We use YAML::Node to get the order nice in the YAML file. | |
3179 | $self->prepare_metadata( my $node = YAML::Node->new({}) ); | |
3180 | ||
3181 | # YAML API changed after version 0.30 | |
3182 | my $yaml_sub = $YAML::VERSION le '0.30' ? \&YAML::StoreFile : \&YAML::DumpFile; | |
3183 | $self->{wrote_metadata} = $yaml_sub->($metafile, $node ); | |
3184 | ||
3185 | } else { | |
a314697d RS |
3186 | require Module::Build::YAML; |
3187 | my (%node, @order_keys); | |
3188 | $self->prepare_metadata(\%node, \@order_keys); | |
3189 | $node{_order} = \@order_keys; | |
3190 | &Module::Build::YAML::DumpFile($metafile, \%node); | |
3191 | $self->{wrote_metadata} = 1; | |
bb4e9162 YST |
3192 | } |
3193 | ||
3194 | $self->_add_to_manifest('MANIFEST', $metafile); | |
3195 | } | |
3196 | ||
3197 | sub prepare_metadata { | |
a314697d | 3198 | my ($self, $node, $keys) = @_; |
bb4e9162 YST |
3199 | my $p = $self->{properties}; |
3200 | ||
a314697d RS |
3201 | # A little helper sub |
3202 | my $add_node = sub { | |
3203 | my ($name, $val) = @_; | |
3204 | $node->{$name} = $val; | |
3205 | push @$keys, $name if $keys; | |
3206 | }; | |
3207 | ||
bb4e9162 YST |
3208 | foreach (qw(dist_name dist_version dist_author dist_abstract license)) { |
3209 | (my $name = $_) =~ s/^dist_//; | |
a314697d | 3210 | $add_node->($name, $self->$_()); |
47f13fd5 | 3211 | die "ERROR: Missing required field '$_' for META.yml\n" |
bb4e9162 YST |
3212 | unless defined($node->{$name}) && length($node->{$name}); |
3213 | } | |
7a501b10 SP |
3214 | # Really don't understand why I need the "... if exists" here |
3215 | $node->{version} = $node->{version}->stringify if exists $node->{version}; | |
bb4e9162 YST |
3216 | |
3217 | if (defined( $self->license ) && | |
3218 | defined( my $url = $self->valid_licenses->{ $self->license } )) { | |
3219 | $node->{resources}{license} = $url; | |
3220 | } | |
3221 | ||
3222 | foreach ( @{$self->prereq_action_types} ) { | |
a314697d RS |
3223 | if (exists $p->{$_} and keys %{ $p->{$_} }) { |
3224 | $add_node->($_, $p->{$_}); | |
3225 | } | |
bb4e9162 YST |
3226 | } |
3227 | ||
a314697d RS |
3228 | if (exists $p->{dynamic_config}) { |
3229 | $add_node->('dynamic_config', $p->{dynamic_config}); | |
3230 | } | |
bb4e9162 YST |
3231 | my $pkgs = eval { $self->find_dist_packages }; |
3232 | if ($@) { | |
7a501b10 | 3233 | $self->log_warn("$@\nWARNING: Possible missing or corrupt 'MANIFEST' file.\n" . |
bb4e9162 YST |
3234 | "Nothing to enter for 'provides' field in META.yml\n"); |
3235 | } else { | |
3236 | $node->{provides} = $pkgs if %$pkgs; | |
3237 | } | |
3238 | ; | |
a314697d RS |
3239 | if (exists $p->{no_index}) { |
3240 | $add_node->('no_index', $p->{no_index}); | |
3241 | } | |
bb4e9162 | 3242 | |
a314697d | 3243 | $add_node->('generated_by', "Module::Build version $Module::Build::VERSION"); |
bb4e9162 | 3244 | |
a314697d RS |
3245 | $add_node->('meta-spec', |
3246 | {version => '1.2', | |
3247 | url => 'http://module-build.sourceforge.net/META-spec-v1.2.html', | |
3248 | }); | |
bb4e9162 YST |
3249 | |
3250 | while (my($k, $v) = each %{$self->meta_add}) { | |
a314697d | 3251 | $add_node->($k, $v); |
bb4e9162 YST |
3252 | } |
3253 | ||
3254 | while (my($k, $v) = each %{$self->meta_merge}) { | |
3255 | $self->_hash_merge($node, $k, $v); | |
3256 | } | |
3257 | ||
3258 | return $node; | |
3259 | } | |
3260 | ||
3261 | sub _read_manifest { | |
3262 | my ($self, $file) = @_; | |
3263 | return undef unless -e $file; | |
3264 | ||
3265 | require ExtUtils::Manifest; # ExtUtils::Manifest is not warnings clean. | |
3266 | local ($^W, $ExtUtils::Manifest::Quiet) = (0,1); | |
3267 | return scalar ExtUtils::Manifest::maniread($file); | |
3268 | } | |
3269 | ||
3270 | sub find_dist_packages { | |
3271 | my $self = shift; | |
3272 | ||
3273 | # Only packages in .pm files are candidates for inclusion here. | |
3274 | # Only include things in the MANIFEST, not things in developer's | |
3275 | # private stock. | |
3276 | ||
3277 | my $manifest = $self->_read_manifest('MANIFEST') | |
3278 | or die "Can't find dist packages without a MANIFEST file - run 'manifest' action first"; | |
3279 | ||
3280 | # Localize | |
3281 | my %dist_files = map { $self->localize_file_path($_) => $_ } | |
3282 | keys %$manifest; | |
3283 | ||
3284 | my @pm_files = grep {exists $dist_files{$_}} keys %{ $self->find_pm_files }; | |
3285 | ||
3286 | # First, we enumerate all packages & versions, | |
3287 | # seperating into primary & alternative candidates | |
3288 | my( %prime, %alt ); | |
3289 | foreach my $file (@pm_files) { | |
3290 | next if $dist_files{$file} =~ m{^t/}; # Skip things in t/ | |
3291 | ||
3292 | my @path = split( /\//, $dist_files{$file} ); | |
3293 | (my $prime_package = join( '::', @path[1..$#path] )) =~ s/\.pm$//; | |
3294 | ||
3295 | my $pm_info = Module::Build::ModuleInfo->new_from_file( $file ); | |
3296 | ||
3297 | foreach my $package ( $pm_info->packages_inside ) { | |
3298 | next if $package eq 'main'; # main can appear numerous times, ignore | |
3299 | next if grep /^_/, split( /::/, $package ); # private package, ignore | |
3300 | ||
3301 | my $version = $pm_info->version( $package ); | |
3302 | ||
3303 | if ( $package eq $prime_package ) { | |
3304 | if ( exists( $prime{$package} ) ) { | |
3305 | # M::B::ModuleInfo will handle this conflict | |
3306 | die "Unexpected conflict in '$package'; multiple versions found.\n"; | |
3307 | } else { | |
3308 | $prime{$package}{file} = $dist_files{$file}; | |
3309 | $prime{$package}{version} = $version if defined( $version ); | |
3310 | } | |
3311 | } else { | |
3312 | push( @{$alt{$package}}, { | |
3313 | file => $dist_files{$file}, | |
3314 | version => $version, | |
3315 | } ); | |
3316 | } | |
3317 | } | |
3318 | } | |
3319 | ||
3320 | # Then we iterate over all the packages found above, identifying conflicts | |
3321 | # and selecting the "best" candidate for recording the file & version | |
3322 | # for each package. | |
3323 | foreach my $package ( keys( %alt ) ) { | |
3324 | my $result = $self->_resolve_module_versions( $alt{$package} ); | |
3325 | ||
3326 | if ( exists( $prime{$package} ) ) { # primary package selected | |
3327 | ||
3328 | if ( $result->{err} ) { | |
3329 | # Use the selected primary package, but there are conflicting | |
3330 | # errors amoung multiple alternative packages that need to be | |
3331 | # reported | |
3332 | $self->log_warn( | |
3333 | "Found conflicting versions for package '$package'\n" . | |
3334 | " $prime{$package}{file} ($prime{$package}{version})\n" . | |
3335 | $result->{err} | |
3336 | ); | |
3337 | ||
3338 | } elsif ( defined( $result->{version} ) ) { | |
3339 | # There is a primary package selected, and exactly one | |
3340 | # alternative package | |
3341 | ||
3342 | if ( exists( $prime{$package}{version} ) && | |
3343 | defined( $prime{$package}{version} ) ) { | |
3344 | # Unless the version of the primary package agrees with the | |
3345 | # version of the alternative package, report a conflict | |
3346 | if ( $self->compare_versions( $prime{$package}{version}, '!=', | |
3347 | $result->{version} ) ) { | |
3348 | $self->log_warn( | |
3349 | "Found conflicting versions for package '$package'\n" . | |
3350 | " $prime{$package}{file} ($prime{$package}{version})\n" . | |
3351 | " $result->{file} ($result->{version})\n" | |
3352 | ); | |
3353 | } | |
3354 | ||
3355 | } else { | |
3356 | # The prime package selected has no version so, we choose to | |
3357 | # use any alternative package that does have a version | |
3358 | $prime{$package}{file} = $result->{file}; | |
3359 | $prime{$package}{version} = $result->{version}; | |
3360 | } | |
3361 | ||
3362 | } else { | |
3363 | # no alt package found with a version, but we have a prime | |
3364 | # package so we use it whether it has a version or not | |
3365 | } | |
3366 | ||
3367 | } else { # No primary package was selected, use the best alternative | |
3368 | ||
3369 | if ( $result->{err} ) { | |
3370 | $self->log_warn( | |
3371 | "Found conflicting versions for package '$package'\n" . | |
3372 | $result->{err} | |
3373 | ); | |
3374 | } | |
3375 | ||
3376 | # Despite possible conflicting versions, we choose to record | |
3377 | # something rather than nothing | |
3378 | $prime{$package}{file} = $result->{file}; | |
3379 | $prime{$package}{version} = $result->{version} | |
3380 | if defined( $result->{version} ); | |
3381 | } | |
3382 | } | |
3383 | ||
7a501b10 SP |
3384 | # Stringify versions |
3385 | for (grep exists $_->{version}, values %prime) { | |
3386 | $_->{version} = $_->{version}->stringify; | |
3387 | } | |
3388 | ||
bb4e9162 YST |
3389 | return \%prime; |
3390 | } | |
3391 | ||
3392 | # seperate out some of the conflict resolution logic from | |
3393 | # $self->find_dist_packages(), above, into a helper function. | |
3394 | # | |
3395 | sub _resolve_module_versions { | |
3396 | my $self = shift; | |
3397 | ||
3398 | my $packages = shift; | |
3399 | ||
3400 | my( $file, $version ); | |
3401 | my $err = ''; | |
3402 | foreach my $p ( @$packages ) { | |
3403 | if ( defined( $p->{version} ) ) { | |
3404 | if ( defined( $version ) ) { | |
3405 | if ( $self->compare_versions( $version, '!=', $p->{version} ) ) { | |
3406 | $err .= " $p->{file} ($p->{version})\n"; | |
3407 | } else { | |
3408 | # same version declared multiple times, ignore | |
3409 | } | |
3410 | } else { | |
3411 | $file = $p->{file}; | |
3412 | $version = $p->{version}; | |
3413 | } | |
3414 | } | |
3415 | $file ||= $p->{file} if defined( $p->{file} ); | |
3416 | } | |
3417 | ||
3418 | if ( $err ) { | |
3419 | $err = " $file ($version)\n" . $err; | |
3420 | } | |
3421 | ||
3422 | my %result = ( | |
3423 | file => $file, | |
3424 | version => $version, | |
3425 | err => $err | |
3426 | ); | |
3427 | ||
3428 | return \%result; | |
3429 | } | |
3430 | ||
3431 | sub make_tarball { | |
3432 | my ($self, $dir, $file) = @_; | |
3433 | $file ||= $dir; | |
3434 | ||
3435 | $self->log_info("Creating $file.tar.gz\n"); | |
3436 | ||
3437 | if ($self->{args}{tar}) { | |
3438 | my $tar_flags = $self->verbose ? 'cvf' : 'cf'; | |
3439 | $self->do_system($self->split_like_shell($self->{args}{tar}), $tar_flags, "$file.tar", $dir); | |
3440 | $self->do_system($self->split_like_shell($self->{args}{gzip}), "$file.tar") if $self->{args}{gzip}; | |
3441 | } else { | |
3442 | require Archive::Tar; | |
3443 | # Archive::Tar versions >= 1.09 use the following to enable a compatibility | |
3444 | # hack so that the resulting archive is compatible with older clients. | |
3445 | $Archive::Tar::DO_NOT_USE_PREFIX = 0; | |
3446 | my $files = $self->rscan_dir($dir); | |
3447 | Archive::Tar->create_archive("$file.tar.gz", 1, @$files); | |
3448 | } | |
3449 | } | |
3450 | ||
dc8021d3 SP |
3451 | sub install_path { |
3452 | my $self = shift; | |
3453 | my( $type, $value ) = ( @_, '<empty>' ); | |
3454 | ||
3455 | Carp::croak( 'Type argument missing' ) | |
3456 | unless defined( $type ); | |
3457 | ||
3458 | my $map = $self->{properties}{install_path}; | |
3459 | return $map unless @_; | |
3460 | ||
3461 | # delete existing value if $value is literal undef() | |
3462 | unless ( defined( $value ) ) { | |
3463 | delete( $map->{$type} ); | |
3464 | return undef; | |
3465 | } | |
3466 | ||
3467 | # return existing value if no new $value is given | |
3468 | if ( $value eq '<empty>' ) { | |
3469 | return undef unless exists $map->{$type}; | |
3470 | return $map->{$type}; | |
3471 | } | |
3472 | ||
3473 | # set value if $value is a valid relative path | |
3474 | return $map->{$type} = $value; | |
3475 | } | |
3476 | ||
bb4e9162 | 3477 | sub install_base_relpaths { |
dc8021d3 SP |
3478 | # Usage: install_base_relpaths(), install_base_relpaths('lib'), |
3479 | # or install_base_relpaths('lib' => $value); | |
bb4e9162 YST |
3480 | my $self = shift; |
3481 | my $map = $self->{properties}{install_base_relpaths}; | |
3482 | return $map unless @_; | |
dc8021d3 | 3483 | return $self->_relpaths($map, @_); |
bb4e9162 YST |
3484 | } |
3485 | ||
3486 | ||
3487 | # Translated from ExtUtils::MM_Any::init_INSTALL_from_PREFIX | |
3488 | sub prefix_relative { | |
3489 | my ($self, $type) = @_; | |
3490 | my $installdirs = $self->installdirs; | |
3491 | ||
3492 | my $relpath = $self->install_sets($installdirs)->{$type}; | |
3493 | ||
3494 | return $self->_prefixify($relpath, | |
3495 | $self->original_prefix($installdirs), | |
3496 | $type, | |
3497 | ); | |
3498 | } | |
3499 | ||
dc8021d3 SP |
3500 | sub _relpaths { |
3501 | my $self = shift; | |
3502 | my( $map, $type, $value ) = ( @_, '<empty>' ); | |
3503 | ||
3504 | Carp::croak( 'Type argument missing' ) | |
3505 | unless defined( $type ); | |
3506 | ||
3507 | my @value = (); | |
3508 | ||
3509 | # delete existing value if $value is literal undef() | |
3510 | unless ( defined( $value ) ) { | |
3511 | delete( $map->{$type} ); | |
3512 | return undef; | |
3513 | } | |
3514 | ||
3515 | # return existing value if no new $value is given | |
3516 | elsif ( $value eq '<empty>' ) { | |
3517 | return undef unless exists $map->{$type}; | |
3518 | @value = @{ $map->{$type} }; | |
3519 | } | |
3520 | ||
3521 | # set value if $value is a valid relative path | |
3522 | else { | |
3523 | Carp::croak( "Value must be a relative path" ) | |
3524 | if File::Spec::Unix->file_name_is_absolute($value); | |
3525 | ||
3526 | @value = split( /\//, $value ); | |
3527 | $map->{$type} = \@value; | |
3528 | } | |
3529 | ||
3530 | return File::Spec->catdir( @value ); | |
3531 | } | |
bb4e9162 YST |
3532 | |
3533 | # Defaults to use in case the config install paths cannot be prefixified. | |
3534 | sub prefix_relpaths { | |
dc8021d3 SP |
3535 | # Usage: prefix_relpaths('site'), prefix_relpaths('site', 'lib'), |
3536 | # or prefix_relpaths('site', 'lib' => $value); | |
bb4e9162 YST |
3537 | my $self = shift; |
3538 | my $installdirs = shift || $self->installdirs; | |
3539 | my $map = $self->{properties}{prefix_relpaths}{$installdirs}; | |
3540 | return $map unless @_; | |
dc8021d3 | 3541 | return $self->_relpaths($map, @_); |
bb4e9162 YST |
3542 | } |
3543 | ||
3544 | ||
3545 | # Translated from ExtUtils::MM_Unix::prefixify() | |
3546 | sub _prefixify { | |
3547 | my($self, $path, $sprefix, $type) = @_; | |
3548 | ||
3549 | my $rprefix = $self->prefix; | |
3550 | $rprefix .= '/' if $sprefix =~ m|/$|; | |
3551 | ||
3552 | $self->log_verbose(" prefixify $path from $sprefix to $rprefix\n") | |
3553 | if defined( $path ) && length( $path ); | |
3554 | ||
3555 | if( !defined( $path ) || ( length( $path ) == 0 ) ) { | |
3556 | $self->log_verbose(" no path to prefixify, falling back to default.\n"); | |
3557 | return $self->_prefixify_default( $type, $rprefix ); | |
3558 | } elsif( !File::Spec->file_name_is_absolute($path) ) { | |
3559 | $self->log_verbose(" path is relative, not prefixifying.\n"); | |
3560 | } elsif( $sprefix eq $rprefix ) { | |
3561 | $self->log_verbose(" no new prefix.\n"); | |
3562 | } elsif( $path !~ s{^\Q$sprefix\E\b}{}s ) { | |
3563 | $self->log_verbose(" cannot prefixify, falling back to default.\n"); | |
3564 | return $self->_prefixify_default( $type, $rprefix ); | |
3565 | } | |
3566 | ||
3567 | $self->log_verbose(" now $path in $rprefix\n"); | |
3568 | ||
3569 | return $path; | |
3570 | } | |
3571 | ||
3572 | sub _prefixify_default { | |
3573 | my $self = shift; | |
3574 | my $type = shift; | |
3575 | my $rprefix = shift; | |
3576 | ||
3577 | my $default = $self->prefix_relpaths($self->installdirs, $type); | |
3578 | if( !$default ) { | |
3579 | $self->log_verbose(" no default install location for type '$type', using prefix '$rprefix'.\n"); | |
3580 | return $rprefix; | |
3581 | } else { | |
3582 | return $default; | |
3583 | } | |
3584 | } | |
3585 | ||
3586 | sub install_destination { | |
3587 | my ($self, $type) = @_; | |
3588 | ||
3589 | return $self->install_path($type) if $self->install_path($type); | |
3590 | ||
3591 | if ( $self->install_base ) { | |
3592 | my $relpath = $self->install_base_relpaths($type); | |
3593 | return $relpath ? File::Spec->catdir($self->install_base, $relpath) : undef; | |
3594 | } | |
3595 | ||
3596 | if ( $self->prefix ) { | |
3597 | my $relpath = $self->prefix_relative($type); | |
3598 | return $relpath ? File::Spec->catdir($self->prefix, $relpath) : undef; | |
3599 | } | |
3600 | ||
3601 | return $self->install_sets($self->installdirs)->{$type}; | |
3602 | } | |
3603 | ||
3604 | sub install_types { | |
3605 | my $self = shift; | |
dc8021d3 SP |
3606 | |
3607 | my %types; | |
3608 | if ( $self->install_base ) { | |
3609 | %types = %{$self->install_base_relpaths}; | |
3610 | } elsif ( $self->prefix ) { | |
3611 | %types = %{$self->prefix_relpaths}; | |
3612 | } else { | |
3613 | %types = %{$self->install_sets($self->installdirs)}; | |
3614 | } | |
3615 | ||
3616 | %types = (%types, %{$self->install_path}); | |
3617 | ||
bb4e9162 YST |
3618 | return sort keys %types; |
3619 | } | |
3620 | ||
3621 | sub install_map { | |
3622 | my ($self, $blib) = @_; | |
3623 | $blib ||= $self->blib; | |
3624 | ||
3625 | my( %map, @skipping ); | |
3626 | foreach my $type ($self->install_types) { | |
3627 | my $localdir = File::Spec->catdir( $blib, $type ); | |
3628 | next unless -e $localdir; | |
3629 | ||
3630 | if (my $dest = $self->install_destination($type)) { | |
3631 | $map{$localdir} = $dest; | |
3632 | } else { | |
3633 | push( @skipping, $type ); | |
3634 | } | |
3635 | } | |
3636 | ||
3637 | $self->log_warn( | |
3638 | "WARNING: Can't figure out install path for types: @skipping\n" . | |
3639 | "Files will not be installed.\n" | |
3640 | ) if @skipping; | |
47f13fd5 | 3641 | |
bb4e9162 | 3642 | # Write the packlist into the same place as ExtUtils::MakeMaker. |
a314697d | 3643 | if ($self->create_packlist and my $module_name = $self->module_name) { |
47f13fd5 SP |
3644 | my $archdir = $self->install_destination('arch'); |
3645 | my @ext = split /::/, $module_name; | |
3646 | $map{write} = File::Spec->catdir($archdir, 'auto', @ext, '.packlist'); | |
3647 | } | |
3648 | ||
bb4e9162 YST |
3649 | # Handle destdir |
3650 | if (length(my $destdir = $self->destdir || '')) { | |
3651 | foreach (keys %map) { | |
3652 | # Need to remove volume from $map{$_} using splitpath, or else | |
3653 | # we'll create something crazy like C:\Foo\Bar\E:\Baz\Quux | |
3654 | my ($volume, $path) = File::Spec->splitpath( $map{$_}, 1 ); | |
3655 | $map{$_} = File::Spec->catdir($destdir, $path); | |
3656 | } | |
3657 | } | |
3658 | ||
3659 | $map{read} = ''; # To keep ExtUtils::Install quiet | |
3660 | ||
3661 | return \%map; | |
3662 | } | |
3663 | ||
3664 | sub depends_on { | |
3665 | my $self = shift; | |
3666 | foreach my $action (@_) { | |
3667 | $self->_call_action($action); | |
3668 | } | |
3669 | } | |
3670 | ||
3671 | sub rscan_dir { | |
3672 | my ($self, $dir, $pattern) = @_; | |
3673 | my @result; | |
3674 | local $_; # find() can overwrite $_, so protect ourselves | |
3675 | my $subr = !$pattern ? sub {push @result, $File::Find::name} : | |
3676 | !ref($pattern) || (ref $pattern eq 'Regexp') ? sub {push @result, $File::Find::name if /$pattern/} : | |
3677 | ref($pattern) eq 'CODE' ? sub {push @result, $File::Find::name if $pattern->()} : | |
3678 | die "Unknown pattern type"; | |
3679 | ||
3680 | File::Find::find({wanted => $subr, no_chdir => 1}, $dir); | |
3681 | return \@result; | |
3682 | } | |
3683 | ||
3684 | sub delete_filetree { | |
3685 | my $self = shift; | |
3686 | my $deleted = 0; | |
3687 | foreach (@_) { | |
3688 | next unless -e $_; | |
3689 | $self->log_info("Deleting $_\n"); | |
3690 | File::Path::rmtree($_, 0, 0); | |
3691 | die "Couldn't remove '$_': $!\n" if -e $_; | |
3692 | $deleted++; | |
3693 | } | |
3694 | return $deleted; | |
3695 | } | |
3696 | ||
3697 | sub autosplit_file { | |
3698 | my ($self, $file, $to) = @_; | |
3699 | require AutoSplit; | |
3700 | my $dir = File::Spec->catdir($to, 'lib', 'auto'); | |
3701 | AutoSplit::autosplit($file, $dir); | |
3702 | } | |
3703 | ||
3704 | sub _cbuilder { | |
3705 | # Returns a CBuilder object | |
3706 | ||
3707 | my $self = shift; | |
3708 | my $p = $self->{properties}; | |
3709 | return $p->{_cbuilder} if $p->{_cbuilder}; | |
3710 | return unless $self->_mb_feature('C_support'); | |
3711 | ||
3712 | require ExtUtils::CBuilder; | |
3713 | return $p->{_cbuilder} = ExtUtils::CBuilder->new(config => $self->config); | |
3714 | } | |
3715 | ||
3716 | sub have_c_compiler { | |
3717 | my ($self) = @_; | |
3718 | ||
3719 | my $p = $self->{properties}; | |
3720 | return $p->{have_compiler} if defined $p->{have_compiler}; | |
3721 | ||
3722 | $self->log_verbose("Checking if compiler tools configured... "); | |
3723 | my $b = $self->_cbuilder; | |
3724 | my $have = $b && $b->have_compiler; | |
3725 | $self->log_verbose($have ? "ok.\n" : "failed.\n"); | |
3726 | return $p->{have_compiler} = $have; | |
3727 | } | |
3728 | ||
3729 | sub compile_c { | |
3730 | my ($self, $file, %args) = @_; | |
3731 | my $b = $self->_cbuilder | |
3732 | or die "Module::Build is not configured with C_support"; | |
3733 | ||
3734 | my $obj_file = $b->object_file($file); | |
3735 | $self->add_to_cleanup($obj_file); | |
3736 | return $obj_file if $self->up_to_date($file, $obj_file); | |
3737 | ||
3738 | $b->compile(source => $file, | |
3739 | defines => $args{defines}, | |
3740 | object_file => $obj_file, | |
3741 | include_dirs => $self->include_dirs, | |
3742 | extra_compiler_flags => $self->extra_compiler_flags, | |
3743 | ); | |
3744 | ||
3745 | return $obj_file; | |
3746 | } | |
3747 | ||
3748 | sub link_c { | |
3749 | my ($self, $to, $file_base) = @_; | |
3750 | my $p = $self->{properties}; # For convenience | |
3751 | ||
3752 | my $spec = $self->_infer_xs_spec($file_base); | |
3753 | ||
3754 | $self->add_to_cleanup($spec->{lib_file}); | |
3755 | ||
3756 | my $objects = $p->{objects} || []; | |
3757 | ||
3758 | return $spec->{lib_file} | |
3759 | if $self->up_to_date([$spec->{obj_file}, @$objects], | |
3760 | $spec->{lib_file}); | |
3761 | ||
3762 | my $module_name = $self->module_name; | |
3763 | $module_name ||= $spec->{module_name}; | |
3764 | ||
3765 | my $b = $self->_cbuilder | |
3766 | or die "Module::Build is not configured with C_support"; | |
3767 | $b->link( | |
3768 | module_name => $module_name, | |
3769 | objects => [$spec->{obj_file}, @$objects], | |
3770 | lib_file => $spec->{lib_file}, | |
3771 | extra_linker_flags => $p->{extra_linker_flags} ); | |
3772 | ||
3773 | return $spec->{lib_file}; | |
3774 | } | |
3775 | ||
3776 | sub compile_xs { | |
3777 | my ($self, $file, %args) = @_; | |
3778 | ||
3779 | $self->log_info("$file -> $args{outfile}\n"); | |
3780 | ||
3781 | if (eval {require ExtUtils::ParseXS; 1}) { | |
3782 | ||
3783 | ExtUtils::ParseXS::process_file( | |
3784 | filename => $file, | |
3785 | prototypes => 0, | |
3786 | output => $args{outfile}, | |
3787 | ); | |
3788 | } else { | |
3789 | # Ok, I give up. Just use backticks. | |
3790 | ||
3791 | my $xsubpp = Module::Build::ModuleInfo->find_module_by_name('ExtUtils::xsubpp') | |
3792 | or die "Can't find ExtUtils::xsubpp in INC (@INC)"; | |
3793 | ||
3794 | my @typemaps; | |
3795 | push @typemaps, Module::Build::ModuleInfo->find_module_by_name('ExtUtils::typemap', \@INC); | |
3796 | my $lib_typemap = Module::Build::ModuleInfo->find_module_by_name('typemap', ['lib']); | |
3797 | if (defined $lib_typemap and -e $lib_typemap) { | |
3798 | push @typemaps, 'typemap'; | |
3799 | } | |
a314697d | 3800 | @typemaps = map {+'-typemap', $_} @typemaps; |
bb4e9162 YST |
3801 | |
3802 | my $cf = $self->config; | |
3803 | my $perl = $self->{properties}{perl}; | |
3804 | ||
a314697d RS |
3805 | my @command = ($perl, "-I$cf->{installarchlib}", "-I$cf->{installprivlib}", $xsubpp, '-noprototypes', |
3806 | @typemaps, $file); | |
bb4e9162 | 3807 | |
a314697d | 3808 | $self->log_info("@command\n"); |
bb4e9162 | 3809 | my $fh = IO::File->new("> $args{outfile}") or die "Couldn't write $args{outfile}: $!"; |
a314697d | 3810 | print {$fh} $self->_backticks(@command); |
bb4e9162 YST |
3811 | close $fh; |
3812 | } | |
3813 | } | |
3814 | ||
3815 | sub split_like_shell { | |
3816 | my ($self, $string) = @_; | |
3817 | ||
3818 | return () unless defined($string); | |
3819 | return @$string if UNIVERSAL::isa($string, 'ARRAY'); | |
3820 | $string =~ s/^\s+|\s+$//g; | |
3821 | return () unless length($string); | |
3822 | ||
3823 | return Text::ParseWords::shellwords($string); | |
3824 | } | |
3825 | ||
3826 | sub run_perl_script { | |
3827 | my ($self, $script, $preargs, $postargs) = @_; | |
3828 | foreach ($preargs, $postargs) { | |
3829 | $_ = [ $self->split_like_shell($_) ] unless ref(); | |
3830 | } | |
3831 | return $self->run_perl_command([@$preargs, $script, @$postargs]); | |
3832 | } | |
3833 | ||
3834 | sub run_perl_command { | |
3835 | # XXX Maybe we should accept @args instead of $args? Must resolve | |
3836 | # this before documenting. | |
3837 | my ($self, $args) = @_; | |
3838 | $args = [ $self->split_like_shell($args) ] unless ref($args); | |
a314697d | 3839 | $args = [ split(/\s+/, $self->_quote_args($args)) ] if $self->os_type eq 'VMS'; |
bb4e9162 YST |
3840 | my $perl = ref($self) ? $self->perl : $self->find_perl_interpreter; |
3841 | ||
3842 | # Make sure our local additions to @INC are propagated to the subprocess | |
3843 | my $c = ref $self ? $self->config : \%Config::Config; | |
3844 | local $ENV{PERL5LIB} = join $c->{path_sep}, $self->_added_to_INC; | |
3845 | ||
3846 | return $self->do_system($perl, @$args); | |
3847 | } | |
3848 | ||
3849 | # Infer various data from the path of the input filename | |
3850 | # that is needed to create output files. | |
3851 | # The input filename is expected to be of the form: | |
3852 | # lib/Module/Name.ext or Module/Name.ext | |
3853 | sub _infer_xs_spec { | |
3854 | my $self = shift; | |
3855 | my $file = shift; | |
3856 | ||
3857 | my $cf = $self->{config}; | |
3858 | ||
3859 | my %spec; | |
3860 | ||
3861 | my( $v, $d, $f ) = File::Spec->splitpath( $file ); | |
3862 | my @d = File::Spec->splitdir( $d ); | |
3863 | (my $file_base = $f) =~ s/\.[^.]+$//i; | |
3864 | ||
3865 | $spec{base_name} = $file_base; | |
3866 | ||
3867 | $spec{src_dir} = File::Spec->catpath( $v, $d, '' ); | |
3868 | ||
3869 | # the module name | |
3870 | shift( @d ) while @d && ($d[0] eq 'lib' || $d[0] eq ''); | |
3871 | pop( @d ) while @d && $d[-1] eq ''; | |
3872 | $spec{module_name} = join( '::', (@d, $file_base) ); | |
3873 | ||
3874 | $spec{archdir} = File::Spec->catdir($self->blib, 'arch', 'auto', | |
3875 | @d, $file_base); | |
3876 | ||
3877 | $spec{bs_file} = File::Spec->catfile($spec{archdir}, "${file_base}.bs"); | |
3878 | ||
3879 | $spec{lib_file} = File::Spec->catfile($spec{archdir}, | |
3880 | "${file_base}.$cf->{dlext}"); | |
3881 | ||
3882 | $spec{c_file} = File::Spec->catfile( $spec{src_dir}, | |
3883 | "${file_base}.c" ); | |
3884 | ||
3885 | $spec{obj_file} = File::Spec->catfile( $spec{src_dir}, | |
3886 | "${file_base}$cf->{obj_ext}" ); | |
3887 | ||
3888 | return \%spec; | |
3889 | } | |
3890 | ||
3891 | sub process_xs { | |
3892 | my ($self, $file) = @_; | |
3893 | my $cf = $self->config; # For convenience | |
3894 | ||
3895 | my $spec = $self->_infer_xs_spec($file); | |
3896 | ||
3897 | # File name, minus the suffix | |
3898 | (my $file_base = $file) =~ s/\.[^.]+$//; | |
3899 | ||
3900 | # .xs -> .c | |
3901 | $self->add_to_cleanup($spec->{c_file}); | |
3902 | ||
3903 | unless ($self->up_to_date($file, $spec->{c_file})) { | |
3904 | $self->compile_xs($file, outfile => $spec->{c_file}); | |
3905 | } | |
3906 | ||
3907 | # .c -> .o | |
3908 | my $v = $self->dist_version; | |
3909 | $self->compile_c($spec->{c_file}, | |
3910 | defines => {VERSION => qq{"$v"}, XS_VERSION => qq{"$v"}}); | |
3911 | ||
3912 | # archdir | |
3913 | File::Path::mkpath($spec->{archdir}, 0, 0777) unless -d $spec->{archdir}; | |
3914 | ||
3915 | # .xs -> .bs | |
3916 | $self->add_to_cleanup($spec->{bs_file}); | |
3917 | unless ($self->up_to_date($file, $spec->{bs_file})) { | |
3918 | require ExtUtils::Mkbootstrap; | |
3919 | $self->log_info("ExtUtils::Mkbootstrap::Mkbootstrap('$spec->{bs_file}')\n"); | |
3920 | ExtUtils::Mkbootstrap::Mkbootstrap($spec->{bs_file}); # Original had $BSLOADLIBS - what's that? | |
3921 | {my $fh = IO::File->new(">> $spec->{bs_file}")} # create | |
3922 | utime((time)x2, $spec->{bs_file}); # touch | |
3923 | } | |
3924 | ||
3925 | # .o -> .(a|bundle) | |
3926 | $self->link_c($spec->{archdir}, $file_base); | |
3927 | } | |
3928 | ||
3929 | sub do_system { | |
3930 | my ($self, @cmd) = @_; | |
3931 | $self->log_info("@cmd\n"); | |
3932 | return !system(@cmd); | |
3933 | } | |
3934 | ||
3935 | sub copy_if_modified { | |
3936 | my $self = shift; | |
3937 | my %args = (@_ > 3 | |
3938 | ? ( @_ ) | |
3939 | : ( from => shift, to_dir => shift, flatten => shift ) | |
3940 | ); | |
3941 | $args{verbose} = !$self->quiet | |
3942 | unless exists $args{verbose}; | |
3943 | ||
3944 | my $file = $args{from}; | |
3945 | unless (defined $file and length $file) { | |
3946 | die "No 'from' parameter given to copy_if_modified"; | |
3947 | } | |
3948 | ||
3949 | my $to_path; | |
3950 | if (defined $args{to} and length $args{to}) { | |
3951 | $to_path = $args{to}; | |
3952 | } elsif (defined $args{to_dir} and length $args{to_dir}) { | |
3953 | $to_path = File::Spec->catfile( $args{to_dir}, $args{flatten} | |
3954 | ? File::Basename::basename($file) | |
3955 | : $file ); | |
3956 | } else { | |
3957 | die "No 'to' or 'to_dir' parameter given to copy_if_modified"; | |
3958 | } | |
3959 | ||
3960 | return if $self->up_to_date($file, $to_path); # Already fresh | |
dc8021d3 SP |
3961 | |
3962 | $self->delete_filetree($to_path); # delete destination if exists | |
3963 | ||
bb4e9162 YST |
3964 | # Create parent directories |
3965 | File::Path::mkpath(File::Basename::dirname($to_path), 0, 0777); | |
3966 | ||
3967 | $self->log_info("$file -> $to_path\n") if $args{verbose}; | |
3968 | File::Copy::copy($file, $to_path) or die "Can't copy('$file', '$to_path'): $!"; | |
dc8021d3 | 3969 | # mode is read-only + (executable if source is executable) |
f943a5bf | 3970 | my $mode = 0444 | ( $self->is_executable($file) ? 0111 : 0 ); |
dc8021d3 SP |
3971 | chmod( $mode, $to_path ); |
3972 | ||
bb4e9162 YST |
3973 | return $to_path; |
3974 | } | |
3975 | ||
3976 | sub up_to_date { | |
3977 | my ($self, $source, $derived) = @_; | |
3978 | $source = [$source] unless ref $source; | |
3979 | $derived = [$derived] unless ref $derived; | |
3980 | ||
3981 | return 0 if grep {not -e} @$derived; | |
3982 | ||
3983 | my $most_recent_source = time / (24*60*60); | |
3984 | foreach my $file (@$source) { | |
3985 | unless (-e $file) { | |
3986 | $self->log_warn("Can't find source file $file for up-to-date check"); | |
3987 | next; | |
3988 | } | |
3989 | $most_recent_source = -M _ if -M _ < $most_recent_source; | |
3990 | } | |
3991 | ||
3992 | foreach my $derived (@$derived) { | |
3993 | return 0 if -M $derived > $most_recent_source; | |
3994 | } | |
3995 | return 1; | |
3996 | } | |
3997 | ||
3998 | sub dir_contains { | |
3999 | my ($self, $first, $second) = @_; | |
4000 | # File::Spec doesn't have an easy way to check whether one directory | |
4001 | # is inside another, unfortunately. | |
4002 | ||
4003 | ($first, $second) = map File::Spec->canonpath($_), ($first, $second); | |
4004 | my @first_dirs = File::Spec->splitdir($first); | |
4005 | my @second_dirs = File::Spec->splitdir($second); | |
4006 | ||
4007 | return 0 if @second_dirs < @first_dirs; | |
4008 | ||
4009 | my $is_same = ( File::Spec->case_tolerant | |
4010 | ? sub {lc(shift()) eq lc(shift())} | |
4011 | : sub {shift() eq shift()} ); | |
4012 | ||
4013 | while (@first_dirs) { | |
4014 | return 0 unless $is_same->(shift @first_dirs, shift @second_dirs); | |
4015 | } | |
4016 | ||
4017 | return 1; | |
4018 | } | |
4019 | ||
4020 | 1; | |
4021 | __END__ | |
4022 | ||
4023 | ||
4024 | =head1 NAME | |
4025 | ||
4026 | Module::Build::Base - Default methods for Module::Build | |
4027 | ||
4028 | =head1 SYNOPSIS | |
4029 | ||
4030 | Please see the Module::Build documentation. | |
4031 | ||
4032 | =head1 DESCRIPTION | |
4033 | ||
4034 | The C<Module::Build::Base> module defines the core functionality of | |
4035 | C<Module::Build>. Its methods may be overridden by any of the | |
4036 | platform-dependent modules in the C<Module::Build::Platform::> | |
4037 | namespace, but the intention here is to make this base module as | |
4038 | platform-neutral as possible. Nicely enough, Perl has several core | |
4039 | tools available in the C<File::> namespace for doing this, so the task | |
4040 | isn't very difficult. | |
4041 | ||
4042 | Please see the C<Module::Build> documentation for more details. | |
4043 | ||
4044 | =head1 AUTHOR | |
4045 | ||
4046 | Ken Williams <ken@cpan.org> | |
4047 | ||
4048 | =head1 COPYRIGHT | |
4049 | ||
4050 | Copyright (c) 2001-2005 Ken Williams. All rights reserved. | |
4051 | ||
4052 | This library is free software; you can redistribute it and/or | |
4053 | modify it under the same terms as Perl itself. | |
4054 | ||
4055 | =head1 SEE ALSO | |
4056 | ||
4057 | perl(1), Module::Build(3) | |
4058 | ||
4059 | =cut |