Upgrade to ExtUtils-Manifest-1.49.
[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 arg_defines {
78   my ($self, %args) = @_;
79   return map "-D$_=$args{$_}", keys %args;
80 }
81
82 sub compile {
83   my ($self, %args) = @_;
84   die "Missing 'source' argument to compile()" unless defined $args{source};
85   
86   my $cf = $self->{config}; # For convenience
87
88   $args{object_file} ||= $self->object_file($args{source});
89   
90   my @include_dirs = $self->arg_include_dirs
91     (@{$args{include_dirs} || []},
92      $self->perl_inc());
93   
94   my @defines = $self->arg_defines( %{$args{defines} || {}} );
95   
96   my @extra_compiler_flags = $self->split_like_shell($args{extra_compiler_flags});
97   my @cccdlflags = $self->split_like_shell($cf->{cccdlflags});
98   my @ccflags = $self->split_like_shell($cf->{ccflags});
99   my @optimize = $self->split_like_shell($cf->{optimize});
100   my @flags = (@include_dirs, @defines, @cccdlflags, @extra_compiler_flags,
101                $self->arg_nolink,
102                @ccflags, @optimize,
103                $self->arg_object_file($args{object_file}),
104               );
105   
106   my @cc = $self->split_like_shell($cf->{cc});
107   
108   $self->do_system(@cc, @flags, $args{source})
109     or die "error building $args{object_file} from '$args{source}'";
110
111   return $args{object_file};
112 }
113
114 sub have_compiler {
115   my ($self) = @_;
116   return $self->{have_compiler} if defined $self->{have_compiler};
117   
118   my $tmpfile = File::Spec->catfile(File::Spec->tmpdir, 'compilet.c');
119   {
120     local *FH;
121     open FH, "> $tmpfile" or die "Can't create $tmpfile: $!";
122     print FH "int boot_compilet() { return 1; }\n";
123     close FH;
124   }
125
126   my ($obj_file, @lib_files);
127   eval {
128     $obj_file = $self->compile(source => $tmpfile);
129     @lib_files = $self->link(objects => $obj_file, module_name => 'compilet');
130   };
131   warn $@ if $@;
132   my $result = $self->{have_compiler} = $@ ? 0 : 1;
133   
134   foreach (grep defined, $tmpfile, $obj_file, @lib_files) {
135     1 while unlink;
136   }
137   return $result;
138 }
139
140 sub lib_file {
141   my ($self, $dl_file) = @_;
142   $dl_file =~ s/\.[^.]+$//;
143   $dl_file =~ tr/"//d;
144   return "$dl_file.$self->{config}{dlext}";
145 }
146
147
148 sub exe_file {
149   my ($self, $dl_file) = @_;
150   $dl_file =~ s/\.[^.]+$//;
151   $dl_file =~ tr/"//d;
152   return "$dl_file$self->{config}{_exe}";
153 }
154
155 sub need_prelink { 0 }
156
157 sub extra_link_args_after_prelink { return }
158
159 sub prelink {
160   my ($self, %args) = @_;
161   
162   ($args{dl_file} = $args{dl_name}) =~ s/.*::// unless $args{dl_file};
163   
164   require ExtUtils::Mksymlists;
165   ExtUtils::Mksymlists::Mksymlists( # dl. abbrev for dynamic library
166     DL_VARS  => $args{dl_vars}      || [],
167     DL_FUNCS => $args{dl_funcs}     || {},
168     FUNCLIST => $args{dl_func_list} || [],
169     IMPORTS  => $args{dl_imports}   || {},
170     NAME     => $args{dl_name},
171     DLBASE   => $args{dl_base},
172     FILE     => $args{dl_file},
173     VERSION  => (defined $args{dl_version} ? $args{dl_version} : '0.0'),
174   );
175   
176   # Mksymlists will create one of these files
177   return grep -e, map "$args{dl_file}.$_", qw(ext def opt);
178 }
179
180 sub link {
181   my ($self, %args) = @_;
182   return $self->_do_link('lib_file', lddl => 1, %args);
183 }
184
185 sub link_executable {
186   my ($self, %args) = @_;
187   return $self->_do_link('exe_file', lddl => 0, %args);
188 }
189
190 sub _do_link {
191   my ($self, $type, %args) = @_;
192
193   my $cf = $self->{config}; # For convenience
194   
195   my $objects = delete $args{objects};
196   $objects = [$objects] unless ref $objects;
197   my $out = $args{$type} || $self->$type($objects->[0]);
198   
199   my @temp_files;
200   @temp_files =
201     $self->prelink(%args,
202                    dl_name => $args{module_name}) if $args{lddl} && $self->need_prelink;
203   
204   my @linker_flags = ($self->split_like_shell($args{extra_linker_flags}),
205                       $self->extra_link_args_after_prelink(%args, dl_name => $args{module_name},
206                                                            prelink_res => \@temp_files));
207
208   my @output = $args{lddl} ? $self->arg_share_object_file($out) : $self->arg_exec_file($out);
209   my @shrp = $self->split_like_shell($cf->{shrpenv});
210   my @ld = $self->split_like_shell($cf->{ld});
211   
212   $self->do_system(@shrp, @ld, @output, @$objects, @linker_flags)
213     or die "error building $out from @$objects";
214   
215   return wantarray ? ($out, @temp_files) : $out;
216 }
217
218
219 sub do_system {
220   my ($self, @cmd) = @_;
221   print "@cmd\n" if !$self->{quiet};
222   return !system(@cmd);
223 }
224
225 sub split_like_shell {
226   my ($self, $string) = @_;
227   
228   return () unless defined($string);
229   return @$string if UNIVERSAL::isa($string, 'ARRAY');
230   $string =~ s/^\s+|\s+$//g;
231   return () unless length($string);
232   
233   return Text::ParseWords::shellwords($string);
234 }
235
236 # if building perl, perl's main source directory
237 sub perl_src {
238   # N.B. makemaker actually searches regardless of PERL_CORE, but
239   # only squawks at not finding it if PERL_CORE is set
240
241   return unless $ENV{PERL_CORE};
242
243   my $Updir = File::Spec->updir;
244   my $dir   = File::Spec->curdir;
245
246   # Try up to 5 levels upwards
247   for (0..5) {
248     if (
249         -f File::Spec->catfile($dir,"config_h.SH")
250         &&
251         -f File::Spec->catfile($dir,"perl.h")
252         &&
253         -f File::Spec->catfile($dir,"lib","Exporter.pm")
254        ) {
255       return $dir;
256     }
257
258     $dir = File::Spec->catdir($dir, $Updir);
259   }
260
261   warn "PERL_CORE is set but I can't find your perl source!\n";
262   return ''; # return empty string if $ENV{PERL_CORE} but can't find dir ???
263 }
264
265 # directory of perl's include files
266 sub perl_inc {
267   my $self = shift;
268
269   $self->perl_src() || File::Spec->catdir($self->{config}{archlibexp},"CORE");
270 }
271
272 sub DESTROY {
273   my $self = shift;
274   $self->cleanup();
275 }
276
277 1;