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 | |