[Qt] Re-generate QtWebKit API forwarding headers when API changes
[WebKit-https.git] / Tools / qmake / syncqt-4.8
1 #!/usr/bin/perl -w
2 ######################################################################
3 #
4 # Synchronizes Qt header files - internal development tool.
5 #
6 # Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 # Contact: Nokia Corporation (qt-info@nokia.com)
8 #
9 ######################################################################
10
11 # use packages -------------------------------------------------------
12 use File::Basename;
13 use File::Path;
14 use Cwd;
15 use Config;
16 use strict;
17
18 for (my $i = 0; $i < $#ARGV; $i++) {
19     if ($ARGV[$i] eq "-base-dir" && $i < $#ARGV - 1) {
20         $ENV{"QTDIR"} = $ARGV[$i + 1];
21         last;
22     }
23 }
24
25 die "syncqt: QTDIR not defined" if ! $ENV{"QTDIR"}; # sanity check
26
27 # global variables
28 my $isunix = 0;
29 my $basedir = $ENV{"QTDIR"};
30 $basedir =~ s=\\=/=g;
31 my %modules = ( # path to module name map
32         "QtGui" => "$basedir/src/gui",
33         "QtOpenGL" => "$basedir/src/opengl",
34         "QtOpenVG" => "$basedir/src/openvg",
35         "QtCore" => "$basedir/src/corelib",
36         "QtXml" => "$basedir/src/xml",
37         "QtXmlPatterns" => "$basedir/src/xmlpatterns",
38         "QtSql" => "$basedir/src/sql",
39         "QtNetwork" => "$basedir/src/network",
40         "QtSvg" => "$basedir/src/svg",
41         "QtDeclarative" => "$basedir/src/declarative",
42         "QtScript" => "$basedir/src/script",
43         "QtScriptTools" => "$basedir/src/scripttools",
44         "Qt3Support" => "$basedir/src/qt3support",
45         "ActiveQt" => "$basedir/src/activeqt",
46         "QtTest" => "$basedir/src/testlib",
47         "QtHelp" => "$basedir/tools/assistant/lib",
48         "QtDesigner" => "$basedir/tools/designer/src/lib",
49         "QtUiTools" => "$basedir/tools/designer/src/uitools",
50         "QtDBus" => "$basedir/src/dbus",
51         "QtWebKit" => "$basedir/src/3rdparty/webkit/Source/WebKit/qt",
52         "phonon" => "$basedir/src/phonon",
53         "QtMultimedia" => "$basedir/src/multimedia",
54         "QtMeeGoGraphicsSystemHelper" => "$basedir/tools/qmeegographicssystemhelper",
55 );
56 my %moduleheaders = ( # restrict the module headers to those found in relative path
57         "QtWebKit" => "Api",
58         "phonon" => "../3rdparty/phonon/phonon",
59 );
60
61 #$modules{"QtCore"} .= ";$basedir/mkspecs/" . $ENV{"MKSPEC"} if defined $ENV{"MKSPEC"};
62
63 # global variables (modified by options)
64 my $module = 0;
65 my $showonly = 0;
66 my $quiet = 0;
67 my $remove_stale = 1;
68 my $force_win = 0;
69 my $force_relative = 0;
70 my $check_includes = 0;
71 my $copy_headers = 0;
72 my $create_uic_class_map = 1;
73 my $create_private_headers = 1;
74 my @modules_to_sync ;
75 $force_relative = 1 if ( -d "/System/Library/Frameworks" );
76 my $out_basedir = $basedir;
77 $out_basedir =~ s=\\=/=g;
78 my $quoted_basedir = "\Q$basedir";
79
80 # functions ----------------------------------------------------------
81
82 ######################################################################
83 # Syntax:  showUsage()
84 # Params:  -none-
85 #
86 # Purpose: Show the usage of the script.
87 # Returns: -none-
88 ######################################################################
89 sub showUsage
90 {
91     print "$0 usage:\n";
92     print "  -copy                 Copy headers instead of include-fwd(default: " . ($copy_headers ? "yes" : "no") . ")\n";
93     print "  -remove-stale         Removes stale headers              (default: " . ($remove_stale ? "yes" : "no") . ")\n";
94     print "  -relative             Force relative symlinks            (default: " . ($force_relative ? "yes" : "no") . ")\n";
95     print "  -windows              Force platform to Windows          (default: " . ($force_win ? "yes" : "no") . ")\n";
96     print "  -showonly             Show action but not perform        (default: " . ($showonly ? "yes" : "no") . ")\n";
97     print "  -outdir <PATH>        Specify output directory for sync  (default: $out_basedir)\n";
98     print "  -quiet                Only report problems, not activity (default: " . ($quiet ? "yes" : "no") . ")\n";
99     print "  -separate-module <NAME>:<PROFILEDIR>:<HEADERDIR> Create headers for <NAME> with original headers in <HEADERDIR> relative to <PROFILEDIR> \n";
100     print "  -help                 This help\n";
101     exit 0;
102 }
103
104 ######################################################################
105 # Syntax:  checkUnix()
106 # Params:  -none-
107 #
108 # Purpose: Check if script runs on a Unix system or not. Cygwin
109 #          systems are _not_ detected as Unix systems.
110 # Returns: 1 if a unix system, else 0.
111 ######################################################################
112 sub checkUnix {
113     my ($r) = 0;
114     if ( $force_win != 0) {
115         return 0;
116     } elsif ( -f "/bin/uname" ) {
117         $r = 1;
118         (-f "\\bin\\uname") && ($r = 0);
119     } elsif ( -f "/usr/bin/uname" ) {
120         $r = 1;
121         (-f "\\usr\\bin\\uname") && ($r = 0);
122     }
123     if($r) {
124         $_ = $Config{'osname'};
125         $r = 0 if( /(ms)|(cyg)win/i );
126     }
127     return $r;
128 }
129
130 sub checkRelative {
131     my ($dir) = @_;
132     return 0 if($dir =~ /^\//);
133     return 0 if(!checkUnix() && $dir =~ /[a-zA-Z]:[\/\\]/);
134     return 1;
135 }
136
137 ######################################################################
138 # Syntax:  shouldMasterInclude(iheader)
139 # Params:  iheader, string, filename to verify inclusion
140 #
141 # Purpose: Determines if header should be in the master include file.
142 # Returns: 0 if file contains "#pragma qt_no_master_include" or not
143 #          able to open, else 1.
144 ######################################################################
145 sub shouldMasterInclude {
146     my ($iheader) = @_;
147     return 0 if(basename($iheader) =~ /_/);
148     return 0 if(basename($iheader) =~ /qconfig/);
149     if(open(F, "<$iheader")) {
150         while(<F>) {
151             chomp;
152             return 0 if(/^\#pragma qt_no_master_include$/);
153         }
154         close(F);
155     } else {
156         return 0;
157     }
158     return 1;
159 }
160
161 ######################################################################
162 # Syntax:  classNames(iheader)
163 # Params:  iheader, string, filename to parse for classname "symlinks"
164 #
165 # Purpose: Scans through iheader to find all classnames that should be
166 #          synced into library's include structure.
167 # Returns: List of all class names in a file.
168 ######################################################################
169 sub classNames {
170     my @ret;
171     my ($iheader) = @_;
172     if(basename($iheader) eq "qglobal.h") {
173         push @ret, "QtGlobal";
174     } elsif(basename($iheader) eq "qendian.h") {
175         push @ret, "QtEndian";
176     } elsif(basename($iheader) eq "qconfig.h") {
177         push @ret, "QtConfig";
178     } elsif(basename($iheader) eq "qplugin.h") {
179         push @ret, "QtPlugin";
180     } elsif(basename($iheader) eq "qalgorithms.h") {
181         push @ret, "QtAlgorithms";
182     } elsif(basename($iheader) eq "qcontainerfwd.h") {
183         push @ret, "QtContainerFwd";
184     } elsif(basename($iheader) eq "qdebug.h") {
185         push @ret, "QtDebug";
186     } elsif(basename($iheader) eq "qevent.h") {
187         push @ret, "QtEvents";
188     } elsif(basename($iheader) eq "qnamespace.h") {
189         push @ret, "Qt"
190     } elsif(basename($iheader) eq "qssl.h") {
191         push @ret, "QSsl";
192     } elsif(basename($iheader) eq "qtest.h") {
193         push @ret, "QTest"
194     } elsif(basename($iheader) eq "qtconcurrentmap.h") {
195         push @ret, "QtConcurrentMap"
196     } elsif(basename($iheader) eq "qtconcurrentfilter.h") {
197         push @ret, "QtConcurrentFilter"
198     } elsif(basename($iheader) eq "qtconcurrentrun.h") {
199         push @ret, "QtConcurrentRun"
200     } elsif(basename($iheader) eq "qaudio.h") {
201         push @ret, "QAudio"
202     }
203
204     my $parsable = "";
205     if(open(F, "<$iheader")) {
206         while(<F>) {
207             my $line = $_;
208             chomp $line;
209                         chop $line if ($line =~ /\r$/);
210             if($line =~ /^\#/) {
211                 if($line =~ /\\$/) {
212                     while($line = <F>) {
213                         chomp $line;
214                         last unless($line =~ /\\$/);
215                     }
216                 }
217                 return @ret if($line =~ m/^#pragma qt_sync_stop_processing/);
218                 push(@ret, $1) if($line =~ m/^#pragma qt_class\(([^)]*)\)[\r\n]*$/);
219                 $line = 0;
220             }
221             if($line) {
222                 $line =~ s,//.*$,,; #remove c++ comments
223                 $line .= ";" if($line =~ m/^Q_[A-Z_]*\(.*\)[\r\n]*$/); #qt macro
224                 $line .= ";" if($line =~ m/^QT_(BEGIN|END)_HEADER[\r\n]*$/); #qt macro
225                 $line .= ";" if($line =~ m/^QT_(BEGIN|END)_NAMESPACE[\r\n]*$/); #qt macro
226                 $line .= ";" if($line =~ m/^QT_MODULE\(.*\)[\r\n]*$/); # QT_MODULE macro
227                 $parsable .= " " . $line;
228             }
229         }
230         close(F);
231     }
232
233     my $last_definition = 0;
234     my @namespaces;
235     for(my $i = 0; $i < length($parsable); $i++) {
236         my $definition = 0;
237         my $character = substr($parsable, $i, 1);
238         if($character eq "/" && substr($parsable, $i+1, 1) eq "*") { #I parse like this for greedy reasons
239             for($i+=2; $i < length($parsable); $i++) {
240                 my $end = substr($parsable, $i, 2);
241                 if($end eq "*/") {
242                     $last_definition = $i+2;
243                     $i++;
244                     last;
245                 }
246             }
247         } elsif($character eq "{") {
248             my $brace_depth = 1;
249             my $block_start = $i + 1;
250           BLOCK: for($i+=1; $i < length($parsable); $i++) {
251               my $ignore = substr($parsable, $i, 1);
252               if($ignore eq "{") {
253                   $brace_depth++;
254               } elsif($ignore eq "}") {
255                   $brace_depth--;
256                   unless($brace_depth) {
257                       for(my $i2 = $i+1; $i2 < length($parsable); $i2++) {
258                           my $end = substr($parsable, $i2, 1);
259                           if($end eq ";" || $end ne " ") {
260                               $definition = substr($parsable, $last_definition, $block_start - $last_definition) . "}";
261                               $i = $i2 if($end eq ";");
262                               $last_definition = $i + 1;
263                               last BLOCK;
264                           }
265                       }
266                   }
267               }
268           }
269         } elsif($character eq ";") {
270             $definition = substr($parsable, $last_definition, $i - $last_definition + 1);
271             $last_definition = $i + 1;
272         } elsif($character eq "}") {
273             # a naked } must be a namespace ending
274             # if it's not a namespace, it's eaten by the loop above
275             pop @namespaces;
276             $last_definition = $i + 1;
277         }
278
279         if (substr($parsable, $last_definition, $i - $last_definition + 1) =~ m/ namespace ([^ ]*) /
280             && substr($parsable, $i+1, 1) eq "{") {
281             push @namespaces, $1;
282
283             # Eat the opening { so that the condensing loop above doesn't see it
284             $i++;
285             $last_definition = $i + 1;
286         }
287
288         if($definition) {
289             $definition =~ s=[\n\r]==g;
290             my @symbols;
291             if($definition =~ m/^ *typedef *.*\(\*([^\)]*)\)\(.*\);$/) {
292                 push @symbols, $1;
293             } elsif($definition =~ m/^ *typedef +(.*) +([^ ]*);$/) {
294                 push @symbols, $2;
295             } elsif($definition =~ m/^ *(template *<.*> *)?(class|struct) +([^ ]* +)?([^<\s]+) ?(<[^>]*> ?)?\s*((,|:)\s*(public|protected|private) *.*)? *\{\}$/) {
296                 push @symbols, $4;
297             } elsif($definition =~ m/^ *Q_DECLARE_.*ITERATOR\((.*)\);$/) {
298                 push @symbols, "Q" . $1 . "Iterator";
299                 push @symbols, "QMutable" . $1 . "Iterator";
300             }
301
302             foreach my $symbol (@symbols) {
303                 $symbol = (join("::", @namespaces) . "::" . $symbol) if (scalar @namespaces);
304                 push @ret, $symbol
305                     if ($symbol =~ /^Q[^:]*$/           # no-namespace, starting with Q
306                         || $symbol =~ /^Phonon::/);     # or in the Phonon namespace
307             }
308         }
309     }
310     return @ret;
311 }
312
313 ######################################################################
314 # Syntax:  syncHeader(header, iheader, copy, timestamp)
315 # Params:  header, string, filename to create "symlink" for
316 #          iheader, string, destination name of symlink
317 #          copy, forces header to be a copy of iheader
318 #          timestamp, the requested modification time if copying
319 #
320 # Purpose: Syncronizes header to iheader
321 # Returns: 1 if successful, else 0.
322 ######################################################################
323 sub syncHeader {
324     my ($header, $iheader, $copy, $ts) = @_;
325     $iheader =~ s=\\=/=g;
326     $header =~ s=\\=/=g;
327     return copyFile($iheader, $header) if($copy);
328
329     unless(-e $header) {
330         my $header_dir = dirname($header);
331         mkpath $header_dir, !$quiet;
332
333         #write it
334         my $iheader_out = fixPaths($iheader, $header_dir);
335         open HEADER, ">$header" || die "Could not open $header for writing!\n";
336         print HEADER "#include \"$iheader_out\"\n";
337         close HEADER;
338         if(defined($ts)) {
339             utime(time, $ts, $header) or die "$iheader, $header";
340         }
341         return 1;
342     }
343     return 0;
344 }
345
346 ######################################################################
347 # Syntax:  fixPaths(file, dir)
348 # Params:  file, string, filepath to be made relative to dir
349 #          dir, string, dirpath for point of origin
350 #
351 # Purpose: file is made relative (if possible) of dir.
352 # Returns: String with the above applied conversion.
353 ######################################################################
354 sub fixPaths {
355     my ($file, $dir) = @_;
356     $dir =~ s=^$quoted_basedir/=$out_basedir/= if(!($basedir eq $out_basedir));
357     $file =~ s=\\=/=g;
358     $dir =~ s=\\=/=g;
359
360     #setup
361     my $ret = $file;
362     $ret =~ s,/cygdrive/([a-zA-Z])/,$1:/,g;
363     my $file_dir = dirname($file);
364     if($file_dir eq ".") {
365         $file_dir = getcwd();
366         $file_dir =~ s=\\=/=g;
367     }
368     $file_dir =~ s,/cygdrive/([a-zA-Z])/,$1:/,g;
369     if($dir eq ".") {
370         $dir = getcwd();
371         $dir =~ s=\\=/=g;
372     }
373     $dir =~ s,/cygdrive/([a-zA-Z])/,$1:/,g;
374     return basename($file) if($file_dir eq $dir);
375
376     #guts
377     my $match_dir = 0;
378     for(my $i = 1; $i < length($file_dir); $i++) {
379         my $slash = index($file_dir, "/", $i);
380         last if($slash == -1);
381         my $tmp = substr($file_dir, 0, $slash);
382         last unless($dir =~ m,^\Q$tmp\E/,);
383         $match_dir = $tmp;
384         $i = $slash;
385     }
386     if($match_dir) {
387         my $after = substr($dir, length($match_dir));
388         my $count = ($after =~ tr,/,,);
389         my $dots = "";
390         for(my $i = 0; $i < $count; $i++) {
391             $dots .= "../";
392         }
393         $ret =~ s,^\Q$match_dir\E,$dots,;
394     }
395     $ret =~ s,/+,/,g;
396     return $ret;
397 }
398
399 ######################################################################
400 # Syntax:  fileContents(filename)
401 # Params:  filename, string, filename of file to return contents
402 #
403 # Purpose: Get the contents of a file.
404 # Returns: String with contents of the file, or empty string if file
405 #          doens't exist.
406 # Warning: Dies if it does exist but script cannot get read access.
407 ######################################################################
408 sub fileContents {
409     my ($filename) = @_;
410     my $filecontents = "";
411     if (-e $filename) {
412         open(I, "< $filename") || die "Could not open $filename for reading, read block?";
413         local $/;
414         binmode I;
415         $filecontents = <I>;
416         close I;
417     }
418     return $filecontents;
419 }
420
421 ######################################################################
422 # Syntax:  fileCompare(file1, file2)
423 # Params:  file1, string, filename of first file
424 #          file2, string, filename of second file
425 #
426 # Purpose: Determines if files are equal, and which one is newer.
427 # Returns: 0 if files are equal no matter the timestamp, -1 if file1
428 #          is newer, 1 if file2 is newer.
429 ######################################################################
430 sub fileCompare {
431     my ($file1, $file2) = @_;
432     my $file1contents = fileContents($file1);
433     my $file2contents = fileContents($file2);
434     if (! -e $file1) { return 1; }
435     if (! -e $file2) { return -1; }
436     return $file1contents ne $file2contents ? (stat($file2))[9] <=> (stat($file1))[9] : 0;
437 }
438
439 ######################################################################
440 # Syntax:  copyFile(file, ifile)
441 # Params:  file, string, filename to create duplicate for
442 #          ifile, string, destination name of duplicate
443 #
444 # Purpose: Keeps files in sync so changes in the newer file will be
445 #          written to the other.
446 # Returns: 1 if files were synced, else 0.
447 # Warning: Dies if script cannot get write access.
448 ######################################################################
449 sub copyFile
450 {
451     my ($file,$ifile, $copy,$knowdiff,$filecontents,$ifilecontents) = @_;
452     # Bi-directional synchronization
453     open( I, "< " . $file ) || die "Could not open $file for reading";
454     local $/;
455     binmode I;
456     $filecontents = <I>;
457     close I;
458     if ( open(I, "< " . $ifile) ) {
459         local $/;
460         binmode I;
461         $ifilecontents = <I>;
462         close I;
463         $copy = fileCompare($file, $ifile);
464         $knowdiff = 0,
465     } else {
466         $copy = -1;
467         $knowdiff = 1;
468     }
469
470     if ( $knowdiff || ($filecontents ne $ifilecontents) ) {
471         if ( $copy > 0 ) {
472             my $file_dir = dirname($file);
473             mkpath $file_dir, !$quiet unless(-e $file_dir);
474             open(O, "> " . $file) || die "Could not open $file for writing (no write permission?)";
475             local $/;
476             binmode O;
477             print O $ifilecontents;
478             close O;
479             utime time, (stat($ifile))[9], $file;
480             return 1;
481         } elsif ( $copy < 0 ) {
482             my $ifile_dir = dirname($ifile);
483             mkpath $ifile_dir, !$quiet unless(-e $ifile_dir);
484             open(O, "> " . $ifile) || die "Could not open $ifile for writing (no write permission?)";
485             local $/;
486             binmode O;
487             print O $filecontents;
488             close O;
489             utime time, (stat($file))[9], $ifile;
490             return 1;
491         }
492     }
493     return 0;
494 }
495
496 ######################################################################
497 # Syntax:  symlinkFile(file, ifile)
498 # Params:  file, string, filename to create "symlink" for
499 #          ifile, string, destination name of symlink
500 #
501 # Purpose: File is symlinked to ifile (or copied if filesystem doesn't
502 #          support symlink).
503 # Returns: 1 on success, else 0.
504 ######################################################################
505 sub symlinkFile
506 {
507     my ($file,$ifile) = @_;
508
509     if ($isunix) {
510         print "symlink created for $file " unless $quiet;
511         if ( $force_relative && ($ifile =~ /^$quoted_basedir/)) {
512             my $t = getcwd();
513             my $c = -1;
514             my $p = "../";
515             $t =~ s-^$quoted_basedir/--;
516             $p .= "../" while( ($c = index( $t, "/", $c + 1)) != -1 );
517             $file =~ s-^$quoted_basedir/-$p-;
518             print " ($file)\n" unless $quiet;
519         }
520         print "\n" unless $quiet;
521         return symlink($file, $ifile);
522     }
523     return copyFile($file, $ifile);
524 }
525
526 ######################################################################
527 # Syntax:  findFiles(dir, match, descend)
528 # Params:  dir, string, directory to search for name
529 #          match, string, regular expression to match in dir
530 #          descend, integer, 0 = non-recursive search
531 #                            1 = recurse search into subdirectories
532 #
533 # Purpose: Finds files matching a regular expression.
534 # Returns: List of matching files.
535 #
536 # Examples:
537 #   findFiles("/usr","\.cpp$",1)  - finds .cpp files in /usr and below
538 #   findFiles("/tmp","^#",0)      - finds #* files in /tmp
539 ######################################################################
540 sub findFiles {
541     my ($dir,$match,$descend) = @_;
542     my ($file,$p,@files);
543     local(*D);
544     $dir =~ s=\\=/=g;
545     ($dir eq "") && ($dir = ".");
546     if ( opendir(D,$dir) ) {
547         if ( $dir eq "." ) {
548             $dir = "";
549         } else {
550             ($dir =~ /\/$/) || ($dir .= "/");
551         }
552         foreach $file ( sort readdir(D) ) {
553             next if ( $file  =~ /^\.\.?$/ );
554             $p = $file;
555             ($file =~ /$match/) && (push @files, $p);
556             if ( $descend && -d $p && ! -l $p ) {
557                 push @files, &findFiles($p,$match,$descend);
558             }
559         }
560         closedir(D);
561     }
562     return @files;
563 }
564
565 # --------------------------------------------------------------------
566 # "main" function
567 # --------------------------------------------------------------------
568
569 while ( @ARGV ) {
570     my $var = 0;
571     my $val = 0;
572
573     #parse
574     my $arg = shift @ARGV;
575     if ($arg eq "-h" || $arg eq "-help" || $arg eq "?") {
576         $var = "show_help";
577         $val = "yes";
578     } elsif($arg eq "-copy") {
579         $var = "copy";
580         $val = "yes";
581     } elsif($arg eq "-o" || $arg eq "-outdir") {
582         $var = "output";
583         $val = shift @ARGV;
584     } elsif($arg eq "-showonly" || $arg eq "-remove-stale" || $arg eq "-windows" ||
585             $arg eq "-relative" || $arg eq "-check-includes") {
586         $var = substr($arg, 1);
587         $val = "yes";
588     } elsif($arg =~ /^-no-(.*)$/) {
589         $var = $1;
590         $val = "no";
591         #these are for commandline compat
592     } elsif($arg eq "-inc") {
593         $var = "output";
594         $val = shift @ARGV;
595     } elsif($arg eq "-module") {
596         $var = "module";
597         $val = shift @ARGV;
598     } elsif($arg eq "-separate-module") {
599         $var = "separate-module";
600         $val = shift @ARGV;
601     } elsif($arg eq "-show") {
602         $var = "showonly";
603         $val = "yes";
604     } elsif($arg eq "-quiet") {
605         $var = "quiet";
606         $val = "yes";
607     } elsif($arg eq "-base-dir") {
608         # skip, it's been dealt with at the top of the file
609         shift @ARGV;
610         next;
611     }
612
613     #do something
614     if(!$var || $var eq "show_help") {
615         print "Unknown option: $arg\n\n" if(!$var);
616         showUsage();
617     } elsif ($var eq "copy") {
618         if($val eq "yes") {
619             $copy_headers++;
620         } elsif($showonly) {
621             $copy_headers--;
622         }
623     } elsif ($var eq "showonly") {
624         if($val eq "yes") {
625             $showonly++;
626         } elsif($showonly) {
627             $showonly--;
628         }
629     } elsif ($var eq "quiet") {
630         if($val eq "yes") {
631             $quiet++;
632         } elsif($quiet) {
633             $quiet--;
634         }
635     } elsif ($var eq "check-includes") {
636         if($val eq "yes") {
637             $check_includes++;
638         } elsif($check_includes) {
639             $check_includes--;
640         }
641     } elsif ($var eq "remove-stale") {
642         if($val eq "yes") {
643             $remove_stale++;
644         } elsif($remove_stale) {
645             $remove_stale--;
646         }
647     } elsif ($var eq "windows") {
648         if($val eq "yes") {
649             $force_win++;
650         } elsif($force_win) {
651             $force_win--;
652         }
653     } elsif ($var eq "relative") {
654         if($val eq "yes") {
655             $force_relative++;
656         } elsif($force_relative) {
657             $force_relative--;
658         }
659     } elsif ($var eq "module") {
660         print "module :$val:\n" unless $quiet;
661         die "No such module: $val" unless(defined $modules{$val});
662         push @modules_to_sync, $val;
663     } elsif ($var eq "separate-module") {
664         my $dirsep = ($force_win ? ";" : ":");
665         my ($module, $prodir, $headerdir) = split($dirsep, $val);
666         $modules{$module} = $prodir;
667         push @modules_to_sync, $module;
668         $moduleheaders{$module} = $headerdir;
669         $create_uic_class_map = 0;
670         $create_private_headers = 0;
671     } elsif ($var eq "output") {
672         my $outdir = $val;
673         if(checkRelative($outdir)) {
674             $out_basedir = getcwd();
675             chomp $out_basedir;
676             $out_basedir .= "/" . $outdir;
677         } else {
678             $out_basedir = $outdir;
679         }
680         # \ -> /
681         $out_basedir =~ s=\\=/=g;
682     }
683 }
684 @modules_to_sync = keys(%modules) if($#modules_to_sync == -1);
685
686 $isunix = checkUnix; #cache checkUnix
687
688 # create path
689 mkpath "$out_basedir/include", !$quiet;
690 mkpath "$out_basedir/include/Qt", !$quiet;
691
692 my @ignore_headers = ();
693 my $class_lib_map_contents = "";
694 my @ignore_for_master_contents = ( "qt.h", "qpaintdevicedefs.h" );
695 my @ignore_for_include_check = ( "qatomic.h" );
696 my @ignore_for_qt_begin_header_check = ( "qiconset.h", "qconfig.h", "qconfig-dist.h", "qconfig-large.h", "qconfig-medium.h", "qconfig-minimal.h", "qconfig-small.h", "qfeatures.h", "qt_windows.h" );
697 my @ignore_for_qt_begin_namespace_check = ( "qconfig.h", "qconfig-dist.h", "qconfig-large.h", "qconfig-medium.h", "qconfig-minimal.h", "qconfig-small.h", "qfeatures.h", "qatomic_arch.h", "qatomic_windowsce.h", "qt_windows.h", "qatomic_macosx.h", "qatomic_arm.h", "qatomic_armv7.h" );
698 my @ignore_for_qt_module_check = ( "$modules{QtCore}/arch", "$modules{QtCore}/global", "$modules{QtSql}/drivers", "$modules{QtTest}", "$modules{QtDesigner}", "$modules{QtUiTools}", "$modules{QtDBus}", "$modules{phonon}" );
699 my %colliding_headers = ();
700 my %inject_headers = ( "$basedir/src/corelib/global" => ( "qconfig.h" ) ); # all from build dir
701
702 foreach my $lib (@modules_to_sync) {
703     #iteration info
704     my $dir = $modules{$lib};
705     my $pathtoheaders = "";
706     $pathtoheaders = $moduleheaders{$lib} if ($moduleheaders{$lib});
707
708     #information used after the syncing
709     my $pri_install_classes = "";
710     my $pri_install_files = "";
711     my $pri_install_pfiles = "";
712
713     my $libcapitals = $lib;
714     $libcapitals =~ y/a-z/A-Z/;
715     my $master_contents = "#ifndef QT_".$libcapitals."_MODULE_H\n#define QT_".$libcapitals."_MODULE_H\n";
716
717     #get dependencies
718     my $pro_file = "$dir/" . basename($dir) . ".pro";
719     if(!open(F, "<$pro_file")) {
720         #the pro file doesn't exist let's try to find one
721         opendir(DIR, $dir);
722         $pro_file = "";
723         foreach my $file (readdir(DIR))
724         {
725             if ( $file =~ /\.pro$/i) {
726                 die "There are multiple pro files for $lib module, which one should I use? \n" if ($pro_file ne "");
727                 $pro_file = "$dir/" . $file;
728             }
729         }
730         closedir(DIR);
731         if ($pro_file eq "") {
732             die "I couldn't find a pro file for $lib module \n";
733         }
734     }
735     if(-e "$pro_file") {
736         if(open(F, "<$pro_file")) {
737             while(my $line = <F>) {
738                 chomp $line;
739                 if($line =~ /^ *QT *\+?= *([^\r\n]*)/) {
740                     foreach(split(/ /, $1)) {
741                         $master_contents .= "#include <QtCore/QtCore>\n" if($_ eq "core");
742                         $master_contents .= "#include <QtGui/QtGui>\n" if($_ eq "gui");
743                         $master_contents .= "#include <QtNetwork/QtNetwork>\n" if($_ eq "network");
744                         $master_contents .= "#include <QtSvg/QtSvg>\n" if($_ eq "svg");
745                         $master_contents .= "#include <QtDeclarative/QtDeclarative>\n" if($_ eq "declarative");
746                         $master_contents .= "#include <QtScript/QtScript>\n" if($_ eq "script");
747                         $master_contents .= "#include <QtScriptTools/QtScriptTools>\n" if($_ eq "scripttools");
748                         $master_contents .= "#include <Qt3Support/Qt3Support>\n" if($_ eq "qt3support");
749                         $master_contents .= "#include <QtSql/QtSql>\n" if($_ eq "sql");
750                         $master_contents .= "#include <QtXml/QtXml>\n" if($_ eq "xml");
751                         $master_contents .= "#include <QtXmlPatterns/QtXmlPatterns>\n" if($_ eq "xmlpatterns");
752                         $master_contents .= "#include <QtOpenGL/QtOpenGL>\n" if($_ eq "opengl");
753                         $master_contents .= "#include <QtOpenVG/QtOpenVG>\n" if($_ eq "openvg");
754                     }
755                 }
756             }
757             close(F);
758         }
759     }
760
761     #remove the old files
762     if($remove_stale) {
763         my @subdirs = ("$out_basedir/include/$lib");
764         foreach my $subdir (@subdirs) {
765             if (opendir DIR, $subdir) {
766                 while(my $t = readdir(DIR)) {
767                     my $file = "$subdir/$t";
768                     if(-d $file) {
769                         push @subdirs, $file unless($t eq "." || $t eq "..");
770                     } else {
771                         my @files = ($file);
772                         #push @files, "$out_basedir/include/Qt/$t" if(-e "$out_basedir/include/Qt/$t");
773                         foreach my $file (@files) {
774                            my $remove_file = 0;
775                            if(open(F, "<$file")) {
776                                 while(my $line = <F>) {
777                                     chomp $line;
778                                     if($line =~ /^\#include \"([^\"]*)\"$/) {
779                                         my $include = $1;
780                                         $include = $subdir . "/" . $include unless(substr($include, 0, 1) eq "/");
781                                         $remove_file = 1 unless(-e $include);
782                                     } else {
783                                         $remove_file = 0;
784                                         last;
785                                     }
786                                 }
787                                 close(F);
788                                 unlink $file if($remove_file);
789                             }
790                         }
791                     }
792                 }
793                 closedir DIR;
794             }
795
796         }
797     }
798
799     #create the new ones
800     foreach my $current_dir (split(/;/, $dir)) {
801         my $headers_dir = $current_dir;
802         $headers_dir .= "/$pathtoheaders" if ($pathtoheaders);
803         #calc subdirs
804         my @subdirs = ($headers_dir);
805         foreach my $subdir (@subdirs) {
806             opendir DIR, $subdir or next;
807             while(my $t = readdir(DIR)) {
808                 push @subdirs, "$subdir/$t" if(-d "$subdir/$t" && !($t eq ".") &&
809                                                !($t eq "..") && !($t eq ".obj") &&
810                                                !($t eq ".moc") && !($t eq ".rcc") &&
811                                                !($t eq ".uic") && !($t eq "build"));
812             }
813             closedir DIR;
814         }
815
816         #calc files and "copy" them
817         foreach my $subdir (@subdirs) {
818             my @headers = findFiles($subdir, "^[-a-z0-9_]*\\.h\$" , 0);
819             if (defined $inject_headers{$subdir}) {
820                 foreach my $if ($inject_headers{$subdir}) {
821                     @headers = grep(!/^\Q$if\E$/, @headers); #in case we configure'd previously
822                     push @headers, "*".$if;
823                 }
824             }
825             foreach my $header (@headers) {
826                 my $shadow = ($header =~ s/^\*//);
827                 $header = 0 if($header =~ /^ui_.*.h/);
828                 foreach (@ignore_headers) {
829                     $header = 0 if($header eq $_);
830                 }
831                 if($header) {
832                     my $header_copies = 0;
833                     #figure out if it is a public header
834                     my $public_header = $header;
835                     if($public_header =~ /_p.h$/ || $public_header =~ /_pch.h$/) {
836                         $public_header = 0;
837                     } else {
838                         foreach (@ignore_for_master_contents) {
839                             $public_header = 0 if($header eq $_);
840                         }
841                     }
842
843                     my $iheader = $subdir . "/" . $header;
844                     $iheader =~ s/^\Q$basedir\E/$out_basedir/ if ($shadow);
845                     my @classes = $public_header ? classNames($iheader) : ();
846                     if($showonly) {
847                         print "$header [$lib]\n";
848                         foreach(@classes) {
849                             print "SYMBOL: $_\n";
850                         }
851                     } else {
852                         my $ts = (stat($iheader))[9];
853                         #find out all the places it goes..
854                         my @headers;
855                         if ($public_header) {
856                             @headers = ( "$out_basedir/include/$lib/$header" );
857
858                             # write forwarding headers to include/Qt
859                             if ($lib ne "phonon" && $subdir =~ /^$quoted_basedir\/src/) {
860                                 my $file_name = "$out_basedir/include/Qt/$header";
861                                 my $file_op = '>';
862                                 my $header_content = '';
863                                 if (exists $colliding_headers{$file_name}) {
864                                     $file_op = '>>';
865                                 } else {
866                                     $colliding_headers{$file_name} = 1;
867                                     my $warning_msg = 'Inclusion of header files from include/Qt is deprecated.';
868                                     $header_content = "#ifndef QT_NO_QT_INCLUDE_WARN\n" .
869                                                       "    #if defined(__GNUC__)\n" .
870                                                       "        #warning \"$warning_msg\"\n" .
871                                                       "    #elif defined(_MSC_VER)\n" .
872                                                       "        #pragma message(\"WARNING: $warning_msg\")\n" .
873                                                       "    #endif\n".
874                                                       "#endif\n\n";
875                                 }
876                                 $header_content .= '#include "' . "../$lib/$header" . "\"\n";
877                                 open HEADERFILE, $file_op, $file_name or die "unable to open '$file_name' : $!\n";
878                                 print HEADERFILE $header_content;
879                                 close HEADERFILE;
880                             }
881
882                             foreach my $full_class (@classes) {
883                                 my $header_base = basename($header);
884                                 # Strip namespaces:
885                                 my $class = $full_class;
886                                 $class =~ s/^.*:://;
887 #                               if ($class =~ m/::/) {
888 #                                  class =~ s,::,/,g;
889 #                               }
890                                 $class_lib_map_contents .= "QT_CLASS_LIB($full_class, $lib, $header_base)\n";
891                                 $header_copies++ if(syncHeader("$out_basedir/include/$lib/$class", "$out_basedir/include/$lib/$header", 0, $ts));
892
893                                 # KDE-Compat headers for Phonon
894                                 if ($lib eq "phonon") {
895                                     $header_copies++ if (syncHeader("$out_basedir/include/phonon_compat/Phonon/$class", "$out_basedir/include/$lib/$header", 0, $ts));
896                                 }
897                             }
898                         } elsif ($create_private_headers) {
899                             @headers = ( "$out_basedir/include/$lib/private/$header" );
900                         }
901                         foreach(@headers) { #sync them
902                             $header_copies++ if(syncHeader($_, $iheader, $copy_headers && !$shadow, $ts));
903                         }
904
905                         if($public_header) {
906                             #put it into the master file
907                             $master_contents .= "#include \"$public_header\"\n" if(shouldMasterInclude($iheader));
908
909                             #deal with the install directives
910                             if($public_header) {
911                                 my $pri_install_iheader = fixPaths($iheader, $current_dir);
912                                 foreach my $class (@classes) {
913                                     # Strip namespaces:
914                                     $class =~ s/^.*:://;
915 #                                   if ($class =~ m/::/) {
916 #                                       $class =~ s,::,/,g;
917 #                                   }
918                                     my $class_header = fixPaths("$out_basedir/include/$lib/$class",
919                                                                 $current_dir) . " ";
920                                     $pri_install_classes .= $class_header
921                                                                 unless($pri_install_classes =~ $class_header);
922                                 }
923                                 $pri_install_files.= "$pri_install_iheader ";;
924                             }
925                         }
926                         else {
927                             my $pri_install_iheader = fixPaths($iheader, $current_dir);
928                             $pri_install_pfiles.= "$pri_install_iheader ";;
929                         }
930                     }
931                     print "header created for $iheader ($header_copies)\n" if($header_copies > 0 && !$quiet);
932                 }
933             }
934         }
935     }
936
937     # close the master include:
938     $master_contents .= "#endif\n";
939
940     unless($showonly) {
941         my @master_includes;
942         push @master_includes, "$out_basedir/include/$lib/$lib";
943         push @master_includes, "$out_basedir/include/phonon_compat/Phonon/Phonon" if ($lib eq "phonon");
944         foreach my $master_include (@master_includes) {
945             #generate the "master" include file
946             my @tmp = split(/;/,$modules{$lib});
947             $pri_install_files .= fixPaths($master_include, $tmp[0]) . " "; #get the master file installed too
948             if($master_include && -e $master_include) {
949                 open MASTERINCLUDE, "<$master_include";
950                 local $/;
951                 binmode MASTERINCLUDE;
952                 my $oldmaster = <MASTERINCLUDE>;
953                 close MASTERINCLUDE;
954                 $oldmaster =~ s/\r//g; # remove \r's , so comparison is ok on all platforms
955                 $master_include = 0 if($oldmaster eq $master_contents);
956             }
957             if($master_include && $master_contents) {
958                 my $master_dir = dirname($master_include);
959                 mkpath $master_dir, !$quiet;
960                 print "header (master) created for $lib\n" unless $quiet;
961                 open MASTERINCLUDE, ">$master_include";
962                 print MASTERINCLUDE $master_contents;
963                 close MASTERINCLUDE;
964             }
965         }
966
967         #handle the headers.pri for each module
968         my $headers_pri_contents = "";
969         $headers_pri_contents .= "SYNCQT.HEADER_FILES = $pri_install_files\n";
970         $headers_pri_contents .= "SYNCQT.HEADER_CLASSES = $pri_install_classes\n";
971         $headers_pri_contents .= "SYNCQT.PRIVATE_HEADER_FILES = $pri_install_pfiles\n";
972         my $headers_pri_file = "$out_basedir/include/$lib/headers.pri";
973         if(-e $headers_pri_file) {
974             open HEADERS_PRI_FILE, "<$headers_pri_file";
975             local $/;
976             binmode HEADERS_PRI_FILE;
977             my $old_headers_pri_contents = <HEADERS_PRI_FILE>;
978             close HEADERS_PRI_FILE;
979             $old_headers_pri_contents =~ s/\r//g; # remove \r's , so comparison is ok on all platforms
980             $headers_pri_file = 0 if($old_headers_pri_contents eq $headers_pri_contents);
981         }
982         if($headers_pri_file && $master_contents) {
983             my $headers_pri_dir = dirname($headers_pri_file);
984             mkpath $headers_pri_dir, !$quiet;
985             print "headers.pri file created for $lib\n" unless $quiet;
986             open HEADERS_PRI_FILE, ">$headers_pri_file";
987             print HEADERS_PRI_FILE $headers_pri_contents;
988             close HEADERS_PRI_FILE;
989         }
990     }
991 }
992 unless($showonly || !$create_uic_class_map) {
993     my $class_lib_map = "$out_basedir/src/tools/uic/qclass_lib_map.h";
994     if(-e $class_lib_map) {
995         open CLASS_LIB_MAP, "<$class_lib_map";
996         local $/;
997         binmode CLASS_LIB_MAP;
998         my $old_class_lib_map_contents = <CLASS_LIB_MAP>;
999         close CLASS_LIB_MAP;
1000         $old_class_lib_map_contents =~ s/\r//g; # remove \r's , so comparison is ok on all platforms
1001         $class_lib_map = 0 if($old_class_lib_map_contents eq $class_lib_map_contents);
1002     }
1003     if($class_lib_map) {
1004         my $class_lib_map_dir = dirname($class_lib_map);
1005         mkpath $class_lib_map_dir, !$quiet;
1006         open CLASS_LIB_MAP, ">$class_lib_map";
1007         print CLASS_LIB_MAP $class_lib_map_contents;
1008         close CLASS_LIB_MAP;
1009     }
1010 }
1011
1012 if($check_includes) {
1013     for my $lib (keys(%modules)) {
1014             #calc subdirs
1015             my @subdirs = ($modules{$lib});
1016             foreach my $subdir (@subdirs) {
1017                 opendir DIR, $subdir or die "Huh, directory ".$subdir." cannot be opened.";
1018                 while(my $t = readdir(DIR)) {
1019                     push @subdirs, "$subdir/$t" if(-d "$subdir/$t" && !($t eq ".") &&
1020                                                    !($t eq "..") && !($t eq ".obj") &&
1021                                                    !($t eq ".moc") && !($t eq ".rcc") &&
1022                                                    !($t eq ".uic") && !($t eq "build"));
1023                 }
1024                 closedir DIR;
1025             }
1026
1027             foreach my $subdir (@subdirs) {
1028                 my $header_skip_qt_module_test = 0;
1029                 foreach(@ignore_for_qt_module_check) {
1030                     foreach (split(/;/, $_)) {
1031                         $header_skip_qt_module_test = 1 if ($subdir =~ /^$_/);
1032                     }
1033                 }
1034                 my @headers = findFiles($subdir, "^[-a-z0-9_]*\\.h\$" , 0);
1035                 foreach my $header (@headers) {
1036                     my $header_skip_qt_begin_header_test = 0;
1037                     my $header_skip_qt_begin_namespace_test = 0;
1038                     $header = 0 if($header =~ /^ui_.*.h/);
1039                     foreach (@ignore_headers) {
1040                         $header = 0 if($header eq $_);
1041                     }
1042                     if($header) {
1043                         my $public_header = $header;
1044                         if($public_header =~ /_p.h$/ || $public_header =~ /_pch.h$/) {
1045                             $public_header = 0;
1046                         } else {
1047                             foreach (@ignore_for_master_contents) {
1048                                 $public_header = 0 if($header eq $_);
1049                             }
1050                             if($public_header) {
1051                                 foreach (@ignore_for_include_check) {
1052                                     $public_header = 0 if($header eq $_);
1053                                 }
1054                                 foreach(@ignore_for_qt_begin_header_check) {
1055                                     $header_skip_qt_begin_header_test = 1 if ($header eq $_);
1056                                 }
1057                                 foreach(@ignore_for_qt_begin_namespace_check) {
1058                                     $header_skip_qt_begin_namespace_test = 1 if ($header eq $_);
1059                                 }
1060                             }
1061                         }
1062
1063                         my $iheader = $subdir . "/" . $header;
1064                         if($public_header) {
1065                             if(open(F, "<$iheader")) {
1066                                 my $qt_module_found = 0;
1067                                 my $qt_begin_header_found = 0;
1068                                 my $qt_end_header_found = 0;
1069                                 my $qt_begin_namespace_found = 0;
1070                                 my $qt_end_namespace_found = 0;
1071                                 my $line;
1072                                 while($line = <F>) {
1073                                     chomp $line;
1074                                     my $output_line = 1;
1075                                     if($line =~ /^ *\# *pragma (qt_no_included_check|qt_sync_stop_processing)/) {
1076                                         last;
1077                                     } elsif($line =~ /^ *\# *include/) {
1078                                         my $include = $line;
1079                                         if($line =~ /<.*>/) {
1080                                             $include =~ s,.*<(.*)>.*,$1,;
1081                                         } elsif($line =~ /".*"/) {
1082                                             $include =~ s,.*"(.*)".*,$1,;
1083                                         } else {
1084                                             $include = 0;
1085                                         }
1086                                         if($include) {
1087                                             for my $trylib (keys(%modules)) {
1088                                                 if(-e "$out_basedir/include/$trylib/$include") {
1089                                                     print "WARNING: $iheader includes $include when it should include $trylib/$include\n";
1090                                                 }
1091                                             }
1092                                         }
1093                                     } elsif ($header_skip_qt_begin_header_test == 0 and $line =~ /^QT_BEGIN_HEADER\s*$/) {
1094                                         $qt_begin_header_found = 1;
1095                                     } elsif ($header_skip_qt_begin_header_test == 0 and $line =~ /^QT_END_HEADER\s*$/) {
1096                                         $qt_end_header_found = 1;
1097                                     } elsif ($header_skip_qt_begin_namespace_test == 0 and $line =~ /^QT_BEGIN_NAMESPACE\s*$/) {
1098                                         $qt_begin_namespace_found = 1;
1099                                     } elsif ($header_skip_qt_begin_namespace_test == 0 and $line =~ /^QT_END_NAMESPACE\s*$/) {
1100                                         $qt_end_namespace_found = 1;
1101                                     } elsif ($header_skip_qt_module_test == 0 and $line =~ /^QT_MODULE\(.*\)\s*$/) {
1102                                         $qt_module_found = 1;
1103                                     }
1104                                 }
1105                                 if ($header_skip_qt_begin_header_test == 0) {
1106                                     if ($qt_begin_header_found == 0) {
1107                                         print "WARNING: $iheader does not include QT_BEGIN_HEADER\n";
1108                                     }
1109
1110                                     if ($qt_begin_header_found && $qt_end_header_found == 0) {
1111                                         print "WARNING: $iheader has QT_BEGIN_HEADER but no QT_END_HEADER\n";
1112                                     }
1113                                 }
1114
1115                                 if ($header_skip_qt_begin_namespace_test == 0) {
1116                                     if ($qt_begin_namespace_found == 0) {
1117                                         print "WARNING: $iheader does not include QT_BEGIN_NAMESPACE\n";
1118                                     }
1119
1120                                     if ($qt_begin_namespace_found && $qt_end_namespace_found == 0) {
1121                                         print "WARNING: $iheader has QT_BEGIN_NAMESPACE but no QT_END_NAMESPACE\n";
1122                                     }
1123                                 }
1124
1125                                 if ($header_skip_qt_module_test == 0) {
1126                                     if ($qt_module_found == 0) {
1127                                         print "WARNING: $iheader does not include QT_MODULE\n";
1128                                     }
1129                                 }
1130                                 close(F);
1131                             }
1132                         }
1133                     }
1134                 }
1135             }
1136     }
1137 }
1138
1139 exit 0;