Re: Win32 modules & cygwin
[p5sagit/p5-mst-13.2.git] / ext / Win32API / File / ExtUtils / Myconst2perl.pm
CommitLineData
00701878 1# This should eventually become part of MakeMaker as ExtUtils::Mkconst2perl.
2# Documentation for this is very skimpy at this point. Full documentation
3# will be added to ExtUtils::Mkconst2perl when it is created.
4package ExtUtils::Myconst2perl;
5
6use strict;
7use Config;
8
9use vars qw( @ISA @EXPORT @EXPORT_OK $VERSION );
10BEGIN {
11 require Exporter;
12 push @ISA, 'Exporter';
13 @EXPORT= qw( &Myconst2perl );
14 @EXPORT_OK= qw( &ParseAttribs );
15 $VERSION= 1.00;
16}
17
18use Carp;
19use File::Basename;
20use ExtUtils::MakeMaker qw( neatvalue );
21
22# Return the extension to use for a file of C++ source code:
23sub _cc
24{
25 # Some day, $Config{_cc} might be defined for us:
26 return $Config{_cc} if $Config{_cc};
27 return ".cxx"; # Seems to be the most widely accepted extension.
28}
29
30=item ParseAttribs
31
32Parses user-firendly options into coder-firendly specifics.
33
34=cut
35
36sub ParseAttribs
37{
38 # Usage: ParseAttribs( "Package::Name", \%opts, {opt=>\$var} );
39 my( $pkg, $hvAttr, $hvRequests )= @_;
40 my( $outfile, @perlfiles, %perlfilecodes, @cfiles, %cfilecodes );
41 my @importlist= @{$hvAttr->{IMPORT_LIST}};
42 my $perlcode= $hvAttr->{PERL_PE_CODE} ||
43 'last if /^\s*(bootstrap|XSLoader::load)\b/';
44 my $ccode= $hvAttr->{C_PE_CODE} ||
45 'last if m#/[/*]\s*CONSTS_DEFINED\b|^\s*MODULE\b#';
46 my $ifdef= $hvAttr->{IFDEF} || 0;
47 my $writeperl= !! $hvAttr->{WRITE_PERL};
48 my $export= !! $hvAttr->{DO_EXPORT};
49 my $importto= $hvAttr->{IMPORT_TO} || "_constants";
50 my $cplusplus= $hvAttr->{CPLUSPLUS};
51 $cplusplus= "" if ! defined $cplusplus;
52 my $object= "";
53 my $binary= "";
54 my $final= "";
55 my $norebuild= "";
56 my $subroutine= "";
57 my $base;
58 my %params= (
59 PERL_PE_CODE => \$perlcode,
60 PERL_FILE_LIST => \@perlfiles,
61 PERL_FILE_CODES => \%perlfilecodes,
62 PERL_FILES => sub { map {($_,$perlfilecodes{$_})} @perlfiles },
63 C_PE_CODE => \$ccode,
64 C_FILE_LIST => \@cfiles,
65 C_FILE_CODES => \%cfilecodes,
66 C_FILES => sub { map {($_,$cfilecodes{$_})} @cfiles },
67 DO_EXPORT => \$export,
68 IMPORT_TO => \$importto,
69 IMPORT_LIST => \@importlist,
70 SUBROUTINE => \$subroutine,
71 IFDEF => \$ifdef,
72 WRITE_PERL => \$writeperl,
73 CPLUSPLUS => \$cplusplus,
74 BASEFILENAME => \$base,
75 OUTFILE => \$outfile,
76 OBJECT => \$object,
77 BINARY => \$binary,
78 FINAL_PERL => \$final,
79 NO_REBUILD => \$norebuild,
80 );
81 { my @err= grep {! defined $params{$_}} keys %$hvAttr;
82 carp "ExtUtils::Myconst2perl::ParseAttribs: ",
83 "Unsupported option(s) (@err).\n"
84 if @err;
85 }
86 $norebuild= $hvAttr->{NO_REBUILD} if exists $hvAttr->{NO_REBUILD};
87 my $module= ( split /::/, $pkg )[-1];
88 $base= "c".$module;
89 $base= $hvAttr->{BASEFILENAME} if exists $hvAttr->{BASEFILENAME};
90 my $ext= ! $cplusplus ? ($Config{_c}||".c")
91 : $cplusplus =~ /^[.]/ ? $cplusplus : _cc();
92 if( $writeperl ) {
93 $outfile= $base . "_pc" . $ext;
94 $object= $base . "_pc" . ($Config{_o}||$Config{obj_ext});
95 $object= $hvAttr->{OBJECT} if $hvAttr->{OBJECT};
96 $binary= $base . "_pc" . ($Config{_exe}||$Config{exe_ext});
97 $binary= $hvAttr->{BINARY} if $hvAttr->{BINARY};
98 $final= $base . ".pc";
99 $final= $hvAttr->{FINAL_PERL} if $hvAttr->{FINAL_PERL};
100 $subroutine= "main";
101 } elsif( $cplusplus ) {
102 $outfile= $base . $ext;
103 $object= $base . ($Config{_o}||$Config{obj_ext});
104 $object= $hvAttr->{OBJECT} if $hvAttr->{OBJECT};
105 $subroutine= "const2perl_" . $pkg;
106 $subroutine =~ s/\W/_/g;
107 } else {
108 $outfile= $base . ".h";
109 }
110 $outfile= $hvAttr->{OUTFILE} if $hvAttr->{OUTFILE};
111 if( $hvAttr->{PERL_FILES} ) {
112 carp "ExtUtils::Myconst2perl: PERL_FILES option not allowed ",
113 "with PERL_FILE_LIST nor PERL_FILE_CODES.\n"
114 if $hvAttr->{PERL_FILE_LIST} || $hvAttr->{PERL_FILE_CODES};
115 %perlfilecodes= @{$hvAttr->{PERL_FILES}};
116 my $odd= 0;
117 @perlfiles= grep {$odd= !$odd} @{$hvAttr->{PERL_FILES}};
118 } else {
119 if( $hvAttr->{PERL_FILE_LIST} ) {
120 @perlfiles= @{$hvAttr->{PERL_FILE_LIST}};
121 } elsif( $hvAttr->{PERL_FILE_CODES} ) {
122 @perlfiles= keys %{$hvAttr->{PERL_FILE_CODES}};
123 } else {
124 @perlfiles= ( "$module.pm" );
125 }
126 %perlfilecodes= %{$hvAttr->{PERL_FILE_CODES}}
127 if $hvAttr->{PERL_FILE_CODES};
128 }
129 for my $file ( @perlfiles ) {
130 $perlfilecodes{$file}= $perlcode if ! $perlfilecodes{$file};
131 }
132 if( ! $subroutine ) {
133 ; # Don't process any C source code files.
134 } elsif( $hvAttr->{C_FILES} ) {
135 carp "ExtUtils::Myconst2perl: C_FILES option not allowed ",
136 "with C_FILE_LIST nor C_FILE_CODES.\n"
137 if $hvAttr->{C_FILE_LIST} || $hvAttr->{C_FILE_CODES};
138 %cfilecodes= @{$hvAttr->{C_FILES}};
139 my $odd= 0;
140 @cfiles= grep {$odd= !$odd} @{$hvAttr->{C_FILES}};
141 } else {
142 if( $hvAttr->{C_FILE_LIST} ) {
143 @cfiles= @{$hvAttr->{C_FILE_LIST}};
144 } elsif( $hvAttr->{C_FILE_CODES} ) {
145 @cfiles= keys %{$hvAttr->{C_FILE_CODES}};
146 } elsif( $writeperl || $cplusplus ) {
147 @cfiles= ( "$module.xs" );
148 }
149 %cfilecodes= %{$hvAttr->{C_FILE_CODES}} if $hvAttr->{C_FILE_CODES};
150 }
151 for my $file ( @cfiles ) {
152 $cfilecodes{$file}= $ccode if ! $cfilecodes{$file};
153 }
154 for my $key ( keys %$hvRequests ) {
155 if( ! $params{$key} ) {
156 carp "ExtUtils::Myconst2perl::ParseAttribs: ",
157 "Unsupported output ($key).\n";
158 } elsif( "SCALAR" eq ref( $params{$key} ) ) {
159 ${$hvRequests->{$key}}= ${$params{$key}};
160 } elsif( "ARRAY" eq ref( $params{$key} ) ) {
161 @{$hvRequests->{$key}}= @{$params{$key}};
162 } elsif( "HASH" eq ref( $params{$key} ) ) {
163 %{$hvRequests->{$key}}= %{$params{$key}};
164 } elsif( "CODE" eq ref( $params{$key} ) ) {
165 @{$hvRequests->{$key}}= &{$params{$key}};
166 } else {
167 die "Impossible value in \$params{$key}";
168 }
169 }
170}
171
172=item Myconst2perl
173
174Generates a file used to implement C constants as "constant subroutines" in
175a Perl module.
176
177Extracts a list of constants from a module's export list by C<eval>ing the
178first part of the Module's F<*.pm> file and then requesting some groups of
179symbols be exported/imported into a dummy package. Then writes C or C++
180code that can convert each C constant into a Perl "constant subroutine"
181whose name is the constant's name and whose value is the constant's value.
182
183=cut
184
185sub Myconst2perl
186{
187 my( $pkg, %spec )= @_;
188 my( $outfile, $writeperl, $ifdef, $export, $importto, @importlist,
189 @perlfile, %perlcode, @cfile, %ccode, $routine );
190 ParseAttribs( $pkg, \%spec, {
191 DO_EXPORT => \$export,
192 IMPORT_TO => \$importto,
193 IMPORT_LIST => \@importlist,
194 IFDEF => \$ifdef,
195 WRITE_PERL => \$writeperl,
196 OUTFILE => \$outfile,
197 PERL_FILE_LIST => \@perlfile,
198 PERL_FILE_CODES => \%perlcode,
199 C_FILE_LIST => \@cfile,
200 C_FILE_CODES => \%ccode,
201 SUBROUTINE => \$routine,
202 } );
203 my $module= ( split /::/, $pkg )[-1];
204
205 warn "Writing $outfile...\n";
206 open( STDOUT, ">$outfile" ) or die "Can't create $outfile: $!\n";
207
208 my $code= "";
209 my $file;
210 foreach $file ( @perlfile ) {
211 warn "Reading Perl file, $file: $perlcode{$file}\n";
212 open( MODULE, "<$file" ) or die "Can't read Perl file, $file: $!\n";
213 eval qq[
214 while( <MODULE> ) {
215 $perlcode{$file};
216 \$code .= \$_;
217 }
218 1;
219 ] or die "$file eval: $@\n";
220 close( MODULE );
221 }
222
223 print
224 "/* $outfile - Generated by ExtUtils::Myconst2perl::Myconst2perl */\n";
225 if( $routine ) {
226 print "/* See start of $routine() for generation parameters used */\n";
227 #print "#define main _main_proto"
228 # " /* Ignore Perl's main() prototype */\n\n";
229 if( $writeperl ) {
230 # Here are more reasons why the WRITE_PERL option is discouraged.
231 if( $Config{useperlio} ) {
232 print "#define PERLIO_IS_STDIO 1\n";
233 }
234 print "#define WIN32IO_IS_STDIO 1\n"; # May cause a warning
235 print "#define NO_XSLOCKS 1\n"; # What a hack!
236 }
237 foreach $file ( @cfile ) {
238 warn "Reading C file, $file: $ccode{$file}\n";
239 open( XS, "<$file" ) or die "Can't read C file, $file: $!\n";
240 my $code= $ccode{$file};
241 $code =~ s#\\#\\\\#g;
242 $code =~ s#([^\s -~])#"\\x".sprintf "%02X",unpack "C",$1#ge;
243 $code =~ s#[*]/#*\\/#g;
244 print qq[\n/* Include $file: $code */\n];
245 print qq[\n#line 1 "$file"\n];
246 eval qq[
247 while( <XS> ) {
248 $ccode{$file};
249 print;
250 }
251 1;
252 ] or die "$file eval: $@\n";
253 close( XS );
254 }
255 #print qq[\n#undef main\n];
256 print qq[\n#define CONST2WRITE_PERL\n];
257 print qq[\n#include "const2perl.h"\n\n];
258 if( $writeperl ) {
259 print "int\nmain( int argc, char *argv[], char *envp[] )\n";
260 } else {
261 print "void\n$routine( void )\n";
262 }
263 }
264 print "{\n";
265
266 {
267 @ExtUtils::Myconst2perl::importlist= @importlist;
268 my $var= '@ExtUtils::Myconst2perl::importlist';
269 my $port= $export ? "export" : "import";
270 my $arg2= $export ? "q[$importto]," : "";
271 local( $^W )= 0;
272 eval $code . "{\n"
273 . " { package $importto;\n"
274 . " warn qq[\u${port}ing to $importto: $var\\n];\n"
275 . " \$pkg->$port( $arg2 $var );\n"
276 . " }\n"
277 . " { no strict 'refs';\n"
278 . " $var= sort keys %{'_constants::'}; }\n"
279 . " warn 0 + $var, qq[ symbols ${port}ed.\\n];\n"
280 . "}\n1;\n"
281 or die "eval: $@\n";
282 }
283 my @syms= @ExtUtils::Myconst2perl::importlist;
284
285 my $if;
286 my $const;
287 print qq[ START_CONSTS( "$pkg" ) /* No ";" */\n];
288 {
289 my( $head, $tail )= ( "/*", "\n" );
290 if( $writeperl ) {
291 $head= ' printf( "#';
292 $tail= '\\n" );' . "\n";
293 print $head, " Generated by $outfile.", $tail;
294 }
295 print $head, " Package $pkg with options:", $tail;
296 $head= " *" if ! $writeperl;
297 my $key;
298 foreach $key ( sort keys %spec ) {
299 my $val= neatvalue($spec{$key});
300 $val =~ s/\\/\\\\/g if $writeperl;
301 print $head, " $key => ", $val, $tail;
302 }
303 print $head, " Perl files eval'd:", $tail;
304 foreach $key ( @perlfile ) {
305 my $code= $perlcode{$key};
306 $code =~ s#\\#\\\\#g;
307 $code =~ s#([^ -~])#"\\".sprintf "%03o",unpack "C",$1#ge;
308 $code =~ s#"#\\"#g if $writeperl;
309 print $head, " $key => ", $code, $tail;
310 }
311 if( $writeperl ) {
312 print $head, " C files included:", $tail;
313 foreach $key ( @cfile ) {
314 my $code= $ccode{$key};
315 $code =~ s#\\#\\\\#g;
316 $code =~ s#([^ -~])#"\\".sprintf "%03o",unpack "C",$1#ge;
317 $code =~ s#"#\\"#g;
318 print $head, " $key => ", $code, $tail;
319 }
320 } else {
321 print " */\n";
322 }
323 }
324 if( ! ref($ifdef) && $ifdef =~ /[^\s\w]/ ) {
325 my $sub= $ifdef;
326 $sub= 'sub { local($_)= @_; ' . $sub . ' }'
327 unless $sub =~ /^\s*sub\b/;
328 $ifdef= eval $sub;
329 die "$@: $sub\n" if $@;
330 if( "CODE" ne ref($ifdef) ) {
331 die "IFDEF didn't create subroutine reference: eval $sub\n";
332 }
333 }
334 foreach $const ( @syms ) {
335 $if= "CODE" eq ref($ifdef) ? $ifdef->($const) : $ifdef;
336 if( ! $if ) {
337 $if= "";
338 } elsif( "1" eq $if ) {
339 $if= "#ifdef $const\n";
340 } elsif( $if !~ /^#/ ) {
341 $if= "#ifdef $if\n";
342 } else {
343 $if= "$if\n";
344 }
345 print $if
346 . qq[ const2perl( $const );\n];
347 if( $if ) {
348 print "#else\n"
349 . qq[ noconst( $const );\n]
350 . "#endif\n";
351 }
352 }
353 if( $writeperl ) {
354 print
355 qq[ printf( "1;\\n" );\n],
356 qq[ return( 0 );\n];
357 }
358 print "}\n";
359}
360
3611;