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