2
######################################################################
4
# Synchronizes Qt header files - internal Trolltech tool.
6
# Copyright (C) 1997-2005 by Trolltech AS. All rights reserved.
8
######################################################################
10
# use packages -------------------------------------------------------
17
die "syncqt: QTDIR not defined" if ! $ENV{"QTDIR"}; # sanity check
21
my $basedir = $ENV{"QTDIR"};
23
my %modules = ( # path to module name map
24
"QtGui" => "$basedir/src/gui",
25
"QtOpenGL" => "$basedir/src/opengl",
26
"QtCore" => "$basedir/src/corelib",
27
"QtXml" => "$basedir/src/xml",
28
"QtSql" => "$basedir/src/sql",
29
"QtNetwork" => "$basedir/src/network",
30
"Qt3Support" => "$basedir/src/qt3support",
31
"ActiveQt" => "$basedir/extensions/activeqt/container;$basedir/extensions/activeqt/control",
32
"QtAssistant" => "$basedir/tools/assistant/lib",
33
"QtDesigner" => "$basedir/tools/designer/src/lib;",
34
"QtMotif" => "$basedir/extensions/motif/src",
35
"QtNsPlugin" => "$basedir/extensions/nsplugin/src",
37
#$modules{"QtCore"} .= ";$basedir/mkspecs/" . $ENV{"MKSPEC"} if defined $ENV{"MKSPEC"};
39
# global variables (modified by options)
44
my $force_relative = 0;
45
my $check_includes = 0;
48
$force_relative = 1 if ( -d "/System/Library/Frameworks" );
49
my $out_basedir = $basedir;
50
$out_basedir =~ s=\\=/=g;
52
# functions ----------------------------------------------------------
54
######################################################################
58
# Purpose: Show the usage of the script.
60
######################################################################
64
print " -copy Copy headers instead of include-fwd(default: " . ($copy_headers ? "yes" : "no") . ")\n";
65
print " -remove-stale Removes stale headers (default: " . ($remove_stale ? "yes" : "no") . ")\n";
66
print " -relative Force relative symlinks (default: " . ($force_relative ? "yes" : "no") . ")\n";
67
print " -windows Force platform to Windows (default: " . ($force_win ? "yes" : "no") . ")\n";
68
print " -showonly Show action but not perform (default: " . ($showonly ? "yes" : "no") . ")\n";
69
print " -outdir <PATH> Specify output directory for sync (default: $out_basedir)\n";
70
print " -help This help\n";
74
######################################################################
78
# Purpose: Check if script runs on a Unix system or not. Cygwin
79
# systems are _not_ detected as Unix systems.
80
# Returns: 1 if a unix system, else 0.
81
######################################################################
84
if ( $force_win != 0) {
86
} elsif ( -f "/bin/uname" ) {
88
(-f "\\bin\\uname") && ($r = 0);
89
} elsif ( -f "/usr/bin/uname" ) {
91
(-f "\\usr\\bin\\uname") && ($r = 0);
94
$_ = $Config{'osname'};
95
$r = 0 if( /(ms)|(cyg)win/i );
100
######################################################################
101
# Syntax: shouldMasterInclude(iheader)
102
# Params: iheader, string, filename to verify inclusion
104
# Purpose: Determines if header should be in the master include file.
105
# Returns: 0 if file contains "#pragma qt_no_master_include" or not
106
# able to open, else 1.
107
######################################################################
108
sub shouldMasterInclude {
110
return 0 if(basename($iheader) =~ /_/);
111
return 0 if(basename($iheader) =~ /qconfig/);
112
if(open(F, "<$iheader")) {
115
return 0 if(/^\#pragma qt_no_master_include$/);
124
######################################################################
125
# Syntax: classNames(iheader)
126
# Params: iheader, string, filename to parse for classname "symlinks"
128
# Purpose: Scans through iheader to find all classnames that should be
129
# synced into library's include structure.
130
# Returns: List of all class names in a file.
131
######################################################################
135
if(basename($iheader) eq "qglobal.h") {
136
push @ret, "QtGlobal";
137
} elsif(basename($iheader) eq "qalgorithms.h") {
138
push @ret, "QtAlgorithms";
139
} elsif(basename($iheader) eq "qdebug.h") {
140
push @ret, "QtDebug";
141
} elsif(basename($iheader) eq "qnamespace.h") {
146
if(open(F, "<$iheader")) {
154
last unless($line =~ /\\$/);
160
$line =~ s,//.*$,,; #remove c++ comments
161
$line .= ";" if($line =~ m/^Q_[A-Z_]*\(.*\)[\r\n]*$/); #qt macro
162
$parsable .= " " . $line;
168
my $last_definition = 0;
169
for(my $i = 0; $i < length($parsable); $i++) {
171
my $character = substr($parsable, $i, 1);
172
if($character eq "/" && substr($parsable, $i+1, 1) eq "*") { #I parse like this for greedy reasons
173
for($i+=2; $i < length($parsable); $i++) {
174
my $end = substr($parsable, $i, 2);
176
$last_definition = $i+2;
181
} elsif($character eq "{") {
183
my $block_start = $i + 1;
184
BLOCK: for($i+=1; $i < length($parsable); $i++) {
185
my $ignore = substr($parsable, $i, 1);
188
} elsif($ignore eq "}") {
190
unless($brace_depth) {
191
for(my $i2 = $i+1; $i2 < length($parsable); $i2++) {
192
my $end = substr($parsable, $i2, 1);
193
if($end eq ";" || $end ne " ") {
194
$definition = substr($parsable, $last_definition, $block_start - $last_definition) . "}";
195
$i = $i2 if($end eq ";");
196
$last_definition = $i + 1;
203
} elsif($character eq ";") {
204
$definition = substr($parsable, $last_definition, $i - $last_definition + 1);
205
$last_definition = $i + 1;
208
$definition =~ s=[\n\r]==g;
210
if($definition =~ m/^ *typedef *.*\(\*([^\)]*)\)\(.*\);$/) {
212
} elsif($definition =~ m/^ *typedef +(.*) +([^ ]*);$/) {
214
} elsif($definition =~ m/^ *(template *<.*> *)?(class|struct) +([^ ]* +)?([^<\s]+) ?(<[^>]*> ?)?\s*((,|:)\s*(public|private) *.*)? *\{\}$/) {
216
} elsif($definition =~ m/^ *Q_DECLARE_.*ITERATOR\((.*)\);$/) {
217
push @symbols, "Q" . $1 . "Iterator";
218
push @symbols, "QMutable" . $1 . "Iterator";
223
push @ret, $symbol if($symbol =~ /^Q/);
230
######################################################################
231
# Syntax: syncHeader(header, iheader, copy)
232
# Params: header, string, filename to create "symlink" for
233
# iheader, string, destination name of symlink
234
# copy, forces header to be a copy of iheader
236
# Purpose: Syncronizes header to iheader
237
# Returns: 1 if successful, else 0.
238
######################################################################
240
my ($header, $iheader, $copy) = @_;
241
$iheader =~ s=\\=/=g;
243
return copyFile($iheader, $header) if($copy);
245
my $iheader_no_basedir = $iheader;
246
$iheader_no_basedir =~ s,^$basedir/?,,;
248
unless(-e "$header") {
249
my $header_dir = dirname($header);
250
mkpath $header_dir, 0777;
253
my $iheader_out = fixPaths($iheader, $header_dir);
254
open HEADER, ">$header" || die "Could not open $header for writing!\n";
255
print HEADER "#include \"$iheader_out\"\n";
262
######################################################################
263
# Syntax: fixPaths(file, dir)
264
# Params: file, string, filepath to be made relative to dir
265
# dir, string, dirpath for point of origin
267
# Purpose: file is made relative (if possible) of dir.
268
# Returns: String with the above applied conversion.
269
######################################################################
271
my ($file, $dir) = @_;
272
$dir =~ s=^$basedir=$out_basedir= if(!($basedir eq $out_basedir));
278
my $file_dir = dirname($file);
279
if($file_dir eq ".") {
280
$file_dir = getcwd();
281
$file_dir =~ s=\\=/=g;
283
$file_dir =~ s,/cygdrive/([a-zA-Z])/,$1:,g;
288
$dir =~ s,/cygdrive/([a-zA-Z])/,$1:/,g;
289
return basename($file) if("$file_dir" eq "$dir");
293
for(my $i = 1; $i < length($file_dir); $i++) {
294
my $slash = index($file_dir, "/", $i);
295
last if($slash == -1);
296
my $tmp = substr($file_dir, 0, $slash);
297
last unless($dir =~ m,^$tmp/,);
302
my $after = substr($dir, length($match_dir));
303
my $count = ($after =~ tr,/,,);
305
for(my $i = 0; $i < $count; $i++) {
308
$ret =~ s,^$match_dir,$dots,;
314
######################################################################
315
# Syntax: fileContents(filename)
316
# Params: filename, string, filename of file to return contents
318
# Purpose: Get the contents of a file.
319
# Returns: String with contents of the file, or empty string if file
321
# Warning: Dies if it does exist but script cannot get read access.
322
######################################################################
325
my $filecontents = "";
327
open(I, "< $filename") || die "Could not open $filename for reading, read block?";
333
return $filecontents;
336
######################################################################
337
# Syntax: fileCompare(file1, file2)
338
# Params: file1, string, filename of first file
339
# file2, string, filename of second file
341
# Purpose: Determines if files are equal, and which one is newer.
342
# Returns: 0 if files are equal no matter the timestamp, -1 if file1
343
# is newer, 1 if file2 is newer.
344
######################################################################
346
my ($file1, $file2) = @_;
347
my $file1contents = fileContents($file1);
348
my $file2contents = fileContents($file2);
349
if (! -e $file1) { return 1; }
350
if (! -e $file2) { return -1; }
351
return $file1contents ne $file2contents ? (stat("$file2"))[9] <=> (stat("$file1"))[9] : 0;
354
######################################################################
355
# Syntax: copyFile(file, ifile)
356
# Params: file, string, filename to create duplicate for
357
# ifile, string, destination name of duplicate
359
# Purpose: Keeps files in sync so changes in the newer file will be
360
# written to the other.
361
# Returns: 1 if files were synced, else 0.
362
# Warning: Dies if script cannot get write access.
363
######################################################################
366
my ($file,$ifile, $copy,$knowdiff,$filecontents,$ifilecontents) = @_;
367
# Bi-directional synchronization
368
open( I, "< " . $file ) || die "Could not open $file for reading";
373
if ( open(I, "< " . $ifile) ) {
376
$ifilecontents = <I>;
378
$copy = fileCompare($file, $ifile);
385
if ( $knowdiff || ($filecontents ne $ifilecontents) ) {
387
my $file_dir = dirname($file);
388
mkpath $file_dir, 0777 unless(-e "$file_dir");
389
open(O, "> " . $file) || die "Could not open $file for writing (no write permission?)";
392
print O $ifilecontents;
395
} elsif ( $copy < 0 ) {
396
my $ifile_dir = dirname($ifile);
397
mkpath $ifile_dir, 0777 unless(-e "$ifile_dir");
398
open(O, "> " . $ifile) || die "Could not open $ifile for writing (no write permission?)";
401
print O $filecontents;
409
######################################################################
410
# Syntax: symlinkFile(file, ifile)
411
# Params: file, string, filename to create "symlink" for
412
# ifile, string, destination name of symlink
414
# Purpose: File is symlinked to ifile (or copied if filesystem doesn't
416
# Returns: 1 on success, else 0.
417
######################################################################
420
my ($file,$ifile) = @_;
423
print "symlink created for $file ";
424
if ( $force_relative && ($ifile =~ /^$basedir/)) {
428
$t =~ s-^$basedir/--;
429
$p .= "../" while( ($c = index( $t, "/", $c + 1)) != -1 );
430
$file =~ s-^$basedir/-$p-;
434
return symlink($file, $ifile);
436
return copyFile($file, $ifile);
439
######################################################################
440
# Syntax: findFiles(dir, match, descend)
441
# Params: dir, string, directory to search for name
442
# match, string, regular expression to match in dir
443
# descend, integer, 0 = non-recursive search
444
# 1 = recurse search into subdirectories
446
# Purpose: Finds files matching a regular expression.
447
# Returns: List of matching files.
450
# findFiles("/usr","\.cpp$",1) - finds .cpp files in /usr and below
451
# findFiles("/tmp","^#",0) - finds #* files in /tmp
452
######################################################################
454
my ($dir,$match,$descend) = @_;
455
my ($file,$p,@files);
458
($dir eq "") && ($dir = ".");
459
if ( opendir(D,$dir) ) {
463
($dir =~ /\/$/) || ($dir .= "/");
465
foreach $file ( readdir(D) ) {
466
next if ( $file =~ /^\.\.?$/ );
468
($file =~ /$match/) && (push @files, $p);
469
if ( $descend && -d $p && ! -l $p ) {
470
push @files, &findFiles($p,$match,$descend);
478
# --------------------------------------------------------------------
480
# --------------------------------------------------------------------
487
my $arg = shift @ARGV;
488
if ("$arg" eq "-h" || "$arg" eq "-help" || "$arg" eq "?") {
491
} elsif("$arg" eq "-copy") {
494
} elsif("$arg" eq "-o" || "$arg" eq "-outdir") {
497
} elsif("$arg" eq "-showonly" || "$arg" eq "-remove-stale" || "$arg" eq "-windows" ||
498
"$arg" eq "-relative" || "$arg" eq "-check-includes") {
499
$var = substr($arg, 1);
501
} elsif("$arg" =~ /^-no-(.*)$/) {
504
#these are for commandline compat
505
} elsif("$arg" eq "-inc") {
508
} elsif("$arg" eq "-module") {
511
} elsif("$arg" eq "-show") {
514
} elsif("$arg" eq '*') {
515
# workaround for windows 9x where "%*" expands to "*"
520
if(!$var || "$var" eq "show_help") {
521
print "Unknown option: $arg\n\n" if(!$var);
523
} elsif ("$var" eq "copy") {
524
if("$val" eq "yes") {
529
} elsif ("$var" eq "showonly") {
530
if("$val" eq "yes") {
535
} elsif ("$var" eq "check-includes") {
536
if("$val" eq "yes") {
538
} elsif($check_includes) {
541
} elsif ("$var" eq "remove-stale") {
542
if("$val" eq "yes") {
544
} elsif($remove_stale) {
547
} elsif ("$var" eq "windows") {
548
if("$val" eq "yes") {
550
} elsif($force_win) {
553
} elsif ("$var" eq "relative") {
554
if("$val" eq "yes") {
556
} elsif($force_relative) {
559
} elsif ("$var" eq "module") {
560
print "module :$val:\n";
561
die "No such module: $val" unless(defined $modules{$val});
562
push @modules_to_sync, $val;
563
} elsif ("$var" eq "output") {
565
if($outdir !~ /^\//) {
566
$out_basedir = getcwd();
568
$out_basedir .= "/" . $outdir;
570
$out_basedir = $outdir;
573
$out_basedir =~ s=\\=/=g;
576
@modules_to_sync = keys(%modules) if($#modules_to_sync == -1);
578
$isunix = checkUnix; #cache checkUnix
581
mkpath "$out_basedir/include", 0777;
583
my @ignore_headers = ();
584
my $class_lib_map_contents = "";
585
my @ignore_for_master_contents = ( "qt.h", "qpaintdevicedefs.h" );
586
my @ignore_for_include_check = ( "qatomic.h" );
588
foreach (@modules_to_sync) {
591
my $dir = "$modules{$lib}";
593
#information used after the syncing
594
my $master_contents = "";
595
my $pri_install_classes = "";
596
my $pri_install_files = "";
599
if(-e "$dir/" . basename($dir) . ".pro") {
600
if(open(F, "<$dir/" . basename($dir) . ".pro")) {
604
if($line =~ /^ *QT *\+?= *(.*)/) {
605
foreach(split(/ /, "$1")) {
606
$master_contents .= "#include <QtCore/QtCore>\n" if("$_" eq "core");
607
$master_contents .= "#include <QtGui/QtGui>\n" if("$_" eq "gui");
608
$master_contents .= "#include <QtNetwork/QtNetwork>\n" if("$_" eq "network");
609
$master_contents .= "#include <Qt3Support/Qt3Support>\n" if("$_" eq "qt3support");
610
$master_contents .= "#include <QtSql/QtSql>\n" if("$_" eq "sql");
611
$master_contents .= "#include <QtXml/QtXml>\n" if("$_" eq "xml");
612
$master_contents .= "#include <QtOpenGL/QtOpenGL>\n" if("$_" eq "opengl");
620
#remove the old files
622
my @subdirs = ("$out_basedir/include/$lib");
625
opendir DIR, "$subdir";
626
while(my $t = readdir(DIR)) {
627
my $file = "$subdir/$t";
629
push @subdirs, "$file" unless($t eq "." || $t eq ".." || $t eq "arch");
631
my @files = ("$file");
632
push @files, "$out_basedir/include/Qt/$t" if(-e "$out_basedir/include/Qt/$t");
636
if(open(F, "<$file")) {
640
if($line =~ /^\#include \"([^\"]*)\"$/) {
642
$include = $subdir . "/" . $include unless(substr($include, 0, 1) eq "/");
643
$remove_file = 1 unless(-e "$include");
650
unlink "$file" if($remove_file);
660
foreach (split(/;/, $dir)) {
661
my $current_dir = "$_";
663
my @subdirs = ($current_dir);
666
opendir DIR, "$subdir";
667
while(my $t = readdir(DIR)) {
668
push @subdirs, "$subdir/$t" if(-d "$subdir/$t" && !($t eq ".") &&
669
!($t eq "..") && !($t eq "arch"));
674
#calc files and "copy" them
677
my @headers = findFiles("$subdir", "^[-a-z0-9_]*\\.h\$" , 0);
680
$header = 0 if("$header" =~ /^ui_.*.h/);
681
foreach (@ignore_headers) {
682
$header = 0 if("$header" eq "$_");
685
my $header_copies = 0;
686
#figure out if it is a public header
687
my $public_header = $header;
688
if($public_header =~ /_p.h$/ || $public_header =~ /_pch.h$/) {
691
foreach (@ignore_for_master_contents) {
692
$public_header = 0 if("$header" eq "$_");
696
my $iheader = $subdir . "/" . $header;
697
my @classes = $public_header ? classNames($iheader) : ();
699
print "$header [$lib]\n";
701
print "SYMBOL: $_\n";
704
#find out all the places it goes..
706
if ($public_header) {
707
@headers = ( "$out_basedir/include/Qt/$header", "$out_basedir/include/$lib/$header" );
710
my $header_base = basename($header);
711
$class_lib_map_contents .= "QT_CLASS_LIB($_, $lib, $header_base)\n";
712
$header_copies++ if(syncHeader("$out_basedir/include/$lib/$class", $header, 0));
715
@headers = ( "$out_basedir/include/Qt/private/$header",
716
"$out_basedir/include/$lib/private/$header" );
718
foreach(@headers) { #sync them
719
$header_copies++ if(syncHeader($_, $iheader, $copy_headers));
723
#put it into the master file
724
$master_contents .= "#include \"$public_header\"\n" if(shouldMasterInclude($iheader));
726
#deal with the install directives
728
my $pri_install_iheader = fixPaths($iheader, $current_dir);
730
my $class_header = fixPaths("$basedir/include/$lib/$_",
732
$pri_install_classes .= $class_header
733
unless($pri_install_classes =~ $class_header);
735
$pri_install_files .= "$pri_install_iheader ";;
739
print "header created for $iheader ($header_copies)\n" if($header_copies > 0);
746
#generate the "master" include file
747
my $master_include = "$out_basedir/include/$lib/$lib";
748
$pri_install_files .= fixPaths($master_include, "$basedir/src/$lib") . " "; #get the master file installed too
749
if(-e "$master_include") {
750
open MASTERINCLUDE, "<$master_include";
752
binmode MASTERINCLUDE;
753
my $oldmaster = <MASTERINCLUDE>;
755
$oldmaster =~ s/\r//g; # remove \r's , so comparison is ok on all platforms
756
$master_include = 0 if($oldmaster eq $master_contents);
758
if($master_include) {
759
my $master_dir = dirname($master_include);
760
mkpath $master_dir, 0777;
761
print "header (master) created for $lib\n";
762
open MASTERINCLUDE, ">$master_include";
763
print MASTERINCLUDE "$master_contents";
767
#handle the headers.pri for each module
768
my $headers_pri_contents = "";
769
$headers_pri_contents .= "SYNCQT.HEADER_FILES = $pri_install_files\n";
770
$headers_pri_contents .= "SYNCQT.HEADER_CLASSES = $pri_install_classes\n";
771
my $headers_pri_file = "$out_basedir/include/$lib/headers.pri";
772
if(-e "$headers_pri_file") {
773
open HEADERS_PRI_FILE, "<$headers_pri_file";
775
binmode HEADERS_PRI_FILE;
776
my $old_headers_pri_contents = <HEADERS_PRI_FILE>;
777
close HEADERS_PRI_FILE;
778
$old_headers_pri_contents =~ s/\r//g; # remove \r's , so comparison is ok on all platforms
779
$headers_pri_file = 0 if($old_headers_pri_contents eq $headers_pri_contents);
781
if($headers_pri_file) {
782
my $headers_pri_dir = dirname($headers_pri_file);
783
mkpath $headers_pri_dir, 0777;
784
print "headers.pri file created for $lib\n";
785
open HEADERS_PRI_FILE, ">$headers_pri_file";
786
print HEADERS_PRI_FILE "$headers_pri_contents";
787
close HEADERS_PRI_FILE;
792
my $class_lib_map = "$out_basedir/src/tools/uic/qclass_lib_map.h";
793
if(-e "$class_lib_map") {
794
open CLASS_LIB_MAP, "<$class_lib_map";
796
binmode CLASS_LIB_MAP;
797
my $old_class_lib_map_contents = <CLASS_LIB_MAP>;
799
$old_class_lib_map_contents =~ s/\r//g; # remove \r's , so comparison is ok on all platforms
800
$class_lib_map = 0 if($old_class_lib_map_contents eq $class_lib_map_contents);
803
my $class_lib_map_dir = dirname($class_lib_map);
804
mkpath $class_lib_map_dir, 0777;
805
open CLASS_LIB_MAP, ">$class_lib_map";
806
print CLASS_LIB_MAP "$class_lib_map_contents";
811
if($check_includes) {
812
for (keys(%modules)) {
815
my $dir = "$modules{$lib}";
816
foreach (split(/;/, $dir)) {
817
my $current_dir = "$_";
819
my @subdirs = ($current_dir);
822
opendir DIR, "$subdir";
823
while(my $t = readdir(DIR)) {
824
push @subdirs, "$subdir/$t" if(-d "$subdir/$t" && !($t eq ".") &&
825
!($t eq "..") && !($t eq "arch"));
832
my @headers = findFiles("$subdir", "^[-a-z0-9_]*\\.h\$" , 0);
835
$header = 0 if("$header" =~ /^ui_.*.h/);
836
foreach (@ignore_headers) {
837
$header = 0 if("$header" eq "$_");
840
my $public_header = $header;
841
if($public_header =~ /_p.h$/ || $public_header =~ /_pch.h$/) {
844
foreach (@ignore_for_master_contents) {
845
$public_header = 0 if("$header" eq "$_");
848
foreach (@ignore_for_include_check) {
849
$public_header = 0 if("$header" eq "$_");
854
my $iheader = $subdir . "/" . $header;
856
if(open(F, "<$iheader")) {
861
if($line =~ /^ *\# *pragma qt_no_included_check/) {
863
} elsif($line =~ /^ *\# *include/) {
865
if($line =~ /<.*>/) {
866
$include =~ s,.*<(.*)>.*,$1,;
867
} elsif($line =~ /".*"/) {
868
$include =~ s,.*"(.*)".*,$1,;
873
for (keys(%modules)) {
875
if(-e "$out_basedir/include/$trylib/$include") {
876
print "WARNING: $iheader includes $include when it should include $trylib/$include\n";