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