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