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