1#!/usr/bin/perl -w 2 3# This script applies 'lipo' to directories. For each file present in 4# any source directory, it will create a file in the destination directory. 5# If all the copies of the file in the source directories are the same, 6# the file is copied directly to the destination. If there are different 7# files in different directories, but the files are executable, 8# lipo is run to merge the files together. Otherwise, an error is 9# produced. 10 11# Device files and pipes are not supported, but symlinks are; the script 12# will check that all the symlinks point to the same place. 13# If a file is present in only one of the source directories, it is 14# trivially the same as itself and so it is just copied to the destination. 15# If there is only one source directory, the script is just an expensive 16# version of 'cp -rp'. 17# The destination directory is not emptied by this script. 18 19use File::Find (); 20use File::Copy (); 21use File::Compare (); 22use POSIX (); 23 24if ($#ARGV < 1) { 25 print "usage: $0 <source-directories ...> <dest-directory>\n"; 26 exit 1; 27} 28 29# The source directories. 30my @sources = @ARGV[0..$#ARGV-1]; 31# The destination directory. 32my $dest = $ARGV[-1]; 33 34# Count of errors. 35my $errors = 0; 36 37# A hash of the files that need to be lipoed. Only the keys of the 38# hash are significant. 39my %needs_lipo = (); 40 41# Print an error message. 42sub error { 43 printf STDERR "%s\n",$_[0]; 44 $errors++; 45} 46 47# First, scan the directories; copy any files to the destination that are 48# identical in the source directory, print error messages, and make 49# a list of the files that need lipo-ing. 50foreach my $s (@sources) 51{ 52 sub eachfile { 53 my $destname = $dest . '/' . $File::Find::name; 54 if (-d) { 55 (mkdir $destname or error ("$destname: $!")) unless (-d $destname); 56 } elsif (-l) { 57 my $link1 = readlink; 58 defined $link1 or error ("$s/$_: $!"); 59 if (-l $destname) { 60 my $link2 = readlink $destname; 61 defined $link2 or error ("$destname: $!"); 62 $link1 eq $link2 or error ("$destname: different symlinks"); 63 } elsif (-e $destname) { 64 error ("$destname: symlink vs. real file"); 65 } else { 66 symlink $link1,$destname or error ("$destname: $!"); 67 } 68 } elsif (! -f) { 69 error ("$destname: no idea how to handle special file"); 70 } elsif (! -f $destname) { 71 system ("cp", "-p", $_, $destname) == 0 or $errors++; 72 } elsif (File::Compare::compare ($_, $destname) != 0) { 73 $needs_lipo{$File::Find::name} = 1; 74 } 75 } 76 77 chdir $s; 78 File::Find::find (\&eachfile, '.'); 79} 80 81# Run lipo. 82foreach my $l (keys %needs_lipo) { 83 # A list of the files to run lipo on. 84 my @f = (); 85 # Work out which source directories actually contain this file and 86 # fill @f. 87 foreach my $s (@sources) { 88 my $sf = $s . '/' . $l; 89 push @f,($sf) if (-f $sf); 90 } 91 die "inconsistent directories" if ($#f == -1); 92 # There might really only be one copy if the file was present 93 # in the tree before the script was started. 94 if ($#f == 0) { 95 system ("cp", "-p", $f[0], $dest . '/' . $l) == 0 96 or $errors++; 97 } else { 98 system ("lipo", "-create", "-output", $dest . '/' . $l, @f) == 0 99 or $errors++; 100 } 101} 102 103exit ($errors ? 1 : 0); 104