Moved to modular java build system.
[panamaz] / src / notzed.nativez / bin / generate-api
1 #!/usr/bin/perl
2
3 # usage
4 #  -t package    target package
5 #  -d directory  output root
6 #  -v            verbose
7 #  -a datafile   add datafile to the dataset, can be from export.so or export-defines, etc
8
9 use Data::Dumper;
10
11 use File::Path qw(make_path);
12 use File::Basename;
13
14 use strict;
15
16 use Carp 'verbose';
17 use FindBin;
18 use lib "$FindBin::Bin/../lib";
19
20 use api;
21 use code;
22 use method;
23
24 $SIG{ __DIE__ } = sub { Carp::confess( @_ ) };
25 $Data::Dumper::Indent = 1;
26
27 my $apidef = "api.api";
28 my $vars = {
29         package => '',
30         output => 'bin',
31         verbose => 0,
32 };
33 my @apilist;
34
35 while (@ARGV) {
36         my $cmd = shift;
37
38         if ($cmd =~ m/^(-[^-])(.+)/) {
39                 $cmd = $1;
40                 unshift @ARGV, $2;
41         }
42
43         if ($cmd eq "-t") {
44                 $vars->{package} = shift;
45     } elsif ($cmd eq "-d") {
46                 $vars->{output} = shift;
47     } elsif ($cmd eq "-I") {
48                 push @{$vars->{include}}, shift;
49     } elsif ($cmd eq "-v") {
50                 $vars->{verbose}++;
51     } elsif ($cmd eq "-a") {
52                 push @apilist, shift;
53         } else {
54                 $apidef = $cmd;
55         }
56 }
57
58 push @{$vars->{include}}, "$FindBin::Bin/../lib";
59
60 print Dumper($vars) if $vars->{verbose};
61
62 my $api = new api($apidef, $vars, @apilist);
63
64 #print Dumper($api);
65
66 exportLibraries($api);
67 exportStructs($api);
68 exportConstants($api);
69 exit 0;
70
71 sub formatFunction {
72         my $api = shift;
73         my $c = shift;
74         my $template = shift;
75         my $info = new method($api, $c);
76
77         #print 'function='.Dumper($c);
78         #print 'members='.Dumper(\@members);
79         #print 'info='.Dumper($info);
80
81         my $code;
82
83         #foreach my $l (split /\n/,Dumper($info->{vars}).Dumper($info->{arguments})) {
84         #       $code .= "// $l\n";
85         #}
86
87         $code .= code::applyTemplate($template, $info->{vars});
88         $code =~ s/^\s*\n//osgm;
89
90         return $code;
91 }
92
93 sub formatCall {
94         my $api = shift;
95         my $c = shift;
96         my $template = $api->{index}->{'code:method'};
97         my $upcall = api::findItem($template, 'upcall');
98         my $downcall = api::findItem($template, 'downcall');
99         my $info = new method($api, $c);
100
101         #print 'function='.Dumper($c);
102         #print 'members='.Dumper(\@members);
103
104         my $code;
105         foreach my $l (split /\n/,Dumper($info)) {
106                 $code .= "//$l\n";
107         }
108
109         $info->{vars}->{downcall} = ($c->{access} =~ m/r/) ? code::applyTemplate($downcall, $info->{vars}) : '';
110         $info->{vars}->{upcall} = ($c->{access} =~ m/w/) ? code::applyTemplate($upcall, $info->{vars}) : '';
111
112         return $code.code::applyTemplate(api::findItem($api->{index}->{'code:class'}, 'call'), $info->{vars});
113 }
114
115 sub formatItems {
116         my $api = shift;
117         my $obj = shift;
118         my $inc = shift;
119         my $res = shift;
120         my @list;
121
122         @list = grep {
123                 $api->{output}->{"$_->{type}:$_->{name}"}++;
124                 $res->{seen}->{"$_->{type}:$_->{name}"}++ == 0
125         } $api->findMatches($inc);
126
127         if ($inc->{type} eq 'func') {
128                 my $def = $api->{index}->{'func:<default>'};
129                 my $func = api::optionValue('func:template', 'code:method=invoke', $inc, $res->{template});
130                 my $init = api::optionValue('init:template', undef, $inc, $res->{template});
131                 my $funct = findTemplateName($api, $func);
132                 my $initt = findTemplateName($api, $init) if defined $init;
133
134                 push @{$res->{func}}, map { formatFunction($api, $_, $funct) } @list;
135                 push @{$res->{init}}, map { formatFunction($api, $_, $initt) } @list if defined($initt);
136         } elsif ($inc->{type} eq 'define') {
137                 push @{$res->{define}}, map { code::formatDefine($api, $_) } @list;
138         } elsif ($inc->{type} eq 'enum') {
139                 push @{$res->{enum}}, map { code::formatEnum($api, $_) } @list;
140         } elsif ($inc->{type} eq 'call') {
141                 push @{$res->{enum}}, map { formatCall($api, $_) } @list;
142         } else {
143                 die;
144         }
145 }
146
147 sub formatLibrary {
148         my $api = shift;
149         my $obj = shift;
150         my $res = shift;
151         my $data = $api->{data};
152         my $d;
153
154         print "library $obj->{name}\n" if ($api->{vars}->{verbose} > 0);
155
156         foreach my $inc (@{$obj->{items}}) {
157                 if ($inc->{type} eq 'library') {
158                         $api->{output}->{"$inc->{match}"}++;
159                         formatLibrary($api, $api->{index}->{$inc->{match}}, $res);
160                 } elsif ($inc->{type} =~ m/^(func|call|define|enum)$/no) {
161                         print "  $inc->{match}\n" if ($api->{vars}->{verbose} > 1);
162                         formatItems($api, $obj, $inc, $res);
163                 } elsif ($inc->{match} eq 'code:<inline>') {
164                         print "  $inc->{match}\n" if ($api->{vars}->{verbose} > 1);
165                         push @{$res->{func}}, $inc->{literal};
166                 } elsif ($inc->{type} eq 'code') {
167                         # apply template perhaps, or apply with set?
168                         push @{$res->{func}}, map {
169                                 if (defined($inc->{options}->[0])) {
170                                         api::findItem($_, $inc->{options}->[0])->{literal};
171                                 } else {
172                                         $_->{literal}
173                                 }
174                         } grep { $_->{match} =~ m/$inc->{regex}/ } @{$api->{api}};
175                 } elsif ($inc->{type} ne 'field') {
176                         print Dumper($inc);
177                         die;
178                 }
179         }
180 }
181
182 sub findTemplateName {
183         my $api = shift;
184         my $name = shift;
185
186         if ($name =~ m/^(.+)=(.+)$/) {
187                 my $template = api::findItem($api->{index}->{$1}, $2);
188                 return $template if defined $template;
189         }
190         die "can't find template '$name'\n";
191 }
192
193 sub findTemplate {
194         my $api = shift;
195         my $obj = shift;
196         my $s = shift;
197         my $data = $api->{data};
198         my $def = $api->{index}->{"$s->{type}:<default>"};
199         my $name = api::optionValue('template', $s->{size} == 0 ? 'code:class=handle' : 'code:class=struct', $obj, $def);
200
201         return findTemplateName($api, $name);
202 }
203
204 # TODO: embedded structs
205 sub formatStruct {
206         my $api = shift;
207         my $obj = shift;
208         my $s = shift;
209         my $data = $api->{data};
210         my $seen = {};
211         my $structTemplate = findTemplate($api, $obj, $s);
212         my @members = code::scanFields($api, $s);
213         my @membersOutput = grep { $_->{field}->{output} } @members;
214
215         my $varhandles = join '', map { code::formatTemplate($_->{match}->{varhandle}, $_->{match}, "\t") } @membersOutput;
216         my $accessors = join "\n", map {
217                 my ($m, $type, $match) = ($_->{field}, $_->{type}, $_->{match});
218                 map {
219                         my $accessor = $api->{index}->{$_};
220
221                         map { code::applyTemplate($_, $match) } grep { $_ } map { api::findItem($accessor, $_) } map {
222                                 my @list = ();
223                                 if ($_ =~ m/r/o) {
224                                         push @list, 'get';
225                                         push @list, 'geti' if $_ =~ m/i/o;
226                                 }
227                                 if ($_ =~ m/w/o) {
228                                         push @list, 'set';
229                                         push @list, 'seti' if $_ =~ m/i/o;
230                                 }
231                                 @list;
232                         } $m->{access};
233                 } split(/,/,api::optionValue('template', undef, $type));
234         } @membersOutput;
235
236         my $res = {
237                 library => [],
238                 func => [],
239                 define => [],
240                 enum => [],
241                 call => [],
242                 seen => {},
243                 template => $structTemplate,
244         };
245
246         formatLibrary($api, $obj, $res);
247
248         my $vars = {
249                 %{$api->{vars}},
250                 layout => code::formatStructLayout($api, $s, \@members),
251                 rename => $s->{rename},
252                 name => $s->{name},
253                 varhandles => $varhandles,
254                 accessors => $accessors,
255                 methods => join("\n", @{$res->{func}}),
256                 defines => join("\n", @{$res->{define}}),
257                 enums => join("\n", @{$res->{enum}}),
258         };
259
260         my $code;
261         foreach my $l (split /\n/,Dumper($s, \@members)) {
262                 $code .= "//$l\n";
263         }
264
265         $api->{output}->{"$s->{type}:$s->{name}"} = 1;
266
267         return $code.code::applyTemplate($structTemplate, $vars);
268 }
269
270 # output all libraries
271 sub exportLibraries {
272         my $api = shift;
273         my $data = $api->{data};
274         my $def = $api->{index}->{'library:<default>'};
275
276         foreach my $obj (grep { $_->{type} eq 'library' } @{$api->{api}}) {
277                 next if $api->{output}->{"$obj->{type}:$obj->{name}"};
278                 next if $obj->{output} != 1;
279
280                 my $library = findTemplateName($api, api::optionValue('template', 'code:class=library', $obj, $def));
281                 my $res = {
282                         library => [],
283                         func => [],
284                         init => [],
285                         define => [],
286                         enum => [],
287                         call => [],
288                         seen => {},
289                         template => $library,
290                 };
291
292                 formatLibrary($api, $obj, $res);
293
294                 my $vars = {
295                         %{$api->{vars}},
296                         name => $obj->{name},
297                         init => join("\n", @{$res->{init}}),
298                         defines => join("\n", @{$res->{define}}),
299                         enums => join("\n", @{$res->{enum}}),
300                         funcs => join("\n", @{$res->{func}}),
301                         calls => join("\n", @{$res->{call}}),
302                 };
303
304                 export($api, $obj->{name}, code::applyTemplate($library, $vars));
305         }
306 }
307
308 sub export {
309         my $api = shift;
310         my $name = shift;
311         my $text = shift;
312
313         my $f = $api->openOutput($name);
314         print $f $text;
315         $api->closeOutput($name, $f);
316 }
317
318 sub formatClass {
319         my $api = shift;
320         my $obj = shift;
321         my $s = shift;
322
323         if ($s->{type} =~ m/struct|union/) {
324                 return formatStruct($api, $obj, $s);
325         } elsif ($s->{type} eq 'call') {
326                 return formatCall($api, $s);
327         } else {
328                 die;
329         }
330 }
331
332 sub exportStructs {
333         my $api = shift;
334         my $data = $api->{data};
335
336         # first those directly referenced
337         foreach my $obj (grep { $_->{type} =~ m/call|struct|union/ } @{$api->{api}}) {
338                 my @list = $api->findMatches($obj);
339
340                 print "gen ".($#list+1)." $obj->{type} $obj->{name}\n" if ($api->{vars}->{verbose} > 0);
341                 foreach my $s (@list) {
342                         next if $api->{output}->{"$s->{type}:$s->{name}"};
343                         print "  $s->{name}\n" if ($api->{vars}->{verbose} > 1);
344
345                         export($api, $s->{rename}, formatClass($api, $obj, $s));
346                 }
347         }
348
349         # then anything else left using the default outputs
350         foreach my $s (grep { $_->{output} && ($_->{type} =~ m/struct|union|call/) && !$api->{output}->{"$_->{type}:$_->{name}"} } @{$api->{api}}) {
351                 my $obj = $data->{"$s->{type}:<default>"};
352                 export($api, $s->{rename}, formatClass($api, $obj, $s));
353         }
354 }
355
356 # exports any define/enum not yet included elsehwere
357 # TODO: this is sort of not very useful
358 sub exportConstants {
359         my $api = shift;
360         my $data = $api->{data};
361         my $template = $api->{index}->{'code:class'};
362         my $constant = api::findItem($template, 'constants');
363
364         # hmm, not sure if i should use obj or just the values directly here
365         foreach my $s (grep { $_->{type} =~ m/define|enum/ } values %{$api->{data}}) {
366                 next if $api->{output}->{"$s->{type}:$s->{name}"};
367                 next if !$s->{output};
368
369                 my $defines = '';
370                 my $enums = '';
371
372                 $defines = code::formatDefine($api, $s) if $s->{type} eq 'define';
373                 $enums = code::formatEnum($api, $s) if $s->{type} eq 'enum';
374
375                 my $vars = {
376                         %{$api->{vars}},
377                         name => $s->{name},
378                         defines => $defines,
379                         enums => $enums,
380                 };
381
382                 my $code;
383                 foreach my $l (split /\n/,Dumper($s)) {
384                         $code .= "//$l\n";
385                 }
386
387                 export($api, $s->{name}, $code.code::applyTemplate($constant, $vars));
388         }
389 }