Home | History | Annotate | Download | only in Scripts
      1 #!/usr/bin/perl -w
      2 
      3 # Copyright (C) 2007, 2008, 2009, 2010 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::Spec;
     35 use File::Temp qw(tempfile);
     36 use Getopt::Long;
     37 
     38 sub sortChildrenByFileName($$);
     39 sub sortFilesByFileName($$);
     40 
     41 # Files (or products) without extensions
     42 my %isFile = map { $_ => 1 } qw(
     43     create_hash_table
     44     jsc
     45     minidom
     46     testapi
     47     testjsglue
     48 );
     49 
     50 my $printWarnings = 1;
     51 my $showHelp;
     52 
     53 my $getOptionsResult = GetOptions(
     54     'h|help'         => \$showHelp,
     55     'w|warnings!'    => \$printWarnings,
     56 );
     57 
     58 if (scalar(@ARGV) == 0 && !$showHelp) {
     59     print STDERR "ERROR: No Xcode project files (project.pbxproj) listed on command-line.\n";
     60     undef $getOptionsResult;
     61 }
     62 
     63 if (!$getOptionsResult || $showHelp) {
     64     print STDERR <<__END__;
     65 Usage: @{[ basename($0) ]} [options] path/to/project.pbxproj [path/to/project.pbxproj ...]
     66   -h|--help           show this help message
     67   -w|--[no-]warnings  show or suppress warnings (default: show warnings)
     68 __END__
     69     exit 1;
     70 }
     71 
     72 for my $projectFile (@ARGV) {
     73     if (basename($projectFile) =~ /\.xcodeproj$/) {
     74         $projectFile = File::Spec->catfile($projectFile, "project.pbxproj");
     75     }
     76 
     77     if (basename($projectFile) ne "project.pbxproj") {
     78         print STDERR "WARNING: Not an Xcode project file: $projectFile\n" if $printWarnings;
     79         next;
     80     }
     81 
     82     # Grab the mainGroup for the project file
     83     my $mainGroup = "";
     84     open(IN, "< $projectFile") || die "Could not open $projectFile: $!";
     85     while (my $line = <IN>) {
     86         $mainGroup = $2 if $line =~ m#^(\s*)mainGroup = ([0-9A-F]{24} /\* .+ \*/);$#;
     87     }
     88     close(IN);
     89 
     90     my ($OUT, $tempFileName) = tempfile(
     91         basename($projectFile) . "-XXXXXXXX",
     92         DIR => dirname($projectFile),
     93         UNLINK => 0,
     94     );
     95 
     96     # Clean up temp file in case of die()
     97     $SIG{__DIE__} = sub {
     98         close(IN);
     99         close($OUT);
    100         unlink($tempFileName);
    101     };
    102 
    103     my @lastTwo = ();
    104     open(IN, "< $projectFile") || die "Could not open $projectFile: $!";
    105     while (my $line = <IN>) {
    106         if ($line =~ /^(\s*)files = \(\s*$/) {
    107             print $OUT $line;
    108             my $endMarker = $1 . ");";
    109             my @files;
    110             while (my $fileLine = <IN>) {
    111                 if ($fileLine =~ /^\Q$endMarker\E\s*$/) {
    112                     $endMarker = $fileLine;
    113                     last;
    114                 }
    115                 push @files, $fileLine;
    116             }
    117             print $OUT sort sortFilesByFileName @files;
    118             print $OUT $endMarker;
    119         } elsif ($line =~ /^(\s*)children = \(\s*$/) {
    120             print $OUT $line;
    121             my $endMarker = $1 . ");";
    122             my @children;
    123             while (my $childLine = <IN>) {
    124                 if ($childLine =~ /^\Q$endMarker\E\s*$/) {
    125                     $endMarker = $childLine;
    126                     last;
    127                 }
    128                 push @children, $childLine;
    129             }
    130             if ($lastTwo[0] =~ m#^\s+\Q$mainGroup\E = \{$#) {
    131                 # Don't sort mainGroup
    132                 print $OUT @children;
    133             } else {
    134                 print $OUT sort sortChildrenByFileName @children;
    135             }
    136             print $OUT $endMarker;
    137         } else {
    138             print $OUT $line;
    139         }
    140 
    141         push @lastTwo, $line;
    142         shift @lastTwo if scalar(@lastTwo) > 2;
    143     }
    144     close(IN);
    145     close($OUT);
    146 
    147     unlink($projectFile) || die "Could not delete $projectFile: $!";
    148     rename($tempFileName, $projectFile) || die "Could not rename $tempFileName to $projectFile: $!";
    149 }
    150 
    151 exit 0;
    152 
    153 sub sortChildrenByFileName($$)
    154 {
    155     my ($a, $b) = @_;
    156     my $aFileName = $1 if $a =~ /^\s*[A-Z0-9]{24} \/\* (.+) \*\/,$/;
    157     my $bFileName = $1 if $b =~ /^\s*[A-Z0-9]{24} \/\* (.+) \*\/,$/;
    158     my $aSuffix = $1 if $aFileName =~ m/\.([^.]+)$/;
    159     my $bSuffix = $1 if $bFileName =~ m/\.([^.]+)$/;
    160     if ((!$aSuffix && !$isFile{$aFileName} && $bSuffix) || ($aSuffix && !$bSuffix && !$isFile{$bFileName})) {
    161         return !$aSuffix ? -1 : 1;
    162     }
    163     return lc($aFileName) cmp lc($bFileName);
    164 }
    165 
    166 sub sortFilesByFileName($$)
    167 {
    168     my ($a, $b) = @_;
    169     my $aFileName = $1 if $a =~ /^\s*[A-Z0-9]{24} \/\* (.+) in /;
    170     my $bFileName = $1 if $b =~ /^\s*[A-Z0-9]{24} \/\* (.+) in /;
    171     return lc($aFileName) cmp lc($bFileName);
    172 }
    173