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