Make CBuilder and ParseXS clean up their temp test files
[p5sagit/p5-mst-13.2.git] / lib / ExtUtils / CBuilder / Base.pm
1 package ExtUtils::CBuilder::Base;
2
3 use strict;
4 use File::Spec;
5 use File::Basename;
6 use Config;
7 use Text::ParseWords;
8
9 use vars qw($VERSION);
10 $VERSION = '0.12';
11
12 sub new {
13   my $class = shift;
14   my $self = bless {@_}, $class;
15
16   $self->{properties}{perl} = $class->find_perl_interpreter
17     or warn "Warning: Can't locate your perl binary";
18
19   while (my ($k,$v) = each %Config) {
20     $self->{config}{$k} = $v unless exists $self->{config}{$k};
21   }
22   return $self;
23 }
24
25 sub find_perl_interpreter {
26   my $perl;
27   File::Spec->file_name_is_absolute($perl = $^X)
28     or -f ($perl = $Config::Config{perlpath})
29     or ($perl = $^X);
30   return $perl;
31 }
32
33 sub add_to_cleanup {
34   my $self = shift;
35   foreach (@_) {
36     $self->{files_to_clean}{$_} = 1;
37   }
38 }
39
40 sub cleanup {
41   my $self = shift;
42   foreach my $file (keys %{$self->{files_to_clean}}) {
43     unlink $file;
44   }
45 }
46
47 sub object_file {
48   my ($self, $filename) = @_;
49
50   # File name, minus the suffix
51   (my $file_base = $filename) =~ s/\.[^.]+$//;
52   return "$file_base$self->{config}{obj_ext}";
53 }
54
55 sub arg_include_dirs {
56   my $self = shift;
57   return map {"-I$_"} @_;
58 }
59
60 sub arg_nolink { '-c' }
61
62 sub arg_object_file {
63   my ($self, $file) = @_;
64   return ('-o', $file);
65 }
66
67 sub arg_share_object_file {
68   my ($self, $file) = @_;
69   return ($self->split_like_shell($self->{config}{lddlflags}), '-o', $file);
70 }
71
72 sub arg_exec_file {
73   my ($self, $file) = @_;
74   return ('-o', $file);
75 }
76
77 sub compile {
78   my ($self, %args) = @_;
79   die "Missing 'source' argument to compile()" unless defined $args{source};
80   
81   my $cf = $self->{config}; # For convenience
82
83   $args{object_file} ||= $self->object_file($args{source});
84   
85   my @include_dirs = $self->arg_include_dirs
86     (@{$args{include_dirs} || []},
87      $self->perl_inc());
88   
89   my @extra_compiler_flags = $self->split_like_shell($args{extra_compiler_flags});
90   my @cccdlflags = $self->split_like_shell($cf->{cccdlflags});
91   my @ccflags = $self->split_like_shell($cf->{ccflags});
92   my @optimize = $self->split_like_shell($cf->{optimize});
93   my @flags = (@include_dirs, @cccdlflags, @extra_compiler_flags,
94                $self->arg_nolink,
95                @ccflags, @optimize,
96                $self->arg_object_file($args{object_file}),
97               );
98   
99   my @cc = $self->split_like_shell($cf->{cc});
100   
101   $self->do_system(@cc, @flags, $args{source})
102     or die "error building $args{object_file} from '$args{source}'";
103
104   return $args{object_file};
105 }
106
107 sub have_compiler {
108   my ($self) = @_;
109   return $self->{have_compiler} if defined $self->{have_compiler};
110   
111   my $tmpfile = File::Spec->catfile(File::Spec->tmpdir, 'compilet.c');
112   {
113     local *FH;
114     open FH, "> $tmpfile" or die "Can't create $tmpfile: $!";
115     print FH "int boot_compilet() { return 1; }\n";
116     close FH;
117   }
118
119   my ($obj_file, @lib_files);
120   eval {
121     $obj_file = $self->compile(source => $tmpfile);
122     @lib_files = $self->link(objects => $obj_file, module_name => 'compilet');
123   };
124   warn $@ if $@;
125   my $result = $self->{have_compiler} = $@ ? 0 : 1;
126   
127   foreach (grep defined, $tmpfile, $obj_file, @lib_files) {
128     1 while unlink;
129   }
130   return $result;
131 }
132
133 sub lib_file {
134   my ($self, $dl_file) = @_;
135   $dl_file =~ s/\.[^.]+$//;
136   $dl_file =~ tr/"//d;
137   return "$dl_file.$self->{config}{dlext}";
138 }
139
140
141 sub exe_file {
142   my ($self, $dl_file) = @_;
143   $dl_file =~ s/\.[^.]+$//;
144   $dl_file =~ tr/"//d;
145   return "$dl_file$self->{config}{_exe}";
146 }
147
148 sub need_prelink { 0 }
149
150 sub prelink {
151   my ($self, %args) = @_;
152   
153   ($args{dl_file} = $args{dl_name}) =~ s/.*::// unless $args{dl_file};
154   
155   require ExtUtils::Mksymlists;
156   ExtUtils::Mksymlists::Mksymlists( # dl. abbrev for dynamic library
157     DL_VARS  => $args{dl_vars}      || [],
158     DL_FUNCS => $args{dl_funcs}     || {},
159     FUNCLIST => $args{dl_func_list} || [],
160     IMPORTS  => $args{dl_imports}   || {},
161     NAME     => $args{dl_name},
162     DLBASE   => $args{dl_base},
163     FILE     => $args{dl_file},
164   );
165   
166   # Mksymlists will create one of these files
167   return grep -e, map "$args{dl_file}.$_", qw(ext def opt);
168 }
169
170 sub link {
171   my ($self, %args) = @_;
172   return $self->_do_link('lib_file', lddl => 1, %args);
173 }
174
175 sub link_executable {
176   my ($self, %args) = @_;
177   return $self->_do_link('exe_file', lddl => 0, %args);
178 }
179                                    
180 sub _do_link {
181   my ($self, $type, %args) = @_;
182
183   my $cf = $self->{config}; # For convenience
184   
185   my $objects = delete $args{objects};
186   $objects = [$objects] unless ref $objects;
187   my $out = $args{$type} || $self->$type($objects->[0]);
188   
189   my @temp_files;
190   @temp_files =
191     $self->prelink(%args,
192                    dl_name => $args{module_name}) if $args{lddl} && $self->need_prelink;
193   
194   my @linker_flags = $self->split_like_shell($args{extra_linker_flags});
195   my @output = $args{lddl} ? $self->arg_share_object_file($out) : $self->arg_exec_file($out);
196   my @shrp = $self->split_like_shell($cf->{shrpenv});
197   my @ld = $self->split_like_shell($cf->{ld});
198   $self->do_system(@shrp, @ld, @output, @$objects, @linker_flags)
199     or die "error building $out from @$objects";
200   
201   return wantarray ? ($out, @temp_files) : $out;
202 }
203
204
205 sub do_system {
206   my ($self, @cmd) = @_;
207   print "@cmd\n" if !$self->{quiet};
208   return !system(@cmd);
209 }
210
211 sub split_like_shell {
212   my ($self, $string) = @_;
213   
214   return () unless defined($string);
215   return @$string if UNIVERSAL::isa($string, 'ARRAY');
216   $string =~ s/^\s+|\s+$//g;
217   return () unless length($string);
218   
219   return Text::ParseWords::shellwords($string);
220 }
221
222 # if building perl, perl's main source directory
223 sub perl_src {
224   # N.B. makemaker actually searches regardless of PERL_CORE, but
225   # only squawks at not finding it if PERL_CORE is set
226
227   return unless $ENV{PERL_CORE};
228
229   my $Updir  = File::Spec->updir;
230   my $dir = $Updir;
231
232   # Try up to 5 levels upwards
233   for (1..5) {
234     if (
235         -f File::Spec->catfile($dir,"config_h.SH")
236         &&
237         -f File::Spec->catfile($dir,"perl.h")
238         &&
239         -f File::Spec->catfile($dir,"lib","Exporter.pm")
240        ) {
241       return $dir;
242     }
243
244     $dir = File::Spec->catdir($dir, $Updir);
245   }
246   
247   warn "PERL_CORE is set but I can't find your perl source!\n";
248   return;
249 }
250
251 # directory of perl's include files
252 sub perl_inc {
253   my $self = shift;
254
255   $self->perl_src() || File::Spec->catdir($self->{config}{archlibexp},"CORE");
256 }
257
258 sub DESTROY {
259   my $self = shift;
260   $self->cleanup();
261 }
262
263 1;