1#!/usr/bin/perl -w
2#
3# Copyright (C) 2005 Apple 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
24# This script is a temporary hack.
25# Files are generated in the source directory, when they really should go
26# to the DerivedSources directory.
27# This should also eventually be a build rule driven off of .idl files
28# however a build rule only solution is blocked by several radars:
29# <rdar://problems/4251781&4251785>
30
31use strict;
32
33use File::Path;
34use File::Basename;
35use Getopt::Long;
36use Text::ParseWords;
37use Cwd;
38
39use IDLParser;
40use CodeGenerator;
41
42my @idlDirectories;
43my $outputDirectory;
44my $outputHeadersDirectory;
45my $generator;
46my $defines;
47my $filename;
48my $prefix;
49my $preprocessor;
50my $writeDependencies;
51my $verbose;
52my $supplementalDependencyFile;
53my $additionalIdlFiles;
54my $idlAttributesFile;
55
56GetOptions('include=s@' => \@idlDirectories,
57           'outputDir=s' => \$outputDirectory,
58           'outputHeadersDir=s' => \$outputHeadersDirectory,
59           'generator=s' => \$generator,
60           'defines=s' => \$defines,
61           'filename=s' => \$filename,
62           'prefix=s' => \$prefix,
63           'preprocessor=s' => \$preprocessor,
64           'verbose' => \$verbose,
65           'write-dependencies' => \$writeDependencies,
66           'supplementalDependencyFile=s' => \$supplementalDependencyFile,
67           'additionalIdlFiles=s' => \$additionalIdlFiles,
68           'idlAttributesFile=s' => \$idlAttributesFile);
69
70my $targetIdlFile = $ARGV[0];
71
72die('Must specify input file.') unless defined($targetIdlFile);
73die('Must specify generator') unless defined($generator);
74die('Must specify output directory.') unless defined($outputDirectory);
75
76if (!$outputHeadersDirectory) {
77    $outputHeadersDirectory = $outputDirectory;
78}
79$targetIdlFile = Cwd::realpath($targetIdlFile);
80if ($verbose) {
81    print "$generator: $targetIdlFile\n";
82}
83my $targetInterfaceName = fileparse(basename($targetIdlFile), ".idl");
84
85my $idlFound = 0;
86my @supplementedIdlFiles;
87if ($supplementalDependencyFile) {
88    # The format of a supplemental dependency file:
89    #
90    # DOMWindow.idl P.idl Q.idl R.idl
91    # Document.idl S.idl
92    # Event.idl
93    # ...
94    #
95    # The above indicates that DOMWindow.idl is supplemented by P.idl, Q.idl and R.idl,
96    # Document.idl is supplemented by S.idl, and Event.idl is supplemented by no IDLs.
97    # The IDL that supplements another IDL (e.g. P.idl) never appears in the dependency file.
98    open FH, "< $supplementalDependencyFile" or die "Cannot open $supplementalDependencyFile\n";
99    while (my $line = <FH>) {
100        my ($idlFile, @followingIdlFiles) = split(/\s+/, $line);
101        if ($idlFile and basename($idlFile) eq basename($targetIdlFile)) {
102            $idlFound = 1;
103            @supplementedIdlFiles = sort @followingIdlFiles;
104        }
105    }
106    close FH;
107
108    # $additionalIdlFiles is list of IDL files which should not be included in
109    # DerivedSources*.cpp (i.e. they are not described in the supplemental
110    # dependency file) but should generate .h and .cpp files.
111    if (!$idlFound and $additionalIdlFiles) {
112        my @idlFiles = shellwords($additionalIdlFiles);
113        $idlFound = grep { $_ and basename($_) eq basename($targetIdlFile) } @idlFiles;
114    }
115
116    if (!$idlFound) {
117        my $codeGen = CodeGenerator->new(\@idlDirectories, $generator, $outputDirectory, $outputHeadersDirectory, 0, $preprocessor, $writeDependencies, $verbose);
118
119        # We generate empty .h and .cpp files just to tell build scripts that .h and .cpp files are created.
120        generateEmptyHeaderAndCpp($codeGen->FileNamePrefix(), $targetInterfaceName, $outputHeadersDirectory, $outputDirectory);
121        exit 0;
122    }
123}
124
125# Parse the target IDL file.
126my $targetParser = IDLParser->new(!$verbose);
127my $targetDocument = $targetParser->Parse($targetIdlFile, $defines, $preprocessor);
128
129if ($idlAttributesFile) {
130    my $idlAttributes = loadIDLAttributes($idlAttributesFile);
131    checkIDLAttributes($idlAttributes, $targetDocument, basename($targetIdlFile));
132}
133
134foreach my $idlFile (@supplementedIdlFiles) {
135    next if $idlFile eq $targetIdlFile;
136
137    my $interfaceName = fileparse(basename($idlFile), ".idl");
138    my $parser = IDLParser->new(!$verbose);
139    my $document = $parser->Parse($idlFile, $defines, $preprocessor);
140
141    foreach my $interface (@{$document->interfaces}) {
142        if (!$interface->isPartial || $interface->name eq $targetInterfaceName) {
143            my $targetDataNode;
144            foreach my $interface (@{$targetDocument->interfaces}) {
145                if ($interface->name eq $targetInterfaceName) {
146                    $targetDataNode = $interface;
147                    last;
148                }
149            }
150            die "Not found an interface ${targetInterfaceName} in ${targetInterfaceName}.idl." unless defined $targetDataNode;
151
152            # Support for attributes of partial interfaces.
153            foreach my $attribute (@{$interface->attributes}) {
154                # Record that this attribute is implemented by $interfaceName.
155                $attribute->signature->extendedAttributes->{"ImplementedBy"} = $interfaceName if $interface->isPartial;
156
157                # Add interface-wide extended attributes to each attribute.
158                foreach my $extendedAttributeName (keys %{$interface->extendedAttributes}) {
159                    $attribute->signature->extendedAttributes->{$extendedAttributeName} = $interface->extendedAttributes->{$extendedAttributeName};
160                }
161                push(@{$targetDataNode->attributes}, $attribute);
162            }
163
164            # Support for methods of partial interfaces.
165            foreach my $function (@{$interface->functions}) {
166                # Record that this method is implemented by $interfaceName.
167                $function->signature->extendedAttributes->{"ImplementedBy"} = $interfaceName if $interface->isPartial;
168
169                # Add interface-wide extended attributes to each method.
170                foreach my $extendedAttributeName (keys %{$interface->extendedAttributes}) {
171                    $function->signature->extendedAttributes->{$extendedAttributeName} = $interface->extendedAttributes->{$extendedAttributeName};
172                }
173                push(@{$targetDataNode->functions}, $function);
174            }
175
176            # Support for constants of partial interfaces.
177            foreach my $constant (@{$interface->constants}) {
178                # Record that this constant is implemented by $interfaceName.
179                $constant->extendedAttributes->{"ImplementedBy"} = $interfaceName if $interface->isPartial;
180
181                # Add interface-wide extended attributes to each constant.
182                foreach my $extendedAttributeName (keys %{$interface->extendedAttributes}) {
183                    $constant->extendedAttributes->{$extendedAttributeName} = $interface->extendedAttributes->{$extendedAttributeName};
184                }
185                push(@{$targetDataNode->constants}, $constant);
186            }
187        } else {
188            die "$idlFile is not a supplemental dependency of $targetIdlFile. There maybe a bug in the the supplemental dependency generator (preprocess-idls.pl).\n";
189        }
190    }
191}
192
193# Generate desired output for the target IDL file.
194my $codeGen = CodeGenerator->new(\@idlDirectories, $generator, $outputDirectory, $outputHeadersDirectory, 0, $preprocessor, $writeDependencies, $verbose, $targetIdlFile);
195$codeGen->ProcessDocument($targetDocument, $defines);
196
197sub generateEmptyHeaderAndCpp
198{
199    my ($prefix, $targetInterfaceName, $outputHeadersDirectory, $outputDirectory) = @_;
200
201    my $headerName = "${prefix}${targetInterfaceName}.h";
202    my $cppName = "${prefix}${targetInterfaceName}.cpp";
203    my $contents = "/*
204    This file is generated just to tell build scripts that $headerName and
205    $cppName are created for ${targetInterfaceName}.idl, and thus
206    prevent the build scripts from trying to generate $headerName and
207    $cppName at every build. This file must not be tried to compile.
208*/
209";
210    open FH, "> ${outputHeadersDirectory}/${headerName}" or die "Cannot open $headerName\n";
211    print FH $contents;
212    close FH;
213
214    open FH, "> ${outputDirectory}/${cppName}" or die "Cannot open $cppName\n";
215    print FH $contents;
216    close FH;
217}
218
219sub loadIDLAttributes
220{
221    my $idlAttributesFile = shift;
222
223    my %idlAttributes;
224    open FH, "<", $idlAttributesFile or die "Couldn't open $idlAttributesFile: $!";
225    while (my $line = <FH>) {
226        chomp $line;
227        next if $line =~ /^\s*#/;
228        next if $line =~ /^\s*$/;
229
230        if ($line =~ /^\s*([^=\s]*)\s*=?\s*(.*)/) {
231            my $name = $1;
232            $idlAttributes{$name} = {};
233            if ($2) {
234                foreach my $rightValue (split /\|/, $2) {
235                    $rightValue =~ s/^\s*|\s*$//g;
236                    $rightValue = "VALUE_IS_MISSING" unless $rightValue;
237                    $idlAttributes{$name}{$rightValue} = 1;
238                }
239            } else {
240                $idlAttributes{$name}{"VALUE_IS_MISSING"} = 1;
241            }
242        } else {
243            die "The format of " . basename($idlAttributesFile) . " is wrong: line $.\n";
244        }
245    }
246    close FH;
247
248    return \%idlAttributes;
249}
250
251sub checkIDLAttributes
252{
253    my $idlAttributes = shift;
254    my $document = shift;
255    my $idlFile = shift;
256
257    foreach my $interface (@{$document->interfaces}) {
258        checkIfIDLAttributesExists($idlAttributes, $interface->extendedAttributes, $idlFile);
259
260        foreach my $attribute (@{$interface->attributes}) {
261            checkIfIDLAttributesExists($idlAttributes, $attribute->signature->extendedAttributes, $idlFile);
262        }
263
264        foreach my $function (@{$interface->functions}) {
265            checkIfIDLAttributesExists($idlAttributes, $function->signature->extendedAttributes, $idlFile);
266            foreach my $parameter (@{$function->parameters}) {
267                checkIfIDLAttributesExists($idlAttributes, $parameter->extendedAttributes, $idlFile);
268            }
269        }
270    }
271}
272
273sub checkIfIDLAttributesExists
274{
275    my $idlAttributes = shift;
276    my $extendedAttributes = shift;
277    my $idlFile = shift;
278
279    my $error;
280    OUTER: for my $name (keys %$extendedAttributes) {
281        if (!exists $idlAttributes->{$name}) {
282            $error = "Unknown IDL attribute [$name] is found at $idlFile.";
283            last OUTER;
284        }
285        if ($idlAttributes->{$name}{"*"}) {
286            next;
287        }
288        for my $rightValue (split /\s*[|&]\s*/, $extendedAttributes->{$name}) {
289            if (!exists $idlAttributes->{$name}{$rightValue}) {
290                $error = "Unknown IDL attribute [$name=" . $extendedAttributes->{$name} . "] is found at $idlFile.";
291                last OUTER;
292            }
293        }
294    }
295    if ($error) {
296        die "IDL ATTRIBUTE CHECKER ERROR: $error
297If 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).
298";
299    }
300}
301