r10111: Make pidl by default assume the input file is an IDL file rather
[samba.git] / source4 / pidl / pidl
1 #!/usr/bin/perl -w
2
3 ###################################################
4 # package to parse IDL files and generate code for
5 # rpc functions in Samba
6 # Copyright tridge@samba.org 2000-2003
7 # Copyright jelmer@samba.org 2005
8 # released under the GNU GPL
9
10 use strict;
11 use FindBin qw($RealBin);
12 use lib "$RealBin";
13 use lib "$RealBin/lib";
14 use Getopt::Long;
15 use File::Basename;
16 use Parse::Pidl;
17 use Parse::Pidl::Util;
18
19 #####################################################################
20 # save a data structure into a file
21 sub SaveStructure($$)
22 {
23         my($filename,$v) = @_;
24         FileSave($filename, Parse::Pidl::Util::MyDumper($v));
25 }
26
27 #####################################################################
28 # load a data structure from a file (as saved with SaveStructure)
29 sub LoadStructure($)
30 {
31         my $f = shift;
32         my $contents = FileLoad($f);
33         defined $contents || return undef;
34         return eval "$contents";
35 }
36
37 #####################################################################
38 # read a file into a string
39 sub FileLoad($)
40 {
41     my($filename) = shift;
42     local(*INPUTFILE);
43     open(INPUTFILE, $filename) || return undef;
44     my($saved_delim) = $/;
45     undef $/;
46     my($data) = <INPUTFILE>;
47     close(INPUTFILE);
48     $/ = $saved_delim;
49     return $data;
50 }
51
52 #####################################################################
53 # write a string into a file
54 sub FileSave($$)
55 {
56     my($filename) = shift;
57     my($v) = shift;
58     local(*FILE);
59     open(FILE, ">$filename") || die "can't open $filename";    
60     print FILE $v;
61     close(FILE);
62 }
63
64 my($opt_help) = 0;
65 my($opt_parse_tree) = 0;
66 my($opt_dump_tree);
67 my($opt_dump_idl) = 0;
68 my($opt_uint_enums) = 0;
69 my($opt_diff) = 0;
70 my($opt_header);
71 my($opt_ndr_header);
72 my($opt_template) = 0;
73 my($opt_client);
74 my($opt_server);
75 my($opt_ndr_parser);
76 my($opt_tdr_header);
77 my($opt_tdr_parser);
78 my($opt_eth_parser);
79 my($opt_swig);
80 my($opt_dcom_proxy);
81 my($opt_com_header);
82 my($opt_ejs);
83 my($opt_odl) = 0;
84 my($opt_quiet) = 0;
85 my($opt_outputdir) = '.';
86 my($opt_verbose) = 0;
87 my($opt_warn_compat) = 0;
88
89 #########################################
90 # display help text
91 sub ShowHelp()
92 {
93 print "perl IDL parser and code generator
94 Copyright (C) tridge\@samba.org
95
96 Usage: pidl [options] [--] <idlfile> [<idlfile>...]
97
98 Generic Options:
99  --help                  this help page
100  --outputdir=OUTDIR      put output in OUTDIR/ [.]
101  --odl                   accept ODL input
102  --warn-compat           warn about incompatibility with other compilers
103  --quiet                 be quiet
104  --verbose               be verbose
105
106 Debugging:
107  --dump-tree[=OUTFILE]   dump internal representation to file [BASENAME.pidl]
108  --parse-tree            read internal representation instead of IDL
109  --dump-idl              regenerate IDL file
110  --diff                  run diff on original IDL and dumped output
111
112 Samba 4 output:
113  --header[=OUTFILE]      create generic header file [BASENAME.h]
114  --uint-enums            don't use C enums, instead use uint* types
115  --ndr-header[=OUTFILE]  create a C NDR-specific header file [ndr_BASENAME.h]
116  --ndr-parser[=OUTFILE]  create a C NDR parser [ndr_BASENAME.c]
117  --client[=OUTFILE]      create a C NDR client [ndr_BASENAME_c.c]
118  --tdr-header[=OUTFILE]  create a C TDR header file [tdr_BASENAME.h]
119  --tdr-parser[=OUTFILE]  create a C TDR parser [tdr_BASENAME.c]
120  --ejs[=OUTFILE]         create ejs wrapper file [BASENAME_ejs.c]
121  --swig[=OUTFILE]        create swig wrapper file [BASENAME.i]
122  --server[=OUTFILE]      create server boilerplate [ndr_BASENAME_s.c]
123  --template              print a template for a pipe
124  --dcom-proxy[=OUTFILE]  create DCOM proxy (implies --odl) [ndr_BASENAME_p.c]
125  --com-header[=OUTFILE]  create header for COM (implies --odl) [com_BASENAME.h]
126
127 Ethereal parsers:
128  --eth-parser[=OUTFILE]  create ethereal parser and header
129 \n";
130     exit(0);
131 }
132
133 # main program
134 GetOptions (
135             'help|h|?' => \$opt_help, 
136             'outputdir=s' => \$opt_outputdir,
137             'dump-idl' => \$opt_dump_idl,
138                 'dump-tree:s' => \$opt_dump_tree,
139                 'parse-tree' => \$opt_parse_tree,
140             'uint-enums' => \$opt_uint_enums,
141             'ndr-header:s' => \$opt_ndr_header,
142                 'header:s' => \$opt_header,
143             'server:s' => \$opt_server,
144             'tdr-header:s' => \$opt_tdr_header,
145             'tdr-parser:s' => \$opt_tdr_parser,
146             'template' => \$opt_template,
147             'ndr-parser:s' => \$opt_ndr_parser,
148             'client:s' => \$opt_client,
149             'eth-parser:s' => \$opt_eth_parser,
150             'ejs' => \$opt_ejs,
151             'diff' => \$opt_diff,
152             'odl' => \$opt_odl,
153             'swig:s' => \$opt_swig,
154             'dcom-proxy:s' => \$opt_dcom_proxy,
155             'com-header:s' => \$opt_com_header,
156             'quiet' => \$opt_quiet,
157                 'verbose' => \$opt_verbose,
158             'warn-compat' => \$opt_warn_compat
159             );
160
161 if ($opt_help) {
162     ShowHelp();
163     exit(0);
164 }
165
166 sub process_file($)
167 {
168         my $idl_file = shift;
169         my $outputdir = $opt_outputdir;
170         my $pidl;
171         my $ndr;
172
173         my $basename = basename($idl_file, ".idl");
174
175         my($pidl_file) = ($opt_dump_tree or "$outputdir/$basename.pidl");
176
177         unless ($opt_quiet) { print "Compiling $idl_file\n"; }
178
179         if ($opt_parse_tree) {
180                 $pidl = LoadStructure($pidl_file);
181                 defined $pidl || die "Failed to load $pidl_file";
182         } else {
183                 require Parse::Pidl::IDL;
184                 my $idl_parser = new Parse::Pidl::IDL;
185
186                 $pidl = $idl_parser->parse_idl($idl_file);
187                 defined @$pidl || die "Failed to parse $idl_file";
188                 require Parse::Pidl::Typelist;
189                 Parse::Pidl::Typelist::LoadIdl($pidl);
190         }
191         
192         if (defined($opt_dump_tree) && !SaveStructure($pidl_file, $pidl)) {
193                     die "Failed to save $pidl_file\n";
194         }
195
196         if ($opt_uint_enums) {
197                 Parse::Pidl::Util::setUseUintEnums(1);
198         }
199
200         if ($opt_dump_idl) {
201                 require Parse::Pidl::Dump;
202                 print Parse::Pidl::Dump($pidl);
203         }
204
205         if ($opt_diff) {
206                 my($tempfile) = "$outputdir/$basename.tmp";
207                 FileSave($tempfile, IdlDump::Dump($pidl));
208                 system("diff -wu $idl_file $tempfile");
209                 unlink($tempfile);
210         }
211
212         if (defined($opt_com_header)) {
213                 require Parse::Pidl::Samba::COM::Header;
214                 my $res = Parse::Pidl::Samba::COM::Header::Parse($pidl);
215                 if ($res) {
216                         my $comh_filename = ($opt_com_header or "$outputdir/com_$basename.h");
217                         FileSave($comh_filename, 
218                         "#include \"librpc/gen_ndr/ndr_orpc.h\"\n" . 
219                         "#include \"$outputdir/ndr_$basename.h\"\n" . 
220                         $res);
221                 }
222                 $opt_odl = 1;
223         }
224
225         if (defined($opt_dcom_proxy)) {
226                 require Parse::Pidl::Samba::COM::Proxy;
227                 my $res = Parse::Pidl::Samba::COM::Proxy::Parse($pidl);
228                 if ($res) {
229                         my ($client) = ($opt_dcom_proxy or "$outputdir/$basename\_p.c");
230                         FileSave($client, 
231                         "#include \"includes.h\"\n" .
232                         "#include \"$outputdir/com_$basename.h\"\n" . 
233                         "#include \"lib/com/dcom/dcom.h\"\n" .$res);
234                 }
235                 $opt_odl = 1;
236         }
237
238         if ($opt_warn_compat) {
239                 require Parse::Pidl::Compat;
240                 Parse::Pidl::Compat::Check($pidl);
241         }
242
243         if ($opt_odl) {
244                 require Parse::Pidl::ODL;
245                 $pidl = Parse::Pidl::ODL::ODL2IDL($pidl);
246         }
247
248         if (defined($opt_ndr_header) or defined($opt_eth_parser) or 
249             defined($opt_client) or defined($opt_server) or 
250             defined($opt_ndr_parser) or defined($opt_ejs)) {
251                 require Parse::Pidl::NDR;
252                 Parse::Pidl::NDR::Validate($pidl);
253                 $ndr = Parse::Pidl::NDR::Parse($pidl);
254         }
255
256         if (defined($opt_header)) {
257                 my $header = ($opt_header or "$outputdir/$basename.h");
258                 require Parse::Pidl::Samba::Header;
259                 FileSave($header, Parse::Pidl::Samba::Header::Parse($pidl));
260         }
261
262         if (defined($opt_ndr_header)) {
263                 my $header = ($opt_ndr_header or "$outputdir/ndr_$basename.h");
264                 require Parse::Pidl::Samba::NDR::Header;
265                 FileSave($header, Parse::Pidl::Samba::NDR::Header::Parse($pidl, $basename));
266                 if (defined($opt_swig)) {
267                   require Parse::Pidl::Samba::SWIG;
268                   my($filename) = ($opt_swig or "$outputdir/$basename.i");
269                   Parse::Pidl::Samba::SWIG::RewriteHeader($pidl, $header, $filename);
270                 }
271         }
272
273         my $h_filename = "$outputdir/ndr_$basename.h";
274         if (defined($opt_client)) {
275                 require Parse::Pidl::Samba::NDR::Client;
276                 my ($client) = ($opt_client or "$outputdir/ndr_$basename\_c.c");
277
278                 FileSave($client, Parse::Pidl::Samba::NDR::Client::Parse($ndr,$h_filename));
279         }
280
281         if (defined($opt_ejs)) {
282                 require Parse::Pidl::Samba::EJS;
283                 require Parse::Pidl::Samba::EJSHeader;
284                 FileSave("$outputdir/ndr_$basename\_ejs.c", Parse::Pidl::Samba::EJS::Parse($ndr, $h_filename));
285
286                 FileSave("$outputdir/ndr_$basename\_ejs.h", Parse::Pidl::Samba::EJSHeader::Parse($ndr));
287         }
288
289         if (defined($opt_server)) {
290                 require Parse::Pidl::Samba::NDR::Server;
291                 my $dcom = "";
292
293                 foreach my $x (@{$pidl}) {
294                         next if ($x->{TYPE} ne "INTERFACE");
295
296                         if (Parse::Pidl::Util::has_property($x, "object")) {
297                                 require Parse::Pidl::Samba::COM::Stub;
298                                 $dcom .= Parse::Pidl::Samba::COM::Stub::ParseInterface($x);
299                         }
300                 }
301
302                 FileSave(($opt_server or "$outputdir/ndr_$basename\_s.c"), Parse::Pidl::Samba::NDR::Server::Parse($ndr,$h_filename));
303
304                 if ($dcom ne "") {
305                         $dcom = "
306 #include \"includes.h\"
307 #include \"$h_filename\"
308 #include \"rpc_server/dcerpc_server.h\"
309 #include \"rpc_server/common/common.h\"
310
311 $dcom
312 ";
313         FileSave("$outputdir/$basename\_d.c", $dcom);
314                 }
315         }
316
317         if (defined($opt_ndr_parser)) {
318                 my $parser = ($opt_ndr_parser or "$outputdir/ndr_$basename.c");
319                 require Parse::Pidl::Samba::NDR::Parser;
320                 FileSave($parser, Parse::Pidl::Samba::NDR::Parser::Parse($ndr, $parser));
321         }
322
323         if (defined($opt_eth_parser)) {
324           require Parse::Pidl::Ethereal::NDR;
325           my($eparser) = ($opt_eth_parser or "$outputdir/packet-dcerpc-$basename.c");
326           my $eheader = $eparser;
327           $eheader =~ s/\.c$/\.h/;
328           my $cnffile = $idl_file;
329           $cnffile =~ s/\.idl$/\.cnf/;
330
331           my ($dp, $dh) = Parse::Pidl::Ethereal::NDR::Parse($ndr, $idl_file, $eheader, $cnffile);
332           FileSave($eparser, $dp) if defined($dp);
333           FileSave($eheader, $dh) if defined($dh);
334         }
335
336         my $tdr_parser = ($opt_tdr_parser or "$outputdir/tdr_$basename.c");
337         my $tdr_header = ($opt_tdr_header or "$outputdir/tdr_$basename.h");
338         if (defined($opt_tdr_parser)) {
339                 require Parse::Pidl::Samba::TDR;
340                 FileSave($tdr_parser, Parse::Pidl::Samba::TDR::Parser($pidl, $tdr_header));
341         }
342
343         if (defined($opt_tdr_header)) {
344                 require Parse::Pidl::Samba::TDR;
345                 FileSave($tdr_header, Parse::Pidl::Samba::TDR::Header($pidl, $outputdir,$basename));
346         }
347
348         if ($opt_template) {
349                 require Parse::Pidl::Samba::Template;
350                 print Parse::Pidl::Samba::Template::Parse($pidl);
351         }
352 }
353
354 if (scalar(@ARGV) == 0) {
355         print "pidl: no input files\n";
356         exit(0);
357 }
358
359 process_file($_) foreach (@ARGV);