#!/usr/bin/perl -w # This script applies 'lipo' to directories. For each file present in # any source directory, it will create a file in the destination directory. # If all the copies of the file in the source directories are the same, # the file is copied directly to the destination. If there are different # files in different directories, but the files are executable, # lipo is run to merge the files together. Otherwise, an error is # produced. # Device files and pipes are not supported, but symlinks are; the script # will check that all the symlinks point to the same place. # If a file is present in only one of the source directories, it is # trivially the same as itself and so it is just copied to the destination. # If there is only one source directory, the script is just an expensive # version of 'cp -rp'. # The destination directory is not emptied by this script. use File::Find (); use File::Copy (); use File::Compare (); use POSIX (); if ($#ARGV < 1) { print "usage: $0 \n"; exit 1; } # The source directories. my @sources = @ARGV[0..$#ARGV-1]; # The destination directory. my $dest = $ARGV[-1]; # Count of errors. my $errors = 0; # A hash of the files that need to be lipoed. Only the keys of the # hash are significant. my %needs_lipo = (); # Print an error message. sub error { printf STDERR "%s\n",$_[0]; $errors++; } # First, scan the directories; copy any files to the destination that are # identical in the source directory, print error messages, and make # a list of the files that need lipo-ing. foreach my $s (@sources) { sub eachfile { my $destname = $dest . '/' . $File::Find::name; if (-d) { (mkdir $destname or error ("$destname: $!")) unless (-d $destname); } elsif (-l) { my $link1 = readlink; defined $link1 or error ("$s/$_: $!"); if (-l $destname) { my $link2 = readlink $destname; defined $link2 or error ("$destname: $!"); $link1 eq $link2 or error ("$destname: different symlinks"); } elsif (-e $destname) { error ("$destname: symlink vs. real file"); } else { symlink $link1,$destname or error ("$destname: $!"); } } elsif (! -f) { error ("$destname: no idea how to handle special file"); } elsif (! -f $destname) { system ("cp", "-p", $_, $destname) == 0 or $errors++; } elsif (File::Compare::compare ($_, $destname) != 0) { $needs_lipo{$File::Find::name} = 1; } } chdir $s; File::Find::find (\&eachfile, '.'); } # Run lipo. foreach my $l (keys %needs_lipo) { # A list of the files to run lipo on. my @f = (); # Work out which source directories actually contain this file and # fill @f. foreach my $s (@sources) { my $sf = $s . '/' . $l; push @f,($sf) if (-f $sf); } die "inconsistent directories" if ($#f == -1); # There might really only be one copy if the file was present # in the tree before the script was started. if ($#f == 0) { system ("cp", "-p", $f[0], $dest . '/' . $l) == 0 or $errors++; } else { system ("lipo", "-create", "-output", $dest . '/' . $l, @f) == 0 or $errors++; } } exit ($errors ? 1 : 0);