Spaces:
Running
Running
package ExtUtils::Embed; | |
require Exporter; | |
use Config; | |
require File::Spec; | |
our ( @Extensions, $opt_o, $opt_s ); | |
use strict; | |
# This is not a dual-life module, so no need for development version numbers | |
our $VERSION = '1.35'; | |
our @ISA = qw(Exporter); | |
our @EXPORT = qw(&xsinit &ldopts | |
&ccopts &ccflags &ccdlflags &perl_inc | |
&xsi_header &xsi_protos &xsi_body); | |
our $Verbose = 0; | |
our $lib_ext = $Config{lib_ext} || '.a'; | |
sub is_cmd { $0 eq '-e' } | |
sub my_return { | |
my $val = shift; | |
if(is_cmd) { | |
print $val; | |
} | |
else { | |
return $val; | |
} | |
} | |
sub xsinit { | |
my($file, $std, $mods) = @_; | |
my($fh,@mods,%seen); | |
$file ||= "perlxsi.c"; | |
my $xsinit_proto = "pTHX"; | |
if (@_) { | |
@mods = @$mods if $mods; | |
} | |
else { | |
require Getopt::Std; | |
Getopt::Std::getopts('o:s:'); | |
$file = $opt_o if defined $opt_o; | |
$std = $opt_s if defined $opt_s; | |
@mods = @ARGV; | |
} | |
$std = 1 unless scalar @mods; | |
if ($file eq "STDOUT") { | |
$fh = \*STDOUT; | |
} | |
else { | |
open $fh, '>', $file | |
or die "Can't open '$file': $!"; | |
} | |
push(@mods, static_ext()) if defined $std; | |
@mods = grep(!$seen{$_}++, @mods); | |
print $fh &xsi_header(); | |
print $fh "\nEXTERN_C void xs_init ($xsinit_proto);\n\n"; | |
print $fh &xsi_protos(@mods); | |
print $fh "\nEXTERN_C void\nxs_init($xsinit_proto)\n{\n"; | |
print $fh &xsi_body(@mods); | |
print $fh "}\n"; | |
} | |
sub xsi_header { | |
return <<EOF; | |
#include "EXTERN.h" | |
#include "perl.h" | |
#include "XSUB.h" | |
EOF | |
} | |
sub xsi_protos { | |
my @exts = @_; | |
my %seen; | |
my $retval = ''; | |
foreach my $cname (canon('__', @exts)) { | |
my $ccode = "EXTERN_C void boot_${cname} (pTHX_ CV* cv);\n"; | |
$retval .= $ccode | |
unless $seen{$ccode}++; | |
} | |
return $retval; | |
} | |
sub xsi_body { | |
my @exts = @_; | |
my %seen; | |
my $retval; | |
$retval .= " static const char file[] = __FILE__;\n" | |
if @exts; | |
$retval .= <<'EOT'; | |
dXSUB_SYS; | |
PERL_UNUSED_CONTEXT; | |
EOT | |
$retval .= "\n" | |
if @exts; | |
foreach my $pname (canon('/', @exts)) { | |
next | |
if $seen{$pname}++; | |
(my $mname = $pname) =~ s!/!::!g; | |
(my $cname = $pname) =~ s!/!__!g; | |
my $fname; | |
if ($pname eq 'DynaLoader'){ | |
# Must NOT install 'DynaLoader::boot_DynaLoader' as 'bootstrap'! | |
# boot_DynaLoader is called directly in DynaLoader.pm | |
$retval .= " /* DynaLoader is a special case */\n"; | |
$fname = "${mname}::boot_DynaLoader"; | |
} else { | |
$fname = "${mname}::bootstrap"; | |
} | |
$retval .= " newXS(\"$fname\", boot_${cname}, file);\n" | |
} | |
return $retval; | |
} | |
sub static_ext { | |
@Extensions = ('DynaLoader', sort $Config{static_ext} =~ /(\S+)/g) | |
unless @Extensions; | |
@Extensions; | |
} | |
sub _escape { | |
my $arg = shift; | |
return $$arg if $^O eq 'VMS'; # parens legal in qualifier lists | |
$$arg =~ s/([\(\)])/\\$1/g; | |
} | |
sub _ldflags { | |
my $ldflags = $Config{ldflags}; | |
_escape(\$ldflags); | |
return $ldflags; | |
} | |
sub _ccflags { | |
my $ccflags = $Config{ccflags}; | |
_escape(\$ccflags); | |
return $ccflags; | |
} | |
sub _ccdlflags { | |
my $ccdlflags = $Config{ccdlflags}; | |
_escape(\$ccdlflags); | |
return $ccdlflags; | |
} | |
sub ldopts { | |
require ExtUtils::MakeMaker; | |
require ExtUtils::Liblist; | |
my($std,$mods,$link_args,$path) = @_; | |
my(@mods,@link_args,@argv); | |
my($dllib,$config_libs,@potential_libs,@path); | |
local($") = ' ' unless $" eq ' '; | |
if (scalar @_) { | |
@link_args = @$link_args if $link_args; | |
@mods = @$mods if $mods; | |
} | |
else { | |
@argv = @ARGV; | |
#hmm | |
while($_ = shift @argv) { | |
/^-std$/ && do { $std = 1; next; }; | |
/^--$/ && do { @link_args = @argv; last; }; | |
/^-I(.*)/ && do { $path = $1 || shift @argv; next; }; | |
push(@mods, $_); | |
} | |
} | |
$std = 1 unless scalar @link_args; | |
my $sep = $Config{path_sep} || ':'; | |
@path = $path ? split(/\Q$sep/, $path) : @INC; | |
push(@potential_libs, @link_args) if scalar @link_args; | |
# makemaker includes std libs on windows by default | |
if ($^O ne 'MSWin32' and defined($std)) { | |
push(@potential_libs, $Config{perllibs}); | |
} | |
push(@mods, static_ext()) if $std; | |
my($mod,@ns,$root,$sub,$extra,$archive,@archives); | |
print STDERR "Searching (@path) for archives\n" if $Verbose; | |
foreach $mod (@mods) { | |
@ns = split(/::|\/|\\/, $mod); | |
$sub = $ns[-1]; | |
$root = File::Spec->catdir(@ns); | |
print STDERR "searching for '$sub${lib_ext}'\n" if $Verbose; | |
foreach (@path) { | |
next unless -e ($archive = File::Spec->catdir($_,"auto",$root,"$sub$lib_ext")); | |
push @archives, $archive; | |
if(-e ($extra = File::Spec->catdir($_,"auto",$root,"extralibs.ld"))) { | |
local(*FH); | |
if(open(FH, '<', $extra)) { | |
my($libs) = <FH>; chomp $libs; | |
push @potential_libs, split /\s+/, $libs; | |
} | |
else { | |
warn "Couldn't open '$extra'"; | |
} | |
} | |
last; | |
} | |
} | |
#print STDERR "\@potential_libs = @potential_libs\n"; | |
my $libperl; | |
if ($^O eq 'MSWin32') { | |
$libperl = $Config{libperl}; | |
} | |
elsif ($^O eq 'os390' && $Config{usedl}) { | |
# Nothing for OS/390 (z/OS) dynamic. | |
} else { | |
$libperl = (grep(/^-l\w*perl\w*$/, @link_args))[0] | |
|| ($Config{libperl} =~ /^lib(\w+)(\Q$lib_ext\E|\.\Q$Config{dlext}\E)$/ | |
? "-l$1" : '') | |
|| "-lperl"; | |
} | |
my $lpath = File::Spec->catdir($Config{archlibexp}, 'CORE'); | |
$lpath = qq["$lpath"] if $^O eq 'MSWin32'; | |
my($extralibs, $bsloadlibs, $ldloadlibs, $ld_run_path) = | |
MM->ext(join ' ', "-L$lpath", $libperl, @potential_libs); | |
my $ld_or_bs = $bsloadlibs || $ldloadlibs; | |
print STDERR "bs: $bsloadlibs ** ld: $ldloadlibs" if $Verbose; | |
my $ccdlflags = _ccdlflags(); | |
my $ldflags = _ldflags(); | |
my $linkage = "$ccdlflags $ldflags @archives $ld_or_bs"; | |
print STDERR "ldopts: '$linkage'\n" if $Verbose; | |
return $linkage if scalar @_; | |
my_return("$linkage\n"); | |
} | |
sub ccflags { | |
my $ccflags = _ccflags(); | |
my_return(" $ccflags "); | |
} | |
sub ccdlflags { | |
my $ccdlflags = _ccdlflags(); | |
my_return(" $ccdlflags "); | |
} | |
sub perl_inc { | |
my $dir = File::Spec->catdir($Config{archlibexp}, 'CORE'); | |
$dir = qq["$dir"] if $^O eq 'MSWin32'; | |
my_return(" -I$dir "); | |
} | |
sub ccopts { | |
ccflags . perl_inc; | |
} | |
sub canon { | |
my($as, @ext) = @_; | |
foreach(@ext) { | |
# might be X::Y or lib/auto/X/Y/Y.a | |
next | |
if s!::!/!g; | |
s!^(?:lib|ext|dist|cpan)/(?:auto/)?!!; | |
s!/\w+\.\w+$!!; | |
} | |
if ($as ne '/') { | |
s!/!$as!g | |
foreach @ext; | |
} | |
@ext; | |
} | |
__END__ | |
=head1 NAME | |
ExtUtils::Embed - Utilities for embedding Perl in C/C++ applications | |
=head1 SYNOPSIS | |
perl -MExtUtils::Embed -e xsinit | |
perl -MExtUtils::Embed -e ccopts | |
perl -MExtUtils::Embed -e ldopts | |
=head1 DESCRIPTION | |
C<ExtUtils::Embed> provides utility functions for embedding a Perl interpreter | |
and extensions in your C/C++ applications. | |
Typically, an application F<Makefile> will invoke C<ExtUtils::Embed> | |
functions while building your application. | |
=head1 @EXPORT | |
C<ExtUtils::Embed> exports the following functions: | |
xsinit(), ldopts(), ccopts(), perl_inc(), ccflags(), | |
ccdlflags(), xsi_header(), xsi_protos(), xsi_body() | |
=head1 FUNCTIONS | |
=over 4 | |
=item xsinit() | |
Generate C/C++ code for the XS initializer function. | |
When invoked as C<`perl -MExtUtils::Embed -e xsinit --`> | |
the following options are recognized: | |
B<-o> E<lt>output filenameE<gt> (Defaults to B<perlxsi.c>) | |
B<-o STDOUT> will print to STDOUT. | |
B<-std> (Write code for extensions that are linked with the current Perl.) | |
Any additional arguments are expected to be names of modules | |
to generate code for. | |
When invoked with parameters the following are accepted and optional: | |
C<xsinit($filename,$std,[@modules])> | |
Where, | |
B<$filename> is equivalent to the B<-o> option. | |
B<$std> is boolean, equivalent to the B<-std> option. | |
B<[@modules]> is an array ref, same as additional arguments mentioned above. | |
=item Examples | |
perl -MExtUtils::Embed -e xsinit -- -o xsinit.c Socket | |
This will generate code with an C<xs_init> function that glues the perl C<Socket::bootstrap> function | |
to the C C<boot_Socket> function and writes it to a file named F<xsinit.c>. | |
Note that L<DynaLoader> is a special case where it must call C<boot_DynaLoader> directly. | |
perl -MExtUtils::Embed -e xsinit | |
This will generate code for linking with C<DynaLoader> and | |
each static extension found in C<$Config{static_ext}>. | |
The code is written to the default file name F<perlxsi.c>. | |
perl -MExtUtils::Embed -e xsinit -- -o xsinit.c \ | |
-std DBI DBD::Oracle | |
Here, code is written for all the currently linked extensions along with code | |
for C<DBI> and C<DBD::Oracle>. | |
If you have a working C<DynaLoader> then there is rarely any need to statically link in any | |
other extensions. | |
=item ldopts() | |
Output arguments for linking the Perl library and extensions to your | |
application. | |
When invoked as C<`perl -MExtUtils::Embed -e ldopts --`> | |
the following options are recognized: | |
B<-std> | |
Output arguments for linking the Perl library and any extensions linked | |
with the current Perl. | |
B<-I> E<lt>path1:path2E<gt> | |
Search path for ModuleName.a archives. | |
Default path is C<@INC>. | |
Library archives are expected to be found as | |
F</some/path/auto/ModuleName/ModuleName.a> | |
For example, when looking for F<Socket.a> relative to a search path, | |
we should find F<auto/Socket/Socket.a> | |
When looking for C<DBD::Oracle> relative to a search path, | |
we should find F<auto/DBD/Oracle/Oracle.a> | |
Keep in mind that you can always supply F</my/own/path/ModuleName.a> | |
as an additional linker argument. | |
B<--> E<lt>list of linker argsE<gt> | |
Additional linker arguments to be considered. | |
Any additional arguments found before the B<--> token | |
are expected to be names of modules to generate code for. | |
When invoked with parameters the following are accepted and optional: | |
C<ldopts($std,[@modules],[@link_args],$path)> | |
Where: | |
B<$std> is boolean, equivalent to the B<-std> option. | |
B<[@modules]> is equivalent to additional arguments found before the B<--> token. | |
B<[@link_args]> is equivalent to arguments found after the B<--> token. | |
B<$path> is equivalent to the B<-I> option. | |
In addition, when ldopts is called with parameters, it will return the argument string | |
rather than print it to STDOUT. | |
=item Examples | |
perl -MExtUtils::Embed -e ldopts | |
This will print arguments for linking with C<libperl> and | |
extensions found in C<$Config{static_ext}>. This includes libraries | |
found in C<$Config{libs}> and the first ModuleName.a library | |
for each extension that is found by searching C<@INC> or the path | |
specified by the B<-I> option. | |
In addition, when ModuleName.a is found, additional linker arguments | |
are picked up from the F<extralibs.ld> file in the same directory. | |
perl -MExtUtils::Embed -e ldopts -- -std Socket | |
This will do the same as the above example, along with printing additional | |
arguments for linking with the C<Socket> extension. | |
perl -MExtUtils::Embed -e ldopts -- -std Msql -- \ | |
-L/usr/msql/lib -lmsql | |
Any arguments after the second '--' token are additional linker | |
arguments that will be examined for potential conflict. If there is no | |
conflict, the additional arguments will be part of the output. | |
=item perl_inc() | |
For including perl header files this function simply prints: | |
-I$Config{archlibexp}/CORE | |
So, rather than having to say: | |
perl -MConfig -e 'print "-I$Config{archlibexp}/CORE"' | |
Just say: | |
perl -MExtUtils::Embed -e perl_inc | |
=item ccflags(), ccdlflags() | |
These functions simply print $Config{ccflags} and $Config{ccdlflags} | |
=item ccopts() | |
This function combines C<perl_inc()>, C<ccflags()> and C<ccdlflags()> into one. | |
=item xsi_header() | |
This function simply returns a string defining the same C<EXTERN_C> macro as | |
F<perlmain.c> along with #including F<perl.h> and F<EXTERN.h>. | |
=item xsi_protos(@modules) | |
This function returns a string of C<boot_$ModuleName> prototypes for each @modules. | |
=item xsi_body(@modules) | |
This function returns a string of calls to C<newXS()> that glue the module I<bootstrap> | |
function to I<boot_ModuleName> for each @modules. | |
C<xsinit()> uses the xsi_* functions to generate most of its code. | |
=back | |
=head1 EXAMPLES | |
For examples on how to use C<ExtUtils::Embed> for building C/C++ applications | |
with embedded perl, see L<perlembed>. | |
=head1 SEE ALSO | |
L<perlembed> | |
=head1 AUTHOR | |
Doug MacEachern E<lt>C<[email protected]>E<gt> | |
Based on ideas from Tim Bunce E<lt>C<[email protected]>E<gt> and | |
F<minimod.pl> by Andreas Koenig E<lt>C<[email protected]>E<gt> and Tim Bunce. | |
=cut | |