Thread::Queue 2.09
[p5sagit/p5-mst-13.2.git] / ext / Devel / PPPort / parts / ppptools.pl
CommitLineData
adfe19db 1################################################################################
2#
3# ppptools.pl -- various utility functions
4#
5################################################################################
6#
c1a049cb 7# $Revision: 23 $
adfe19db 8# $Author: mhx $
c1a049cb 9# $Date: 2008/01/04 10:47:40 +0100 $
adfe19db 10#
11################################################################################
12#
c1a049cb 13# Version 3.x, Copyright (C) 2004-2008, Marcus Holland-Moritz.
adfe19db 14# Version 2.x, Copyright (C) 2001, Paul Marquess.
15# Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
16#
17# This program is free software; you can redistribute it and/or
18# modify it under the same terms as Perl itself.
19#
20################################################################################
21
1d088ed8 22sub cat_file
23{
24 eval { require File::Spec };
25 return $@ ? join('/', @_) : File::Spec->catfile(@_);
26}
27
28sub all_files_in_dir
29{
30 my $dir = shift;
31 local *DIR;
32
33 opendir DIR, $dir or die "cannot open directory $dir: $!\n";
34 my @files = grep { !-d && !/^\./ } readdir DIR; # no dirs or hidden files
35 closedir DIR;
36
37 return map { cat_file($dir, $_) } @files;
38}
39
adfe19db 40sub parse_todo
41{
42 my $dir = shift || 'parts/todo';
43 local *TODO;
44 my %todo;
45 my $todo;
46
1d088ed8 47 for $todo (all_files_in_dir($dir)) {
adfe19db 48 open TODO, $todo or die "cannot open $todo: $!\n";
49 my $perl = <TODO>;
50 chomp $perl;
51 while (<TODO>) {
52 chomp;
53 s/#.*//;
54 s/^\s+//; s/\s+$//;
55 /^\s*$/ and next;
56 /^\w+$/ or die "invalid identifier: $_\n";
57 exists $todo{$_} and die "duplicate identifier: $_ ($todo{$_} <=> $perl)\n";
58 $todo{$_} = $perl;
59 }
60 close TODO;
61 }
62
63 return \%todo;
64}
65
96ad942f 66sub expand_version
67{
68 my($op, $ver) = @_;
69 my($r, $v, $s) = parse_version($ver);
70 $r == 5 or die "only Perl revision 5 is supported\n";
c83e6f19 71 my $bcdver = sprintf "0x%d%03d%03d", $r, $v, $s;
72 return "(PERL_BCDVERSION $op $bcdver)";
96ad942f 73}
74
adfe19db 75sub parse_partspec
76{
77 my $file = shift;
78 my $section = 'implementation';
79 my $vsec = join '|', qw( provides dontwarn implementation
80 xsubs xsinit xsmisc xshead xsboot tests );
81 my(%data, %options);
82 local *F;
83
84 open F, $file or die "$file: $!\n";
85 while (<F>) {
c83e6f19 86 /[ \t]+$/ and warn "$file:$.: warning: trailing whitespace\n";
87 if ($section eq 'implementation') {
88 m!//! && !m!(?:=~|s/).*//! && !m!(?:ht|f)tp://!
89 and warn "$file:$.: warning: potential C++ comment\n";
90 }
adfe19db 91 /^##/ and next;
92 if (/^=($vsec)(?:\s+(.*))?/) {
93 $section = $1;
94 if (defined $2) {
95 my $opt = $2;
96 $options{$section} = eval "{ $opt }";
c83e6f19 97 $@ and die "$file:$.: invalid options ($opt) in section $section: $@\n";
adfe19db 98 }
99 next;
100 }
101 push @{$data{$section}}, $_;
102 }
103 close F;
104
105 for (keys %data) {
106 my @v = @{$data{$_}};
107 shift @v while @v && $v[0] =~ /^\s*$/;
108 pop @v while @v && $v[-1] =~ /^\s*$/;
109 $data{$_} = join '', @v;
110 }
111
112 unless (exists $data{provides}) {
679ad62d 113 $data{provides} = ($file =~ /(\w+)$/)[0];
adfe19db 114 }
115 $data{provides} = [$data{provides} =~ /(\S+)/g];
116
117 if (exists $data{dontwarn}) {
118 $data{dontwarn} = [$data{dontwarn} =~ /(\S+)/g];
119 }
120
121 my @prov;
122 my %proto;
123
124 if (exists $data{tests} && (!exists $data{implementation} || $data{implementation} !~ /\S/)) {
125 $data{implementation} = '';
126 }
127 else {
128 $data{implementation} =~ /\S/ or die "Empty implementation in $file\n";
129
130 my $p;
131
132 for $p (@{$data{provides}}) {
133 if ($p =~ m#^/.*/\w*$#) {
134 my @tmp = eval "\$data{implementation} =~ ${p}gm";
135 $@ and die "invalid regex $p in $file\n";
136 @tmp or warn "no matches for regex $p in $file\n";
137 push @prov, do { my %h; grep !$h{$_}++, @tmp };
138 }
139 elsif ($p eq '__UNDEFINED__') {
140 my @tmp = $data{implementation} =~ /^\s*__UNDEFINED__[^\r\n\S]+(\w+)/gm;
141 @tmp or warn "no __UNDEFINED__ macros in $file\n";
142 push @prov, @tmp;
143 }
144 else {
145 push @prov, $p;
146 }
147 }
148
149 for (@prov) {
150 if ($data{implementation} !~ /\b\Q$_\E\b/) {
151 warn "$file claims to provide $_, but doesn't seem to do so\n";
152 next;
153 }
154
155 # scan for prototypes
156 my($proto) = $data{implementation} =~ /
157 ( ^ (?:[\w*]|[^\S\r\n])+
158 [\r\n]*?
159 ^ \b$_\b \s*
160 \( [^{]* \)
161 )
162 \s* \{
163 /xm or next;
164
165 $proto =~ s/^\s+//;
166 $proto =~ s/\s+$//;
167 $proto =~ s/\s+/ /g;
168
169 exists $proto{$_} and warn "$file: duplicate prototype for $_\n";
170 $proto{$_} = $proto;
171 }
172 }
173
96ad942f 174 for $section (qw( implementation xsubs xsinit xsmisc xshead xsboot )) {
175 if (exists $data{$section}) {
176 $data{$section} =~ s/\{\s*version\s*(<|>|==|!=|>=|<=)\s*([\d._]+)\s*\}/expand_version($1, $2)/gei;
177 }
178 }
179
adfe19db 180 $data{provides} = \@prov;
181 $data{prototypes} = \%proto;
182 $data{OPTIONS} = \%options;
183
184 my %prov = map { ($_ => 1) } @prov;
185 my %dontwarn = exists $data{dontwarn} ? map { ($_ => 1) } @{$data{dontwarn}} : ();
186 my @maybeprov = do { my %h;
187 grep {
188 my($nop) = /^Perl_(.*)/;
189 not exists $prov{$_} ||
190 exists $dontwarn{$_} ||
191 (defined $nop && exists $prov{$nop} ) ||
192 (defined $nop && exists $dontwarn{$nop}) ||
193 $h{$_}++;
194 }
af36fda7 195 $data{implementation} =~ /^\s*#\s*define\s+(\w+)/gm };
adfe19db 196
197 if (@maybeprov) {
198 warn "$file seems to provide these macros, but doesn't list them:\n "
199 . join("\n ", @maybeprov) . "\n";
200 }
201
202 return \%data;
203}
204
205sub compare_prototypes
206{
207 my($p1, $p2) = @_;
208 for ($p1, $p2) {
209 s/^\s+//;
210 s/\s+$//;
211 s/\s+/ /g;
212 s/(\w)\s(\W)/$1$2/g;
213 s/(\W)\s(\w)/$1$2/g;
214 }
215 return $p1 cmp $p2;
216}
217
218sub ppcond
219{
220 my $s = shift;
221 my @c;
222 my $p;
223
224 for $p (@$s) {
225 push @c, map "!($_)", @{$p->{pre}};
226 defined $p->{cur} and push @c, "($p->{cur})";
227 }
228
229 join " && ", @c;
230}
231
232sub trim_arg
233{
234 my $in = shift;
4a582685 235 my $remove = join '|', qw( NN NULLOK );
adfe19db 236
237 $in eq '...' and return ($in);
238
239 local $_ = $in;
240 my $id;
4a582685 241
adfe19db 242 s/[*()]/ /g;
243 s/\[[^\]]*\]/ /g;
244 s/\b(?:auto|const|extern|inline|register|static|volatile|restrict)\b//g;
4a582685 245 s/\b(?:$remove)\b//;
adfe19db 246 s/^\s*//; s/\s*$//;
247
248 if( /^\b(?:struct|union|enum)\s+\w+(?:\s+(\w+))?$/ ) {
249 defined $1 and $id = $1;
250 }
251 else {
252 if( s/\b(?:char|double|float|int|long|short|signed|unsigned|void)\b//g ) {
253 /^\s*(\w+)\s*$/ and $id = $1;
254 }
255 else {
256 /^\s*\w+\s+(\w+)\s*$/ and $id = $1;
257 }
258 }
259
260 $_ = $in;
261
262 defined $id and s/\b$id\b//;
263
264 # these don't matter at all
265 s/\b(?:auto|extern|inline|register|static|volatile|restrict)\b//g;
4a582685 266 s/\b(?:$remove)\b//;
adfe19db 267
268 s/(?=<\*)\s+(?=\*)//g;
269 s/\s*(\*+)\s*/ $1 /g;
270 s/^\s*//; s/\s*$//;
271 s/\s+/ /g;
272
273 return ($_, $id);
274}
275
276sub parse_embed
277{
278 my @files = @_;
279 my @func;
280 my @pps;
281 my $file;
282 local *FILE;
283
284 for $file (@files) {
285 open FILE, $file or die "$file: $!\n";
286 my($line, $l);
287
288 while (defined($line = <FILE>)) {
289 while ($line =~ /\\$/ && defined($l = <FILE>)) {
290 $line =~ s/\\\s*//;
291 $line .= $l;
292 }
293 next if $line =~ /^\s*:/;
294 $line =~ s/^\s+|\s+$//gs;
295 my($dir, $args) = ($line =~ /^\s*#\s*(\w+)(?:\s*(.*?)\s*)?$/);
296 if (defined $dir and defined $args) {
297 for ($dir) {
298 /^ifdef$/ and do { push @pps, { pre => [], cur => "defined($args)" } ; last };
299 /^ifndef$/ and do { push @pps, { pre => [], cur => "!defined($args)" } ; last };
300 /^if$/ and do { push @pps, { pre => [], cur => $args } ; last };
301 /^elif$/ and do { push @{$pps[-1]{pre}}, $pps[-1]{cur}; $pps[-1]{cur} = $args; last };
302 /^else$/ and do { push @{$pps[-1]{pre}}, $pps[-1]{cur}; $pps[-1]{cur} = undef; last };
303 /^endif$/ and do { pop @pps ; last };
304 /^include$/ and last;
305 /^define$/ and last;
306 /^undef$/ and last;
307 warn "unhandled preprocessor directive: $dir\n";
308 }
309 }
310 else {
311 my @e = split /\s*\|\s*/, $line;
312 if( @e >= 3 ) {
313 my($flags, $ret, $name, @args) = @e;
314 for (@args) {
315 $_ = [trim_arg($_)];
316 }
317 ($ret) = trim_arg($ret);
318 push @func, {
319 name => $name,
320 flags => { map { $_, 1 } $flags =~ /./g },
321 ret => $ret,
322 args => \@args,
323 cond => ppcond(\@pps),
324 };
325 }
326 }
327 }
328
329 close FILE;
330 }
331
332 return @func;
333}
334
335sub make_prototype
336{
337 my $f = shift;
338 my @args = map { "@$_" } @{$f->{args}};
339 my $proto;
340 my $pTHX_ = exists $f->{flags}{n} ? "" : "pTHX_ ";
341 $proto = "$f->{ret} $f->{name}" . "($pTHX_" . join(', ', @args) . ')';
342 return $proto;
343}
344
345sub format_version
346{
347 my $ver = shift;
348
349 $ver =~ s/$/000000/;
350 my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/;
351
352 $v = int $v;
353 $s = int $s;
354
355 if ($r < 5 || ($r == 5 && $v < 6)) {
356 if ($s % 10) {
357 die "invalid version '$ver'\n";
358 }
359 $s /= 10;
360
361 $ver = sprintf "%d.%03d", $r, $v;
362 $s > 0 and $ver .= sprintf "_%02d", $s;
363
364 return $ver;
365 }
366
367 return sprintf "%d.%d.%d", $r, $v, $s;
368}
369
370sub parse_version
371{
372 my $ver = shift;
373
374 if ($ver =~ /^(\d+)\.(\d+)\.(\d+)$/) {
375 return ($1, $2, $3);
376 }
377 elsif ($ver !~ /^\d+\.[\d_]+$/) {
378 die "cannot parse version '$ver'\n";
379 }
380
381 $ver =~ s/_//g;
382 $ver =~ s/$/000000/;
383
384 my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/;
385
386 $v = int $v;
387 $s = int $s;
388
389 if ($r < 5 || ($r == 5 && $v < 6)) {
390 if ($s % 10) {
391 die "cannot parse version '$ver'\n";
392 }
393 $s /= 10;
394 }
395
396 return ($r, $v, $s);
397}
398
3991;