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