generate-bindings.pl 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. #!/usr/bin/perl -w
  2. #
  3. # Copyright (C) 2005 Apple Computer, Inc.
  4. # Copyright (C) 2006 Anders Carlsson <andersca@mac.com>
  5. #
  6. # This file is part of WebKit
  7. #
  8. # This library is free software; you can redistribute it and/or
  9. # modify it under the terms of the GNU Library General Public
  10. # License as published by the Free Software Foundation; either
  11. # version 2 of the License, or (at your option) any later version.
  12. #
  13. # This library is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. # Library General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU Library General Public License
  19. # along with this library; see the file COPYING.LIB. If not, write to
  20. # the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  21. # Boston, MA 02110-1301, USA.
  22. #
  23. # This script is a temporary hack.
  24. # Files are generated in the source directory, when they really should go
  25. # to the DerivedSources directory.
  26. # This should also eventually be a build rule driven off of .idl files
  27. # however a build rule only solution is blocked by several radars:
  28. # <rdar://problems/4251781&4251785>
  29. use strict;
  30. use File::Path;
  31. use File::Basename;
  32. use Getopt::Long;
  33. use Text::ParseWords;
  34. use Cwd;
  35. use IDLParser;
  36. use CodeGenerator;
  37. my @idlDirectories;
  38. my $outputDirectory;
  39. my $outputHeadersDirectory;
  40. my $generator;
  41. my $defines;
  42. my $filename;
  43. my $prefix;
  44. my $preprocessor;
  45. my $writeDependencies;
  46. my $verbose;
  47. my $supplementalDependencyFile;
  48. my $additionalIdlFiles;
  49. my $idlAttributesFile;
  50. GetOptions('include=s@' => \@idlDirectories,
  51. 'outputDir=s' => \$outputDirectory,
  52. 'outputHeadersDir=s' => \$outputHeadersDirectory,
  53. 'generator=s' => \$generator,
  54. 'defines=s' => \$defines,
  55. 'filename=s' => \$filename,
  56. 'prefix=s' => \$prefix,
  57. 'preprocessor=s' => \$preprocessor,
  58. 'verbose' => \$verbose,
  59. 'write-dependencies' => \$writeDependencies,
  60. 'supplementalDependencyFile=s' => \$supplementalDependencyFile,
  61. 'additionalIdlFiles=s' => \$additionalIdlFiles,
  62. 'idlAttributesFile=s' => \$idlAttributesFile);
  63. my $targetIdlFile = $ARGV[0];
  64. die('Must specify input file.') unless defined($targetIdlFile);
  65. die('Must specify generator') unless defined($generator);
  66. die('Must specify output directory.') unless defined($outputDirectory);
  67. if (!$outputHeadersDirectory) {
  68. $outputHeadersDirectory = $outputDirectory;
  69. }
  70. $targetIdlFile = Cwd::realpath($targetIdlFile);
  71. if ($verbose) {
  72. print "$generator: $targetIdlFile\n";
  73. }
  74. my $targetInterfaceName = fileparse(basename($targetIdlFile), ".idl");
  75. my $idlFound = 0;
  76. my @supplementedIdlFiles;
  77. if ($supplementalDependencyFile) {
  78. # The format of a supplemental dependency file:
  79. #
  80. # DOMWindow.idl P.idl Q.idl R.idl
  81. # Document.idl S.idl
  82. # Event.idl
  83. # ...
  84. #
  85. # The above indicates that DOMWindow.idl is supplemented by P.idl, Q.idl and R.idl,
  86. # Document.idl is supplemented by S.idl, and Event.idl is supplemented by no IDLs.
  87. # The IDL that supplements another IDL (e.g. P.idl) never appears in the dependency file.
  88. open FH, "< $supplementalDependencyFile" or die "Cannot open $supplementalDependencyFile\n";
  89. while (my $line = <FH>) {
  90. my ($idlFile, @followingIdlFiles) = split(/\s+/, $line);
  91. if ($idlFile and basename($idlFile) eq basename($targetIdlFile)) {
  92. $idlFound = 1;
  93. @supplementedIdlFiles = @followingIdlFiles;
  94. }
  95. }
  96. close FH;
  97. # $additionalIdlFiles is list of IDL files which should not be included in
  98. # DerivedSources*.cpp (i.e. they are not described in the supplemental
  99. # dependency file) but should generate .h and .cpp files.
  100. if (!$idlFound and $additionalIdlFiles) {
  101. my @idlFiles = shellwords($additionalIdlFiles);
  102. $idlFound = grep { $_ and basename($_) eq basename($targetIdlFile) } @idlFiles;
  103. }
  104. if (!$idlFound) {
  105. my $codeGen = CodeGenerator->new(\@idlDirectories, $generator, $outputDirectory, $outputHeadersDirectory, 0, $preprocessor, $writeDependencies, $verbose);
  106. # We generate empty .h and .cpp files just to tell build scripts that .h and .cpp files are created.
  107. generateEmptyHeaderAndCpp($codeGen->FileNamePrefix(), $targetInterfaceName, $outputHeadersDirectory, $outputDirectory);
  108. exit 0;
  109. }
  110. }
  111. # Parse the target IDL file.
  112. my $targetParser = IDLParser->new(!$verbose);
  113. my $targetDocument = $targetParser->Parse($targetIdlFile, $defines, $preprocessor);
  114. if ($idlAttributesFile) {
  115. my $idlAttributes = loadIDLAttributes($idlAttributesFile);
  116. checkIDLAttributes($idlAttributes, $targetDocument, basename($targetIdlFile));
  117. }
  118. foreach my $idlFile (@supplementedIdlFiles) {
  119. next if $idlFile eq $targetIdlFile;
  120. my $interfaceName = fileparse(basename($idlFile), ".idl");
  121. my $parser = IDLParser->new(!$verbose);
  122. my $document = $parser->Parse($idlFile, $defines, $preprocessor);
  123. foreach my $interface (@{$document->interfaces}) {
  124. if ($interface->isPartial and $interface->name eq $targetInterfaceName) {
  125. my $targetDataNode;
  126. foreach my $interface (@{$targetDocument->interfaces}) {
  127. if ($interface->name eq $targetInterfaceName) {
  128. $targetDataNode = $interface;
  129. last;
  130. }
  131. }
  132. die "Not found an interface ${targetInterfaceName} in ${targetInterfaceName}.idl." unless defined $targetDataNode;
  133. # Support for attributes of partial interfaces.
  134. foreach my $attribute (@{$interface->attributes}) {
  135. # Record that this attribute is implemented by $interfaceName.
  136. $attribute->signature->extendedAttributes->{"ImplementedBy"} = $interfaceName;
  137. # Add interface-wide extended attributes to each attribute.
  138. foreach my $extendedAttributeName (keys %{$interface->extendedAttributes}) {
  139. $attribute->signature->extendedAttributes->{$extendedAttributeName} = $interface->extendedAttributes->{$extendedAttributeName};
  140. }
  141. push(@{$targetDataNode->attributes}, $attribute);
  142. }
  143. # Support for methods of partial interfaces.
  144. foreach my $function (@{$interface->functions}) {
  145. # Record that this method is implemented by $interfaceName.
  146. $function->signature->extendedAttributes->{"ImplementedBy"} = $interfaceName;
  147. # Add interface-wide extended attributes to each method.
  148. foreach my $extendedAttributeName (keys %{$interface->extendedAttributes}) {
  149. $function->signature->extendedAttributes->{$extendedAttributeName} = $interface->extendedAttributes->{$extendedAttributeName};
  150. }
  151. push(@{$targetDataNode->functions}, $function);
  152. }
  153. # Support for constants of partial interfaces.
  154. foreach my $constant (@{$interface->constants}) {
  155. # Record that this constant is implemented by $interfaceName.
  156. $constant->extendedAttributes->{"ImplementedBy"} = $interfaceName;
  157. # Add interface-wide extended attributes to each constant.
  158. foreach my $extendedAttributeName (keys %{$interface->extendedAttributes}) {
  159. $constant->extendedAttributes->{$extendedAttributeName} = $interface->extendedAttributes->{$extendedAttributeName};
  160. }
  161. push(@{$targetDataNode->constants}, $constant);
  162. }
  163. } else {
  164. die "$idlFile is not a supplemental dependency of $targetIdlFile. There maybe a bug in the the supplemental dependency generator (preprocess-idls.pl).\n";
  165. }
  166. }
  167. }
  168. # Generate desired output for the target IDL file.
  169. my $codeGen = CodeGenerator->new(\@idlDirectories, $generator, $outputDirectory, $outputHeadersDirectory, 0, $preprocessor, $writeDependencies, $verbose, $targetIdlFile);
  170. $codeGen->ProcessDocument($targetDocument, $defines);
  171. sub generateEmptyHeaderAndCpp
  172. {
  173. my ($prefix, $targetInterfaceName, $outputHeadersDirectory, $outputDirectory) = @_;
  174. my $headerName = "${prefix}${targetInterfaceName}.h";
  175. my $cppName = "${prefix}${targetInterfaceName}.cpp";
  176. my $contents = "/*
  177. This file is generated just to tell build scripts that $headerName and
  178. $cppName are created for ${targetInterfaceName}.idl, and thus
  179. prevent the build scripts from trying to generate $headerName and
  180. $cppName at every build. This file must not be tried to compile.
  181. */
  182. ";
  183. open FH, "> ${outputHeadersDirectory}/${headerName}" or die "Cannot open $headerName\n";
  184. print FH $contents;
  185. close FH;
  186. open FH, "> ${outputDirectory}/${cppName}" or die "Cannot open $cppName\n";
  187. print FH $contents;
  188. close FH;
  189. }
  190. sub loadIDLAttributes
  191. {
  192. my $idlAttributesFile = shift;
  193. my %idlAttributes;
  194. open FH, "<", $idlAttributesFile or die "Couldn't open $idlAttributesFile: $!";
  195. while (my $line = <FH>) {
  196. chomp $line;
  197. next if $line =~ /^\s*#/;
  198. next if $line =~ /^\s*$/;
  199. if ($line =~ /^\s*([^=\s]*)\s*=?\s*(.*)/) {
  200. my $name = $1;
  201. $idlAttributes{$name} = {};
  202. if ($2) {
  203. foreach my $rightValue (split /\|/, $2) {
  204. $rightValue =~ s/^\s*|\s*$//g;
  205. $rightValue = "VALUE_IS_MISSING" unless $rightValue;
  206. $idlAttributes{$name}{$rightValue} = 1;
  207. }
  208. } else {
  209. $idlAttributes{$name}{"VALUE_IS_MISSING"} = 1;
  210. }
  211. } else {
  212. die "The format of " . basename($idlAttributesFile) . " is wrong: line $.\n";
  213. }
  214. }
  215. close FH;
  216. return \%idlAttributes;
  217. }
  218. sub checkIDLAttributes
  219. {
  220. my $idlAttributes = shift;
  221. my $document = shift;
  222. my $idlFile = shift;
  223. foreach my $interface (@{$document->interfaces}) {
  224. checkIfIDLAttributesExists($idlAttributes, $interface->extendedAttributes, $idlFile);
  225. foreach my $attribute (@{$interface->attributes}) {
  226. checkIfIDLAttributesExists($idlAttributes, $attribute->signature->extendedAttributes, $idlFile);
  227. }
  228. foreach my $function (@{$interface->functions}) {
  229. checkIfIDLAttributesExists($idlAttributes, $function->signature->extendedAttributes, $idlFile);
  230. foreach my $parameter (@{$function->parameters}) {
  231. checkIfIDLAttributesExists($idlAttributes, $parameter->extendedAttributes, $idlFile);
  232. }
  233. }
  234. }
  235. }
  236. sub checkIfIDLAttributesExists
  237. {
  238. my $idlAttributes = shift;
  239. my $extendedAttributes = shift;
  240. my $idlFile = shift;
  241. my $error;
  242. OUTER: for my $name (keys %$extendedAttributes) {
  243. if (!exists $idlAttributes->{$name}) {
  244. $error = "Unknown IDL attribute [$name] is found at $idlFile.";
  245. last OUTER;
  246. }
  247. if ($idlAttributes->{$name}{"*"}) {
  248. next;
  249. }
  250. for my $rightValue (split /\s*\|\s*/, $extendedAttributes->{$name}) {
  251. if (!exists $idlAttributes->{$name}{$rightValue}) {
  252. $error = "Unknown IDL attribute [$name=" . $extendedAttributes->{$name} . "] is found at $idlFile.";
  253. last OUTER;
  254. }
  255. }
  256. }
  257. if ($error) {
  258. die "IDL ATTRIBUTE CHECKER ERROR: $error
  259. If you want to add a new IDL attribute, you need to add it to WebCore/bindings/scripts/IDLAttributes.txt and add explanations to the WebKit IDL document (https://trac.webkit.org/wiki/WebKitIDL).
  260. ";
  261. }
  262. }