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