sort-Xcode-project-file.pl 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. #!/usr/bin/env perl
  2. # Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
  3. #
  4. # Redistribution and use in source and binary forms, with or without
  5. # modification, are permitted provided that the following conditions
  6. # are met:
  7. #
  8. # 1. Redistributions of source code must retain the above copyright
  9. # notice, this list of conditions and the following disclaimer.
  10. # 2. Redistributions in binary form must reproduce the above copyright
  11. # notice, this list of conditions and the following disclaimer in the
  12. # documentation and/or other materials provided with the distribution.
  13. # 3. Neither the name of Apple Inc. ("Apple") nor the names of
  14. # its contributors may be used to endorse or promote products derived
  15. # from this software without specific prior written permission.
  16. #
  17. # THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
  18. # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19. # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20. # DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
  21. # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  22. # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  24. # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26. # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. # Script to sort "children" and "files" sections in Xcode project.pbxproj files
  28. use strict;
  29. use warnings;
  30. use File::Basename;
  31. use File::Spec;
  32. use File::Temp qw(tempfile);
  33. use Getopt::Long;
  34. sub sortChildrenByFileName($$);
  35. sub sortFilesByFileName($$);
  36. # Files (or products) without extensions
  37. my %isFile = map { $_ => 1 } qw(
  38. create_hash_table
  39. jsc
  40. minidom
  41. testapi
  42. testjsglue
  43. );
  44. my $printWarnings = 1;
  45. my $showHelp;
  46. my $getOptionsResult = GetOptions(
  47. 'h|help' => \$showHelp,
  48. 'w|warnings!' => \$printWarnings,
  49. );
  50. if (scalar(@ARGV) == 0 && !$showHelp) {
  51. print STDERR "ERROR: No Xcode project files (project.pbxproj) listed on command-line.\n";
  52. undef $getOptionsResult;
  53. }
  54. if (!$getOptionsResult || $showHelp) {
  55. print STDERR <<__END__;
  56. Usage: @{[ basename($0) ]} [options] path/to/project.pbxproj [path/to/project.pbxproj ...]
  57. -h|--help show this help message
  58. -w|--[no-]warnings show or suppress warnings (default: show warnings)
  59. __END__
  60. exit 1;
  61. }
  62. for my $projectFile (@ARGV) {
  63. if (basename($projectFile) =~ /\.xcodeproj$/) {
  64. $projectFile = File::Spec->catfile($projectFile, "project.pbxproj");
  65. }
  66. if (basename($projectFile) ne "project.pbxproj") {
  67. print STDERR "WARNING: Not an Xcode project file: $projectFile\n" if $printWarnings;
  68. next;
  69. }
  70. # Grab the mainGroup for the project file.
  71. my $mainGroup = "";
  72. open(IN, "< $projectFile") || die "Could not open $projectFile: $!";
  73. while (my $line = <IN>) {
  74. $mainGroup = $2 if $line =~ m#^(\s*)mainGroup = ([0-9A-F]{24}( /\* .+ \*/)?);$#;
  75. }
  76. close(IN);
  77. my ($OUT, $tempFileName) = tempfile(
  78. basename($projectFile) . "-XXXXXXXX",
  79. DIR => dirname($projectFile),
  80. UNLINK => 0,
  81. );
  82. # Clean up temp file in case of die()
  83. $SIG{__DIE__} = sub {
  84. close(IN);
  85. close($OUT);
  86. unlink($tempFileName);
  87. };
  88. my @lastTwo = ();
  89. open(IN, "< $projectFile") || die "Could not open $projectFile: $!";
  90. while (my $line = <IN>) {
  91. if ($line =~ /^(\s*)files = \(\s*$/) {
  92. print $OUT $line;
  93. my $endMarker = $1 . ");";
  94. my @files;
  95. while (my $fileLine = <IN>) {
  96. if ($fileLine =~ /^\Q$endMarker\E\s*$/) {
  97. $endMarker = $fileLine;
  98. last;
  99. }
  100. push @files, $fileLine;
  101. }
  102. print $OUT sort sortFilesByFileName @files;
  103. print $OUT $endMarker;
  104. } elsif ($line =~ /^(\s*)children = \(\s*$/) {
  105. print $OUT $line;
  106. my $endMarker = $1 . ");";
  107. my @children;
  108. while (my $childLine = <IN>) {
  109. if ($childLine =~ /^\Q$endMarker\E\s*$/) {
  110. $endMarker = $childLine;
  111. last;
  112. }
  113. push @children, $childLine;
  114. }
  115. if ($lastTwo[0] =~ m#^\s+\Q$mainGroup\E = \{$#) {
  116. # Don't sort mainGroup
  117. print $OUT @children;
  118. } else {
  119. print $OUT sort sortChildrenByFileName @children;
  120. }
  121. print $OUT $endMarker;
  122. } else {
  123. print $OUT $line;
  124. }
  125. push @lastTwo, $line;
  126. shift @lastTwo if scalar(@lastTwo) > 2;
  127. }
  128. close(IN);
  129. close($OUT);
  130. unlink($projectFile) || die "Could not delete $projectFile: $!";
  131. rename($tempFileName, $projectFile) || die "Could not rename $tempFileName to $projectFile: $!";
  132. }
  133. exit 0;
  134. sub sortChildrenByFileName($$)
  135. {
  136. my ($a, $b) = @_;
  137. my $aFileName = $1 if $a =~ /^\s*[A-Z0-9]{24} \/\* (.+) \*\/,$/;
  138. my $bFileName = $1 if $b =~ /^\s*[A-Z0-9]{24} \/\* (.+) \*\/,$/;
  139. my $aSuffix = $1 if $aFileName =~ m/\.([^.]+)$/;
  140. my $bSuffix = $1 if $bFileName =~ m/\.([^.]+)$/;
  141. if ((!$aSuffix && !$isFile{$aFileName} && $bSuffix) || ($aSuffix && !$bSuffix && !$isFile{$bFileName})) {
  142. return !$aSuffix ? -1 : 1;
  143. }
  144. if ($aFileName =~ /^UnifiedSource\d+/ && $bFileName =~ /^UnifiedSource\d+/) {
  145. my $aNumber = $1 if $aFileName =~ /^UnifiedSource(\d+)/;
  146. my $bNumber = $1 if $bFileName =~ /^UnifiedSource(\d+)/;
  147. return $aNumber <=> $bNumber;
  148. }
  149. return lc($aFileName) cmp lc($bFileName);
  150. }
  151. sub sortFilesByFileName($$)
  152. {
  153. my ($a, $b) = @_;
  154. my $aFileName = $1 if $a =~ /^\s*[A-Z0-9]{24} \/\* (.+) in /;
  155. my $bFileName = $1 if $b =~ /^\s*[A-Z0-9]{24} \/\* (.+) in /;
  156. if ($aFileName =~ /^UnifiedSource\d+/ && $bFileName =~ /^UnifiedSource\d+/) {
  157. my $aNumber = $1 if $aFileName =~ /^UnifiedSource(\d+)/;
  158. my $bNumber = $1 if $bFileName =~ /^UnifiedSource(\d+)/;
  159. return $aNumber <=> $bNumber;
  160. }
  161. return lc($aFileName) cmp lc($bFileName);
  162. }