xref: /openbsd-src/gnu/usr.bin/perl/dist/ExtUtils-CBuilder/lib/ExtUtils/CBuilder/Base.pm (revision 3d61058aa5c692477b6d18acfbbdb653a9930ff9)
1898184e3Ssthenpackage ExtUtils::CBuilder::Base;
2898184e3Ssthenuse strict;
3b8851fccSafresh1use warnings;
4898184e3Ssthenuse File::Spec;
5898184e3Ssthenuse File::Basename;
6898184e3Ssthenuse Cwd ();
7898184e3Ssthenuse Config;
8898184e3Ssthenuse Text::ParseWords;
9898184e3Ssthenuse IPC::Cmd qw(can_run);
10898184e3Ssthenuse File::Temp qw(tempfile);
11898184e3Ssthen
12*3d61058aSafresh1our $VERSION = '0.280240'; # VERSION
139f11ffb7Safresh1
14898184e3Ssthen# More details about C/C++ compilers:
15898184e3Ssthen# http://developers.sun.com/sunstudio/documentation/product/compiler.jsp
16898184e3Ssthen# http://gcc.gnu.org/
17898184e3Ssthen# http://publib.boulder.ibm.com/infocenter/comphelp/v101v121/index.jsp
18898184e3Ssthen# http://msdn.microsoft.com/en-us/vstudio/default.aspx
19898184e3Ssthen
20898184e3Ssthenmy %cc2cxx = (
21898184e3Ssthen    # first line order is important to support wrappers like in pkgsrc
22898184e3Ssthen    cc => [ 'c++', 'CC', 'aCC', 'cxx', ], # Sun Studio, HP ANSI C/C++ Compilers
23898184e3Ssthen    gcc => [ 'g++' ], # GNU Compiler Collection
24898184e3Ssthen    xlc => [ 'xlC' ], # IBM C/C++ Set, xlc without thread-safety
25898184e3Ssthen    xlc_r => [ 'xlC_r' ], # IBM C/C++ Set, xlc with thread-safety
26898184e3Ssthen    cl    => [ 'cl' ], # Microsoft Visual Studio
27898184e3Ssthen);
28898184e3Ssthen
29898184e3Ssthensub new {
30898184e3Ssthen  my $class = shift;
31898184e3Ssthen  my $self = bless {@_}, $class;
32898184e3Ssthen
33898184e3Ssthen  $self->{properties}{perl} = $class->find_perl_interpreter
34898184e3Ssthen    or warn "Warning: Can't locate your perl binary";
35898184e3Ssthen
36898184e3Ssthen  while (my ($k,$v) = each %Config) {
37898184e3Ssthen    $self->{config}{$k} = $v unless exists $self->{config}{$k};
38898184e3Ssthen  }
39898184e3Ssthen  $self->{config}{cc} = $ENV{CC} if defined $ENV{CC};
40898184e3Ssthen  $self->{config}{ccflags} = join(" ", $self->{config}{ccflags}, $ENV{CFLAGS})
41898184e3Ssthen     if defined $ENV{CFLAGS};
42898184e3Ssthen  $self->{config}{cxx} = $ENV{CXX} if defined $ENV{CXX};
43898184e3Ssthen  $self->{config}{cxxflags} = $ENV{CXXFLAGS} if defined $ENV{CXXFLAGS};
44898184e3Ssthen  $self->{config}{ld} = $ENV{LD} if defined $ENV{LD};
45898184e3Ssthen  $self->{config}{ldflags} = join(" ", $self->{config}{ldflags}, $ENV{LDFLAGS})
46898184e3Ssthen     if defined $ENV{LDFLAGS};
47898184e3Ssthen
48898184e3Ssthen  unless ( exists $self->{config}{cxx} ) {
499f11ffb7Safresh1
509f11ffb7Safresh1    my ($ccbase, $ccpath, $ccsfx ) = fileparse($self->{config}{cc}, qr/\.[^.]*/);
519f11ffb7Safresh1
529f11ffb7Safresh1    ## If the path is just "cc", fileparse returns $ccpath as "./"
53b46d8ef2Safresh1    $ccpath = "" if $self->{config}{cc} =~ /^\Q$ccbase$ccsfx\E$/;
549f11ffb7Safresh1
55898184e3Ssthen    foreach my $cxx (@{$cc2cxx{$ccbase}}) {
569f11ffb7Safresh1      my $cxx1 = File::Spec->catfile( $ccpath, $cxx . $ccsfx);
579f11ffb7Safresh1
589f11ffb7Safresh1      if( can_run( $cxx1 ) ) {
599f11ffb7Safresh1        $self->{config}{cxx} = $cxx1;
60898184e3Ssthen	last;
61898184e3Ssthen      }
629f11ffb7Safresh1      my $cxx2 = $cxx . $ccsfx;
639f11ffb7Safresh1
649f11ffb7Safresh1      if( can_run( $cxx2 ) ) {
659f11ffb7Safresh1        $self->{config}{cxx} = $cxx2;
66898184e3Ssthen	last;
67898184e3Ssthen      }
689f11ffb7Safresh1
69898184e3Ssthen      if( can_run( $cxx ) ) {
70898184e3Ssthen        $self->{config}{cxx} = $cxx;
71898184e3Ssthen	last;
72898184e3Ssthen      }
73898184e3Ssthen    }
74898184e3Ssthen    unless ( exists $self->{config}{cxx} ) {
75898184e3Ssthen      $self->{config}{cxx} = $self->{config}{cc};
76898184e3Ssthen      my $cflags = $self->{config}{ccflags};
77898184e3Ssthen      $self->{config}{cxxflags} = '-x c++';
78898184e3Ssthen      $self->{config}{cxxflags} .= " $cflags" if defined $cflags;
79898184e3Ssthen    }
80898184e3Ssthen  }
81898184e3Ssthen
82898184e3Ssthen  return $self;
83898184e3Ssthen}
84898184e3Ssthen
85898184e3Ssthensub find_perl_interpreter {
86898184e3Ssthen  my $perl;
87898184e3Ssthen  File::Spec->file_name_is_absolute($perl = $^X)
88898184e3Ssthen    or -f ($perl = $Config::Config{perlpath})
89898184e3Ssthen    or ($perl = $^X); # XXX how about using IPC::Cmd::can_run here?
90898184e3Ssthen  return $perl;
91898184e3Ssthen}
92898184e3Ssthen
93898184e3Ssthensub add_to_cleanup {
94898184e3Ssthen  my $self = shift;
95898184e3Ssthen  foreach (@_) {
96898184e3Ssthen    $self->{files_to_clean}{$_} = 1;
97898184e3Ssthen  }
98898184e3Ssthen}
99898184e3Ssthen
100898184e3Ssthensub cleanup {
101898184e3Ssthen  my $self = shift;
102898184e3Ssthen  foreach my $file (keys %{$self->{files_to_clean}}) {
103898184e3Ssthen    unlink $file;
104898184e3Ssthen  }
105898184e3Ssthen}
106898184e3Ssthen
107898184e3Ssthensub get_config {
108898184e3Ssthen    return %{ $_[0]->{config} };
109898184e3Ssthen}
110898184e3Ssthen
111898184e3Ssthensub object_file {
112898184e3Ssthen  my ($self, $filename) = @_;
113898184e3Ssthen
114898184e3Ssthen  # File name, minus the suffix
115898184e3Ssthen  (my $file_base = $filename) =~ s/\.[^.]+$//;
116898184e3Ssthen  return "$file_base$self->{config}{obj_ext}";
117898184e3Ssthen}
118898184e3Ssthen
119898184e3Ssthensub arg_include_dirs {
120898184e3Ssthen  my $self = shift;
121898184e3Ssthen  return map {"-I$_"} @_;
122898184e3Ssthen}
123898184e3Ssthen
124898184e3Ssthensub arg_nolink { '-c' }
125898184e3Ssthen
126898184e3Ssthensub arg_object_file {
127898184e3Ssthen  my ($self, $file) = @_;
128898184e3Ssthen  return ('-o', $file);
129898184e3Ssthen}
130898184e3Ssthen
131898184e3Ssthensub arg_share_object_file {
132898184e3Ssthen  my ($self, $file) = @_;
133898184e3Ssthen  return ($self->split_like_shell($self->{config}{lddlflags}), '-o', $file);
134898184e3Ssthen}
135898184e3Ssthen
136898184e3Ssthensub arg_exec_file {
137898184e3Ssthen  my ($self, $file) = @_;
138898184e3Ssthen  return ('-o', $file);
139898184e3Ssthen}
140898184e3Ssthen
141898184e3Ssthensub arg_defines {
142898184e3Ssthen  my ($self, %args) = @_;
143b8851fccSafresh1  return map "-D$_=$args{$_}", sort keys %args;
144898184e3Ssthen}
145898184e3Ssthen
146898184e3Ssthensub compile {
147898184e3Ssthen  my ($self, %args) = @_;
148898184e3Ssthen  die "Missing 'source' argument to compile()" unless defined $args{source};
149898184e3Ssthen
150898184e3Ssthen  my $cf = $self->{config}; # For convenience
151898184e3Ssthen
152898184e3Ssthen  my $object_file = $args{object_file}
153898184e3Ssthen    ? $args{object_file}
154898184e3Ssthen    : $self->object_file($args{source});
155898184e3Ssthen
156898184e3Ssthen  my $include_dirs_ref =
157898184e3Ssthen    (exists($args{include_dirs}) && ref($args{include_dirs}) ne "ARRAY")
158898184e3Ssthen      ? [ $args{include_dirs} ]
159898184e3Ssthen      : $args{include_dirs};
160898184e3Ssthen  my @include_dirs = $self->arg_include_dirs(
161898184e3Ssthen    @{ $include_dirs_ref || [] },
162898184e3Ssthen    $self->perl_inc(),
163898184e3Ssthen  );
164898184e3Ssthen
165898184e3Ssthen  my @defines = $self->arg_defines( %{$args{defines} || {}} );
166898184e3Ssthen
167898184e3Ssthen  my @extra_compiler_flags =
168898184e3Ssthen    $self->split_like_shell($args{extra_compiler_flags});
169898184e3Ssthen  my @cccdlflags = $self->split_like_shell($cf->{cccdlflags});
170898184e3Ssthen  my @ccflags = $self->split_like_shell($args{'C++'} ? $cf->{cxxflags} : $cf->{ccflags});
171898184e3Ssthen  my @optimize = $self->split_like_shell($cf->{optimize});
172898184e3Ssthen  my @flags = (
173898184e3Ssthen    @include_dirs,
174898184e3Ssthen    @defines,
175898184e3Ssthen    @cccdlflags,
176898184e3Ssthen    @extra_compiler_flags,
177898184e3Ssthen    $self->arg_nolink,
178898184e3Ssthen    @ccflags,
179898184e3Ssthen    @optimize,
180898184e3Ssthen    $self->arg_object_file($object_file),
181898184e3Ssthen  );
182898184e3Ssthen  my @cc = $self->split_like_shell($args{'C++'} ? $cf->{cxx} : $cf->{cc});
183898184e3Ssthen
184898184e3Ssthen  $self->do_system(@cc, @flags, $args{source})
185898184e3Ssthen    or die "error building $object_file from '$args{source}'";
186898184e3Ssthen
187898184e3Ssthen  return $object_file;
188898184e3Ssthen}
189898184e3Ssthen
190898184e3Ssthensub have_compiler {
191898184e3Ssthen  my ($self, $is_cplusplus) = @_;
192898184e3Ssthen  my $have_compiler_flag = $is_cplusplus ? "have_cxx" : "have_cc";
193898184e3Ssthen  my $suffix = $is_cplusplus ? ".cc" : ".c";
194898184e3Ssthen  return $self->{$have_compiler_flag} if defined $self->{$have_compiler_flag};
195898184e3Ssthen
196898184e3Ssthen  my $result;
197898184e3Ssthen  my $attempts = 3;
198898184e3Ssthen  # tmpdir has issues for some people so fall back to current dir
199898184e3Ssthen
200898184e3Ssthen  # don't clobber existing files (rare, but possible)
201898184e3Ssthen  my ( $FH, $tmpfile ) = tempfile( "compilet-XXXXX", SUFFIX => $suffix );
202898184e3Ssthen  binmode $FH;
203898184e3Ssthen
204898184e3Ssthen  if ( $is_cplusplus ) {
205*3d61058aSafresh1    print $FH q<namespace Bogus { extern "C" int boot_compilet() { return 1; } };> . "\n";
206898184e3Ssthen  }
207898184e3Ssthen  else {
208*3d61058aSafresh1    # Use extern "C" if "cc" was set to a C++ compiler.
209*3d61058aSafresh1    print $FH <<EOF;
210*3d61058aSafresh1#ifdef __cplusplus
211*3d61058aSafresh1extern "C"
212*3d61058aSafresh1#endif
213*3d61058aSafresh1int boot_compilet(void) { return 1; }
214*3d61058aSafresh1EOF
215898184e3Ssthen  }
216898184e3Ssthen  close $FH;
217898184e3Ssthen
218898184e3Ssthen  my ($obj_file, @lib_files);
219898184e3Ssthen  eval {
220898184e3Ssthen    local $^W = 0;
221898184e3Ssthen    local $self->{quiet} = 1;
222898184e3Ssthen    $obj_file = $self->compile('C++' => $is_cplusplus, source => $tmpfile);
223898184e3Ssthen    @lib_files = $self->link(objects => $obj_file, module_name => 'compilet');
224898184e3Ssthen  };
225898184e3Ssthen  $result = $@ ? 0 : 1;
226898184e3Ssthen
227898184e3Ssthen  foreach (grep defined, $tmpfile, $obj_file, @lib_files) {
228898184e3Ssthen    1 while unlink;
229898184e3Ssthen  }
230898184e3Ssthen
231898184e3Ssthen  return $self->{$have_compiler_flag} = $result;
232898184e3Ssthen}
233898184e3Ssthen
234898184e3Ssthensub have_cplusplus {
235898184e3Ssthen  push @_, 1;
236898184e3Ssthen  goto &have_compiler;
237898184e3Ssthen}
238898184e3Ssthen
239898184e3Ssthensub lib_file {
2406fb12b70Safresh1  my ($self, $dl_file, %args) = @_;
241898184e3Ssthen  $dl_file =~ s/\.[^.]+$//;
242898184e3Ssthen  $dl_file =~ tr/"//d;
2436fb12b70Safresh1
2446fb12b70Safresh1  if (defined $args{module_name} and length $args{module_name}) {
2456fb12b70Safresh1    # Need to create with the same name as DynaLoader will load with.
2466fb12b70Safresh1    require DynaLoader;
2476fb12b70Safresh1    if (defined &DynaLoader::mod2fname) {
2486fb12b70Safresh1      my $lib = DynaLoader::mod2fname([split /::/, $args{module_name}]);
2496fb12b70Safresh1      my ($dev, $lib_dir, undef) = File::Spec->splitpath($dl_file);
2506fb12b70Safresh1      $dl_file = File::Spec->catpath($dev, $lib_dir, $lib);
2516fb12b70Safresh1    }
2526fb12b70Safresh1  }
2536fb12b70Safresh1
2546fb12b70Safresh1  $dl_file .= ".$self->{config}{dlext}";
2556fb12b70Safresh1
2566fb12b70Safresh1  return $dl_file;
257898184e3Ssthen}
258898184e3Ssthen
259898184e3Ssthen
260898184e3Ssthensub exe_file {
261898184e3Ssthen  my ($self, $dl_file) = @_;
262898184e3Ssthen  $dl_file =~ s/\.[^.]+$//;
263898184e3Ssthen  $dl_file =~ tr/"//d;
264898184e3Ssthen  return "$dl_file$self->{config}{_exe}";
265898184e3Ssthen}
266898184e3Ssthen
267898184e3Ssthensub need_prelink { 0 }
268898184e3Ssthen
269898184e3Ssthensub extra_link_args_after_prelink { return }
270898184e3Ssthen
271898184e3Ssthensub prelink {
272898184e3Ssthen  my ($self, %args) = @_;
273898184e3Ssthen
274898184e3Ssthen  my ($dl_file_out, $mksymlists_args) = _prepare_mksymlists_args(\%args);
275898184e3Ssthen
276898184e3Ssthen  require ExtUtils::Mksymlists;
277898184e3Ssthen  # dl. abbrev for dynamic library
278898184e3Ssthen  ExtUtils::Mksymlists::Mksymlists( %{ $mksymlists_args } );
279898184e3Ssthen
280898184e3Ssthen  # Mksymlists will create one of these files
281898184e3Ssthen  return grep -e, map "$dl_file_out.$_", qw(ext def opt);
282898184e3Ssthen}
283898184e3Ssthen
284898184e3Ssthensub _prepare_mksymlists_args {
285898184e3Ssthen  my $args = shift;
286898184e3Ssthen  ($args->{dl_file} = $args->{dl_name}) =~ s/.*::// unless $args->{dl_file};
287898184e3Ssthen
288898184e3Ssthen  my %mksymlists_args = (
289898184e3Ssthen    DL_VARS  => $args->{dl_vars}      || [],
290898184e3Ssthen    DL_FUNCS => $args->{dl_funcs}     || {},
291898184e3Ssthen    FUNCLIST => $args->{dl_func_list} || [],
292898184e3Ssthen    IMPORTS  => $args->{dl_imports}   || {},
293898184e3Ssthen    NAME     => $args->{dl_name},    # Name of the Perl module
294898184e3Ssthen    DLBASE   => $args->{dl_base},    # Basename of DLL file
295898184e3Ssthen    FILE     => $args->{dl_file},    # Dir + Basename of symlist file
296898184e3Ssthen    VERSION  => (defined $args->{dl_version} ? $args->{dl_version} : '0.0'),
297898184e3Ssthen  );
298898184e3Ssthen  return ($args->{dl_file}, \%mksymlists_args);
299898184e3Ssthen}
300898184e3Ssthen
301898184e3Ssthensub link {
302898184e3Ssthen  my ($self, %args) = @_;
303898184e3Ssthen  return $self->_do_link('lib_file', lddl => 1, %args);
304898184e3Ssthen}
305898184e3Ssthen
306898184e3Ssthensub link_executable {
307898184e3Ssthen  my ($self, %args) = @_;
308898184e3Ssthen  return $self->_do_link('exe_file', lddl => 0, %args);
309898184e3Ssthen}
310898184e3Ssthen
311898184e3Ssthensub _do_link {
312898184e3Ssthen  my ($self, $type, %args) = @_;
313898184e3Ssthen
314898184e3Ssthen  my $cf = $self->{config}; # For convenience
315898184e3Ssthen
316898184e3Ssthen  my $objects = delete $args{objects};
317898184e3Ssthen  $objects = [$objects] unless ref $objects;
3186fb12b70Safresh1  my $out = $args{$type} || $self->$type($objects->[0], %args);
319898184e3Ssthen
320898184e3Ssthen  my @temp_files;
321898184e3Ssthen  @temp_files =
322898184e3Ssthen    $self->prelink(%args, dl_name => $args{module_name})
323898184e3Ssthen      if $args{lddl} && $self->need_prelink;
324898184e3Ssthen
325898184e3Ssthen  my @linker_flags = (
326898184e3Ssthen    $self->split_like_shell($args{extra_linker_flags}),
327898184e3Ssthen    $self->extra_link_args_after_prelink(
328898184e3Ssthen       %args, dl_name => $args{module_name}, prelink_res => \@temp_files
329898184e3Ssthen    )
330898184e3Ssthen  );
331898184e3Ssthen
332898184e3Ssthen  my @output = $args{lddl}
333898184e3Ssthen    ? $self->arg_share_object_file($out)
334898184e3Ssthen    : $self->arg_exec_file($out);
335898184e3Ssthen  my @shrp = $self->split_like_shell($cf->{shrpenv});
336898184e3Ssthen  my @ld = $self->split_like_shell($cf->{ld});
337898184e3Ssthen
338898184e3Ssthen  $self->do_system(@shrp, @ld, @output, @$objects, @linker_flags)
339898184e3Ssthen    or die "error building $out from @$objects";
340898184e3Ssthen
341898184e3Ssthen  return wantarray ? ($out, @temp_files) : $out;
342898184e3Ssthen}
343898184e3Ssthen
344e0680481Safresh1sub quote_literal {
345e0680481Safresh1  my ($self, $string) = @_;
346e0680481Safresh1
347e0680481Safresh1  if (length $string && $string !~ /[^a-zA-Z0-9,._+@%\/-]/) {
348e0680481Safresh1    return $string;
349e0680481Safresh1  }
350e0680481Safresh1
351e0680481Safresh1  $string =~ s{'}{'\\''}g;
352e0680481Safresh1
353e0680481Safresh1  return "'$string'";
354e0680481Safresh1}
355898184e3Ssthen
356898184e3Ssthensub do_system {
357898184e3Ssthen  my ($self, @cmd) = @_;
358e0680481Safresh1  if (!$self->{quiet}) {
359e0680481Safresh1    my $full = join ' ', map $self->quote_literal($_), @cmd;
360e0680481Safresh1    print $full . "\n";
361e0680481Safresh1  }
362898184e3Ssthen  return !system(@cmd);
363898184e3Ssthen}
364898184e3Ssthen
365898184e3Ssthensub split_like_shell {
366898184e3Ssthen  my ($self, $string) = @_;
367898184e3Ssthen
368898184e3Ssthen  return () unless defined($string);
369898184e3Ssthen  return @$string if UNIVERSAL::isa($string, 'ARRAY');
370898184e3Ssthen  $string =~ s/^\s+|\s+$//g;
371898184e3Ssthen  return () unless length($string);
372898184e3Ssthen
373898184e3Ssthen  # Text::ParseWords replaces all 'escaped' characters with themselves, which completely
374898184e3Ssthen  # breaks paths under windows. As such, we forcibly replace backwards slashes with forward
375898184e3Ssthen  # slashes on windows.
376898184e3Ssthen  $string =~ s@\\@/@g if $^O eq 'MSWin32';
377898184e3Ssthen
378898184e3Ssthen  return Text::ParseWords::shellwords($string);
379898184e3Ssthen}
380898184e3Ssthen
381898184e3Ssthen# if building perl, perl's main source directory
382898184e3Ssthensub perl_src {
383898184e3Ssthen  # N.B. makemaker actually searches regardless of PERL_CORE, but
384898184e3Ssthen  # only squawks at not finding it if PERL_CORE is set
385898184e3Ssthen
386898184e3Ssthen  return unless $ENV{PERL_CORE};
387898184e3Ssthen
388898184e3Ssthen  my $Updir = File::Spec->updir;
389898184e3Ssthen  my $dir   = File::Spec->curdir;
390898184e3Ssthen
391898184e3Ssthen  # Try up to 5 levels upwards
392898184e3Ssthen  for (0..10) {
393898184e3Ssthen    if (
394898184e3Ssthen      -f File::Spec->catfile($dir,"config_h.SH")
395898184e3Ssthen      &&
396898184e3Ssthen      -f File::Spec->catfile($dir,"perl.h")
397898184e3Ssthen      &&
398898184e3Ssthen      -f File::Spec->catfile($dir,"lib","Exporter.pm")
399898184e3Ssthen    ) {
400898184e3Ssthen      return Cwd::realpath( $dir );
401898184e3Ssthen    }
402898184e3Ssthen
403898184e3Ssthen    $dir = File::Spec->catdir($dir, $Updir);
404898184e3Ssthen  }
405898184e3Ssthen
406898184e3Ssthen  warn "PERL_CORE is set but I can't find your perl source!\n";
407898184e3Ssthen  return ''; # return empty string if $ENV{PERL_CORE} but can't find dir ???
408898184e3Ssthen}
409898184e3Ssthen
410898184e3Ssthen# directory of perl's include files
411898184e3Ssthensub perl_inc {
412898184e3Ssthen  my $self = shift;
413898184e3Ssthen
414898184e3Ssthen  $self->perl_src() || File::Spec->catdir($self->{config}{archlibexp},"CORE");
415898184e3Ssthen}
416898184e3Ssthen
417898184e3Ssthensub DESTROY {
418898184e3Ssthen  my $self = shift;
419898184e3Ssthen  local($., $@, $!, $^E, $?);
420898184e3Ssthen  $self->cleanup();
421898184e3Ssthen}
422898184e3Ssthen
423898184e3Ssthen1;
424898184e3Ssthen
425898184e3Ssthen# vim: ts=2 sw=2 et:
426