package Inline::SWIG; use strict; require Inline::C; use Carp; use Cwd qw(cwd); $Inline::SWIG::VERSION = '0.00'; @Inline::SWIG::ISA = qw(Inline::C); #============================================================================ # Register as an Inline language module #============================================================================ sub register { use Config; return { language => 'SWIG', aliases => [], type => 'compiled', suffix => $Config{dlext}, }; } #============================================================================ # Validate the config options. #============================================================================ sub validate { my $o = shift; $o->{ILSM}{AUTO_INCLUDE} ||= <{ILSM}{SWIG_ARGS} .= " -shadow "; # next; #} if ($key eq 'SWIG_INTERFACE') { $o->{ILSM}{$key} = $value; next; } if ($key eq 'SWIG_ARGS') { $o->{ILSM}{$key} .= " $value "; next; } if ($key eq 'SWIG_COMMAND') { $o->{ILSM}{$key} = $value; next; } push @propagate, $key, $value; } $o->SUPER::validate(@propagate); } #============================================================================== # Return a small report about the C code.. #============================================================================== sub info { my $o = shift; return <mkpath($o->{API}{build_dir}) if !-e $o->{API}{build_dir}; my ($modfname) = @{$o->{API}}{modfname}; $o->{ILSM}{MAKEFILE}{OBJECT} .= " ${modfname}.o ${modfname}_wrap.o "; } sub compile { my $o = shift; my $readfile = sub { my($file) = @_; open(IN, "< $file") or croak "Can't open input file \"$file\": $!\n"; my $ret = do{ local $/; }; close IN; return $ret; }; my $writefile = sub {my($text,$op,$file) = @_; open(OUT, "$op $file") or croak "Can't open output file \"$file\": $!\n"; print OUT $text; close OUT; }; my ($code, $pkg,$module, $modpname, $modfname, $build_dir) = @{$o->{API}}{qw(code pkg module modpname modfname build_dir)}; my $code_file = "$o->{API}{build_dir}/$modfname.c"; my $i_file = $code_file; $o->{ILSM}{AUTO_INCLUDE} ||= ""; my $fullcode = $o->{ILSM}{AUTO_INCLUDE} ."\n". $code . "\n"; $writefile->($fullcode,">",$code_file); $o->{ILSM}{MODULE_NAME} ||= $pkg; if($o->{ILSM}{SWIG_INTERFACE}) { $o->{ILSM}{MODULE_NAME} = $1 if $o->{ILSM}{SWIG_INTERFACE} =~ /^%module\s+(\S+)/m; # swig's -module may not completely override a %module, so delete. $o->{ILSM}{SWIG_INTERFACE} =~ s/^%module.*//mg; $i_file = "$o->{API}{build_dir}/$modfname.i"; $writefile->($o->{ILSM}{SWIG_INTERFACE},">",$i_file); } $o->{ILSM}{SWIG_COMMAND} ||= "swig -perl5 "; $o->{ILSM}{SWIG_ARGS} ||= ""; my $cmd = ($o->{ILSM}{SWIG_COMMAND} ." ". $o->{ILSM}{SWIG_ARGS} ." ". " -o $o->{API}{build_dir}/${modfname}_wrap.c ". " -module $modfname ". " $i_file "); $cmd = "$cmd > out.swig 2>&1"; my $cwd = &cwd; ($cwd) = $cwd =~ /(.*)/ if $o->UNTAINT; chdir $build_dir; system($cmd) and do { croak <{API}{language} code. The command that failed was: $cmd The build directory was: $build_dir END }; chdir $cwd; # Fix swig output... # To do Inline, we need files, directories and boot functions, # whose names (related to $modfname) do _not_ match the desired # package names (related to $module_name). # So we tell swig to use $modfname, and then fix the package # names. We use this order because $modfname is in part a # randomly generated unique key (eg, "example_dd77"), so we can # search and replace on it without fear of trampling user code. # At the moment, we are also depending on the property that _only_ # the occurrences of $modfname which must be left unchanged, # basically boot_$modfname and class _wrap_$modfname, have cruft # on the left. So replacing /\b$modfname/ does the right thing. # 2002-Sep-29 mcharity # This note was correct as of Inline::SWIG 0.00 and swig 1.3.15. my $swig_pm = $readfile->("$o->{API}{build_dir}/${modfname}.pm"); my $swig_c = $readfile->("$o->{API}{build_dir}/${modfname}_wrap.c"); my $module_name = $o->{ILSM}{MODULE_NAME}; $swig_pm =~ s/\b${modfname}/${module_name}/g; $swig_c =~ s/\b${modfname}/${module_name}/g; my $req = <<" END"; /* This line was inserted by Inline::SWIG. Load the .pm file, or croak. */ eval_pv("require inlineSWIG_${modfname};",1); END $swig_c =~ s/( +\/\* Install commands \*\/)/$req\n$1/; # Remove the top of the .pm file. Recognizing where to stop # depends on the code generated by swig. Which will change with # future versions of swig. So this regex may need to be changed. # For swig 1.3.15 - Use the @EXPORT line my $new_top = "# Generated by SWIG, modified by Inline::SWIG.\n"; $swig_pm =~ s/^.+?\@EXPORT[^\n]+/$new_top/s; # Kludge: choice of .pm filename: ${modfname}x.pm doesn't get # loaded (fails silently), but x${modfname}.pm does. What a zoo. $writefile->($swig_pm,">","$o->{API}{build_dir}/inlineSWIG_${modfname}.pm"); unlink("$o->{API}{build_dir}/${modfname}.pm"); if($i_file ne $code_file) { $writefile->($swig_c,">","$o->{API}{build_dir}/${modfname}_wrap.c"); } else { $writefile->("\n" ,">","$o->{API}{build_dir}/${modfname}_wrap.c"); $writefile->($swig_c,">>","$o->{API}{build_dir}/${modfname}.c"); } $o->SUPER::compile(@_); } 1; __END__ =head1 NAME Inline::SWIG - Write Perl in C/C++/Objective-C using SWIG. (v0.00) =head1 SYNOPSIS use Inline SWIG => 'int meaning() { return 42; }'; print &meaning(); use Inline SWIG => <<"END_CODE", SWIG_ARGS => '-c++ -shadow', CC => "c++"; class Foo { public: int meaning() { return 42; }; }; END_CODE my $o = new Foo(); print $o->meaning(),"\n"; =head1 DESCRIPTION Inline::SWIG is like Inline::C (or Inline::CPP), but uses SWIG (www.swig.org) to do the parsing and glue code generation. See L, L, L, and the Inline::SWIG homepage L. I suggest using Inline::C or Inline::CPP instead of this module, unless you really want/need SWIG's glue. SWIG's support of perl is currently (ver 1.3.15, 2002-Sep) in flux. For example, the -shadow of the "-c++ -shadow" idiom is required, and is used in SWIG's perl Examples, but it is no longer a documented swig command line argument. Sigh. =head1 CONFIGURATION =head2 CC Inline::C configuration options (such as "CC") can be used. But those which control glue generation, now being done by SWIG, are ignored. ..., CC => 'c++'; # Use c++ instead of cc. =head2 SWIG_ARGS A string of swig command line arguments. See SWIG documentation. "-c++" enables C++ processing. ..., SWIG_ARGS => '-c++'; =head2 SWIG_INTERFACE SWIG .i code. A SWIG_INTERFACE code string is placed in a SWIG .i file. Swig is run on it, instead of the source code. A "%module mumblepackage" declaration (optional) loads the code into package mumblepackage, instead of into the package enclosing the "use Inline SWIG" statement. use Inline SWIG => '... .c code ...', SWIG_INTERFACE => '... .i code ...'; use Inline SWIG => ' ', SWIG_INTERFACE => '... .i code ...'; BEGIN { $header = '... .h code ...'; } use Inline SWIG => $header.' ', SWIG_INTERFACE => <<'END'; %{ $header %} ... .i code, including perhaps another copy of $header ... END =head2 SWIG_COMMAND Defaults to "swig -perl5"; =head1 EXAMPLES =head2 Using a combination of code and interface strings If one uses both code and interface strings, which end up as separate code and interface files, one may need to have common header information. Here is one way to handle it. The BEGIN makes the $foo_h variable available at Perl "compile"-time, which is when the "use Inline" happens. BEGIN { $foo_h = <<" END_OF_H"; class Foo { public: int meaning(); }; END_OF_H } use Inline SWIG => <<"END_OF_CODE", SWIG_INTERFACE => <<"END_OF_I", SWIG_ARGS => '-c++ -shadow', CC => "c++"; $foo_h int Foo::meaning() { return 42; } END_OF_CODE %{ $foo_h %} %module example $foo_h END_OF_I my $o = new example::Foo(); print $o->meaning(),"\n"; =head2 Dynamic code generation Inline::SWIG can also be invoked at Perl "run"-time. use Inline; sub make_adder { my $n = shift; my $function_name = "f".int(rand(1000000)); my $code = "int ${function_name}(int x) { return x + $n; }"; Inline->bind(SWIG => $code); return \&$function_name; } my $add3 = &make_adder(3); print $add3->(2); # -> 5 =head1 IMPLEMENTATION NOTES Inline::SWIG isa Inline::C. To compile code, it runs swig, edits the result, and then hands them off to Inline::C. Inline::SWIG edits the SWIG generated mumble_wrap.c and mumble.pm files. It separates the naming of library boot code from the naming of packages. Thus making the library files compatible with Inline, while retaining the desired package names. Normally, a SWIG .pm file loads the associated library. To avoid fighting with Inline, Inline::SWIG instead loads the .pm file from the library. The .pm file is renamed, its loading code removed, and a "require" inserted into the mumble_wrap.c initialization code. =head1 BUGS AND CAUTIONS This package is a kludge on top of two unstable codebases. Inline::SWIG v0.00 was tested with SWIG v1.3.15, and Inline v0.43. Inline::SWIG does regex substitutions into the SWIG's output code. And SWIG's perl support is currently (ver 1.3.15, 2002-Sep) in a state of flux. And even SWIG command line options are unstable. So new versions of SWIG are likely to break Inline::SWIG. Inline::SWIG inherits from Inline::C, which is part of Inline. So changes to Inline and Inline::C may cause problems. Testing has been very limited. It follows that bugs are very common. =head1 SEE ALSO L, L, L L =head1 AUTHOR Mitchell N Charity Einline_swig@vendian.orgE =head1 COPYRIGHT Copyright (c) 2002, Mitchell N Charity. All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the terms of the Perl Artistic License (see http://www.perl.com/perl/misc/Artistic.html) =cut