Bug 1339182 - Remove OSX universal support in the build system; r?glandium draft
authorMike Shal <mshal@mozilla.com>
Fri, 10 Feb 2017 16:52:17 -0500
changeset 485401 0302e74696d2474e86a49db7b473c9f8bce2786a
parent 482882 e1a4314f8e6eae8bbc06394c14132a9c5011371b
child 546006 5f1b6ee44f35d5cd708258731f94f3a6a0fe636c
push id45720
push userbmo:mshal@mozilla.com
push dateThu, 16 Feb 2017 16:46:26 +0000
reviewersglandium
bugs1339182
milestone54.0a1
Bug 1339182 - Remove OSX universal support in the build system; r?glandium This removes the UNIFY_DIST and UNIFIED_BUILD variables, as well as the --unify flag from the packager and UnifiedBuildFinder from mozpack. As a result the STAGEPATH variable is never defined anymore, so its uses can be removed as well. test_unify.py is currently the only mozbuild/mozpack test that fails without running configure first, and there isn't much point in fixing tests for things that we don't actually use anymore. MozReview-Commit-ID: F5q1FPW3Did
Makefile.in
browser/locales/Makefile.in
build/macosx/universal/mozconfig
build/macosx/universal/mozconfig.common
build/macosx/universal/unify
js/src/old-configure.in
old-configure.in
python/mozbuild/mozbuild/base.py
python/mozbuild/mozbuild/mozinfo.py
python/mozbuild/mozbuild/test/test_mozinfo.py
python/mozbuild/mozpack/test/python.ini
python/mozbuild/mozpack/test/test_unify.py
python/mozbuild/mozpack/unify.py
toolkit/mozapps/installer/package-name.mk
toolkit/mozapps/installer/packager.mk
toolkit/mozapps/installer/packager.py
toolkit/mozapps/installer/upload-files-APK.mk
toolkit/mozapps/installer/upload-files.mk
tools/update-packaging/Makefile.in
--- a/Makefile.in
+++ b/Makefile.in
@@ -264,23 +264,18 @@ DUMP_SYMS_BIN ?= $(DIST)/host/bin/dump_s
 else
 DUMP_SYMS_BIN ?= $(topsrcdir)/toolkit/crashreporter/tools/win32/dump_syms_vc$(_MSC_VER).exe
 endif
 # PDB files don't get moved to dist, so we need to scan the whole objdir
 MAKE_SYM_STORE_PATH := .
 endif
 ifeq ($(OS_ARCH),Darwin)
 # need to pass arch flags for universal builds
-ifdef UNIVERSAL_BINARY
-MAKE_SYM_STORE_ARGS := -c --vcs-info
-MAKE_SYM_STORE_PATH := $(DIST)/bin $(UNIFY_DIST)/bin
-else
 MAKE_SYM_STORE_ARGS := -c -a $(OS_TEST) --vcs-info
 MAKE_SYM_STORE_PATH := $(DIST)/bin
-endif
 DUMP_SYMS_BIN ?= $(DIST)/host/bin/dump_syms
 endif
 ifeq (,$(filter-out Linux SunOS,$(OS_ARCH)))
 MAKE_SYM_STORE_ARGS := -c --vcs-info
 DUMP_SYMS_BIN ?= $(DIST)/host/bin/dump_syms
 MAKE_SYM_STORE_PATH := $(DIST)/bin
 endif
 MAKE_SYM_STORE_ARGS += --install-manifest=$(DEPTH)/_build_manifests/install/dist_include,$(DIST)/include
--- a/browser/locales/Makefile.in
+++ b/browser/locales/Makefile.in
@@ -187,11 +187,11 @@ endif
 l10n-check:: INNER_UNMAKE_PACKAGE=true
 l10n-check::
 	$(RM) -rf x-test
 	$(NSINSTALL) -D x-test/toolkit
 	echo '#define MOZ_LANG_TITLE Just testing' > x-test/toolkit/defines.inc
 	@# ZIP_IN='$(ZIP_IN)' will pass down the *current* value of ZIP_IN, based
 	@# on MOZ_SIMPLE_PACKAGE_NAME not being reset, overwriting the value it
 	@# would get with MOZ_SIMPLE_PACKAGE_NAME reset.
-	$(MAKE) installers-x-test L10NBASEDIR='$(PWD)' LOCALE_MERGEDIR='$(PWD)/mergedir' ZIP_IN='$(ZIP_IN)' MOZ_SIMPLE_PACKAGE_NAME= UNIVERSAL_BINARY=
+	$(MAKE) installers-x-test L10NBASEDIR='$(PWD)' LOCALE_MERGEDIR='$(PWD)/mergedir' ZIP_IN='$(ZIP_IN)' MOZ_SIMPLE_PACKAGE_NAME=
 	$(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/unpack.py $(DIST)/l10n-stage/$(MOZ_PKG_DIR)$(_RESPATH)
 	cd $(DIST)/l10n-stage && test $$(cat $(MOZ_PKG_DIR)$(_RESPATH)/update.locale) = x-test
deleted file mode 100644
--- a/build/macosx/universal/mozconfig
+++ /dev/null
@@ -1,11 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-# i386/x86-64 Universal Build mozconfig
-
-# As used here, arguments in $MOZ_BUILD_PROJECTS are suitable as arguments
-# to gcc's -arch parameter.
-mk_add_options MOZ_BUILD_PROJECTS="x86_64 i386"
-
-. $topsrcdir/build/macosx/universal/mozconfig.common
deleted file mode 100644
--- a/build/macosx/universal/mozconfig.common
+++ /dev/null
@@ -1,54 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-mk_add_options MOZ_UNIFY_BDATE=1
-
-DARWIN_VERSION=10
-ac_add_app_options i386 --target=i386-apple-darwin$DARWIN_VERSION
-ac_add_app_options x86_64 --target=x86_64-apple-darwin$DARWIN_VERSION
-ac_add_app_options i386 --with-unify-dist=../x86_64/dist
-ac_add_app_options x86_64 --with-unify-dist=../i386/dist
-
-if ! test `uname -s` = Linux; then
-  # Cross-universal builds already do the equivalent of this by setting -isysroot directly
-  ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.7.sdk
-fi
-
-. $topsrcdir/build/macosx/mozconfig.common
-
-# $MOZ_BUILD_APP is only defined when sourced by configure.  That's not a
-# problem, because the variables it affects only need to be set for
-# configure.
-if test -n "$MOZ_BUILD_APP" ; then
-if test "$MOZ_BUILD_APP" = "i386" -o "$MOZ_BUILD_APP" = "x86_64"; then
-  TARGET_CPU=$MOZ_BUILD_APP
-
-  # $HOST_CXX is presently unused.  $HOST_CC will only be used during a cross
-  # compile.
-  HOST_CC=$CC
-  HOST_CXX=$CXX
-
-  NATIVE_CPU=`$topsrcdir/build/autoconf/config.guess | cut -f1 -d-`
-
-  # It's not strictly necessary to specify -arch during native builds, but it
-  # makes the merged about:buildconfig easier to follow, and it reduces
-  # conditionalized differences between builds.
-  CC="$CC -arch $TARGET_CPU"
-  CXX="$CXX -arch $TARGET_CPU"
-
-  # These must be set for cross builds, and don't hurt straight builds.
-  RANLIB="${TOOLCHAIN_PREFIX}ranlib"
-  AR="${TOOLCHAIN_PREFIX}ar"
-  AS=$CC
-  STRIP="strip"
-  OTOOL="${TOOLCHAIN_PREFIX}otool"
-
-  # Each per-CPU build should be entirely oblivious to the fact that a
-  # universal binary will be produced.  The exception is packager.mk, which
-  # needs to know to look for universal bits when building the .dmg.
-  UNIVERSAL_BINARY=1
-
-  export CC CXX HOST_CC HOST_CXX RANLIB AR AS STRIP OTOOL
-fi
-fi
deleted file mode 100755
--- a/build/macosx/universal/unify
+++ /dev/null
@@ -1,1525 +0,0 @@
-#!/usr/bin/perl
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-use strict;
-use warnings;
-
-=pod
-
-=head1 NAME
-
-B<unify> - Mac OS X universal binary packager
-
-=head1 SYNOPSIS
-
-B<unify>
-I<ppc-path>
-I<x86-path>
-I<universal-path>
-[B<--dry-run>]
-[B<--only-one> I<action>]
-[B<--verbosity> I<level>]
-[B<--unify-with-sort> I<regex>]
-
-=head1 DESCRIPTION
-
-I<unify> merges any two architecture-specific files or directory trees
-into a single file or tree suitable for use on either architecture as a
-"fat" or "universal binary."
-
-Architecture-specific Mach-O files will be merged into fat Mach-O files
-using L<lipo(1)>.  Non-Mach-O files in the architecture-specific trees
-are compared to ensure that they are equivalent before copying.  Symbolic
-links are permitted in the architecture-specific trees and will cause
-identical links to be created in the merged tree, provided that the source
-links have identical targets.  Directories are processed recursively.
-
-If the architecture-specific source trees contain zip archives (including
-jar files) that are not identical according to a byte-for-byte check, they
-are still assumed to be equivalent if both archives contain exactly the
-same members with identical checksums and sizes.
-
-Behavior when one architecture-specific tree contains files that the other
-does not is controlled by the B<--only-one> option.
-
-If Mach-O files cannot be merged using L<lipo(1)>, zip archives are not
-equivalent, regular files are not identical, or any other error occurs,
-B<unify> will fail with an exit status of 1.  Diagnostic messages are
-typically printed to stderr; this behavior can be controlled with the
-B<--verbosity> option.
-
-=head1 OPTIONS
-
-=over 5
-
-=item I<ppc-path>
-
-=item I<x86-path>
-
-The paths to directory trees containing PowerPC and x86 builds,
-respectively.  I<ppc-path> and I<x86-path> are permitted to contain files
-that are already "fat," and only the appropriate architecture's images will
-be used.
-
-I<ppc-path> and I<x86-path> are also permitted to both be files, in which
-case B<unify> operates solely on those files, and produces an appropriate
-merged file at I<target-path>.
-
-=item I<target-path>
-
-The path to the merged file or directory tree.  This path will be created,
-and it must not exist prior to running B<unify>.
-
-=item B<--dry-run>
-
-When specified, the commands that would be executed are printed, without
-actually executing them.  Note that B<--dry-run> and the equivalent
-B<--verbosity> level during "wet" runs may print equivalent commands when
-no commands are in fact executed: certain operations are handled internally
-within B<unify>, and an approximation of a command that performs a similar
-task is printed.
-
-=item B<--only-one> I<action>
-
-Controls handling of files that are only present in one of the two source
-trees.  I<action> may be:
-  skip - These files are skipped.
-  copy - These files are copied from the tree in which they exist.
-  fail - When this condition occurs, it is treated as an error.
-
-The default I<action> is copy.
-
-=item B<--verbosity> I<level>
-
-Adjusts the level of loudness of B<unify>.  The possible values for
-I<level> are:
-  0 - B<unify> never prints anything.
-      (Other programs that B<unify> calls may still print messages.)
-  1 - Fatal error messages are printed to stderr.
-  2 - Nonfatal warnings are printed to stderr.
-  3 - Commands are printed to stdout as they are executed.
-
-The default I<level> is 2.
-
-=item B<--unify-with-sort> I<regex>
-
-Allows merging files matching I<regex> that differ only by the ordering
-of the lines contained within them. The unified file will have its contents
-sorted. This option may be given multiple times to specify multiple
-regexes for matching files.
-
-=back
-
-=head1 EXAMPLES
-
-=over 5
-
-=item Create a universal .app bundle from two architecture-specific .app
-bundles:
-
-unify --only-one copy ppc/dist/firefox/Firefox.app
-  x86/dist/firefox/Firefox.app universal/Firefox.app
-  --verbosity 3
-
-=item Merge two identical architecture-specific trees:
-
-unify --only-one fail /usr/local /nfs/x86/usr/local
-  /tmp/usrlocal.fat
-
-=back
-
-=head1 REQUIREMENTS
-
-The only esoteric requirement of B<unify> is that the L<lipo(1)> command
-be available.  It is present on Mac OS X systems at least as early as
-10.3.9, and probably earlier.  Mac OS X 10.4 ("Tiger") or later are
-recommended.
-
-=head1 LICENSE
-
-MPL 2.
-
-=head1 AUTHOR
-
-The software was initially written by Mark Mentovai; copyright 2006
-Google Inc.
-
-=head1 SEE ALSO
-
-L<cmp(1)>, L<ditto(1)>, L<lipo(1)>
-
-=cut
-
-use Archive::Zip(':ERROR_CODES');
-use Errno;
-use Fcntl;
-use File::Compare;
-use File::Copy;
-use Getopt::Long;
-
-my (%gConfig, $gDryRun, $gOnlyOne, $gVerbosity, @gSortMatches);
-
-sub argumentEscape(@);
-sub command(@);
-sub compareZipArchives($$);
-sub complain($$@);
-sub copyIfIdentical($$$);
-sub slurp($);
-sub get_sorted($);
-sub compare_sorted($$);
-sub copyIfIdenticalWhenSorted($$$);
-sub createUniqueFile($$);
-sub makeUniversal($$$);
-sub makeUniversalDirectory($$$);
-sub makeUniversalInternal($$$$);
-sub makeUniversalFile($$$);
-sub usage();
-sub readZipCRCs($);
-
-{
-  package FileAttrCache;
-
-  sub new($$);
-
-  sub isFat($);
-  sub isMachO($);
-  sub isZip($);
-  sub lIsDir($);
-  sub lIsExecutable($);
-  sub lIsRegularFile($);
-  sub lIsSymLink($);
-  sub lstat($);
-  sub lstatMode($);
-  sub lstatType($);
-  sub magic($);
-  sub magic2($);
-  sub path($);
-  sub stat($);
-  sub statSize($);
-}
-
-%gConfig = (
-  'cmd_lipo' => 'lipo',
-  'cmd_rm'   => 'rm',
-);
-
-$gDryRun = 0;
-$gOnlyOne = 'copy';
-$gVerbosity = 2;
-@gSortMatches = ();
-
-Getopt::Long::Configure('pass_through');
-GetOptions('dry-run'           => \$gDryRun,
-           'only-one=s'        => \$gOnlyOne,
-           'verbosity=i'       => \$gVerbosity,
-           'unify-with-sort=s' => \@gSortMatches,
-           'config=s'          => \%gConfig); # "hidden" option not in usage()
-
-if (scalar(@ARGV) != 3 || $gVerbosity < 0 || $gVerbosity > 3 ||
-    ($gOnlyOne ne 'skip' && $gOnlyOne ne 'copy' && $gOnlyOne ne 'fail')) {
-  usage();
-  exit(1);
-}
-
-if (!makeUniversal($ARGV[0],$ARGV[1],$ARGV[2])) {
-  # makeUniversal or something it called will have printed an error.
-  exit(1);
-}
-
-exit(0);
-
-# argumentEscape(@arguments)
-#
-# Takes a list of @arguments and makes them shell-safe.
-sub argumentEscape(@) {
-  my (@arguments);
-  @arguments = @_;
-
-  my ($argument, @argumentsOut);
-  foreach $argument (@arguments) {
-    $argument =~ s%([^A-Za-z0-9_\-/.=+,])%\\$1%g;
-    push(@argumentsOut, $argument);
-  }
-
-  return @argumentsOut;
-}
-
-# command(@arguments)
-#
-# Runs the specified command by calling system(@arguments).  If $gDryRun
-# is true, the command is printed but not executed, and 0 is returned.
-# if $gVerbosity is greater than 1, the command is printed before being
-# executed.  When the command is executed, the system() return value will
-# be returned.  stdout and stderr are left connected for command output.
-sub command(@) {
-  my (@arguments);
-  @arguments = @_;
-  if ($gVerbosity >= 3 || $gDryRun) {
-    print(join(' ', argumentEscape(@arguments))."\n");
-  }
-  if ($gDryRun) {
-    return 0;
-  }
-  return system(@arguments);
-}
-
-# compareZipArchives($zip1, $zip2)
-#
-# Given two pathnames to zip archives, determines whether or not they are
-# functionally identical.  Returns true if they are, false if they differ in
-# some substantial way, and undef if an error occurs.  If the zip files
-# differ, diagnostic messages are printed indicating how they differ.
-#
-# Zip files will differ if any of the members are different as defined by
-# readZipCRCs, which consider CRCs, sizes, and file types as stored in the
-# file header.  Timestamps are not considered.  Zip files also differ if one
-# file contains members that the other one does not.  $gOnlyOne has no
-# effect on this behavior.
-sub compareZipArchives($$) {
-  my ($zip1, $zip2);
-  ($zip1, $zip2) = @_;
-
-  my ($CRCHash1, $CRCHash2);
-  if (!defined($CRCHash1 = readZipCRCs($zip1))) {
-    # readZipCRCs printed an error.
-    return undef;
-  }
-  if (!defined($CRCHash2 = readZipCRCs($zip2))) {
-    # readZipCRCs printed an error.
-    return undef;
-  }
-
-  my (@diffCRCs, @onlyInZip1);
-  @diffCRCs = ();
-  @onlyInZip1 = ();
-
-  my ($memberName);
-  foreach $memberName (keys(%$CRCHash1)) {
-    if (!exists($$CRCHash2{$memberName})) {
-      # The member is present in $zip1 but not $zip2.
-      push(@onlyInZip1, $memberName);
-    }
-    elsif ($$CRCHash1{$memberName} ne $$CRCHash2{$memberName}) {
-      # The member is present in both archives but its CRC or some other
-      # other critical attribute isn't identical.
-      push(@diffCRCs, $memberName);
-    }
-    delete($$CRCHash2{$memberName});
-  }
-
-  # If any members remain in %CRCHash2, it's because they're not present
-  # in $zip1.
-  my (@onlyInZip2);
-  @onlyInZip2 = keys(%$CRCHash2);
-
-  if (scalar(@onlyInZip1) + scalar(@onlyInZip2) + scalar(@diffCRCs)) {
-    complain(1, 'compareZipArchives: zip archives differ:',
-             $zip1,
-             $zip2);
-    if (scalar(@onlyInZip1)) {
-      complain(1, 'compareZipArchives: members only in former:',
-               @onlyInZip1);
-    }
-    if (scalar(@onlyInZip2)) {
-      complain(1, 'compareZipArchives: members only in latter:',
-               @onlyInZip2);
-    }
-    if (scalar(@diffCRCs)) {
-      complain(1, 'compareZipArchives: members differ:',
-               @diffCRCs);
-    }
-    return 0;
-  }
-
-  return 1;
-}
-
-# complain($severity, $message, @list)
-#
-# Prints $message to stderr if $gVerbosity allows it for severity level
-# $severity.  @list is a list of words that will be shell-escaped and printed
-# after $message, one per line, intended to be used, for example, to list
-# arguments to a call that failed.
-#
-# Expected severity levels are 1 for hard errors and 2 for non-fatal warnings.
-#
-# Always returns false as a convenience, so callers can return complain's
-# return value when it is used to signal errors.
-sub complain($$@) {
-  my ($severity, $message, @list);
-  ($severity, $message, @list) = @_;
-
-  if ($gVerbosity >= $severity) {
-    print STDERR ($0.': '.$message."\n");
-
-    my ($item);
-    while ($item = shift(@list)) {
-      print STDERR ('  '.(argumentEscape($item))[0].
-                    (scalar(@list)?',':'')."\n");
-    }
-  }
-
-  return 0;
-}
-
-# copyIfIdentical($source1, $source2, $target)
-#
-# $source1 and $source2 are FileAttrCache objects that are compared, and if
-# identical, copied to path string $target.  The comparison is initially
-# done as a byte-for-byte comparison, but if the files differ and appear to
-# be zip archives, compareZipArchives is called to determine whether
-# files that are not byte-for-byte identical are equivalent archives.
-#
-# Returns true on success, false for files that are not identical or
-# equivalent archives, and undef if an error occurs.
-#
-# One of $source1 and $source2 is permitted to be undef.  In this event,
-# whichever source is defined is copied directly to $target without performing
-# any comparisons.  This enables the $gOnlyOne = 'copy' mode, which is
-# driven by makeUniversalDirectory and makeUniversalInternal.
-sub copyIfIdentical($$$) {
-  my ($source1, $source2, $target);
-  ($source1, $source2, $target) = @_;
-
-  if (!defined($source1)) {
-    # If there's only one source file, make it the first file.  Order
-    # isn't important here, and this makes it possible to use
-    # defined($source2) as the switch, and to always copy from $source1.
-    $source1 = $source2;
-    $source2 = undef;
-  }
-
-  if (defined($source2)) {
-    # Only do the comparisons if there are two source files.  If there's
-    # only one source file, skip the comparisons and go straight to the
-    # copy operation.
-    if ($gVerbosity >= 3 || $gDryRun) {
-      print('cmp -s '.
-            join(' ',argumentEscape($source1->path(), $source2->path()))."\n");
-    }
-    my ($comparison);
-    if (!defined($comparison = compare($source1->path(), $source2->path())) ||
-        $comparison == -1) {
-      return complain(1, 'copyIfIdentical: compare: '.$!.' while comparing:',
-                      $source1->path(),
-                      $source2->path());
-    }
-    elsif ($comparison != 0) {
-      my ($zip1, $zip2);
-      if (defined($zip1 = $source1->isZip()) &&
-          defined($zip2 = $source2->isZip()) &&
-          $zip1 && $zip2) {
-        my ($zipComparison);
-        if (!defined($zipComparison = compareZipArchives($source1->path(),
-                                                         $source2->path)) ||
-            !$zipComparison) {
-          # An error occurred or the zip files aren't sufficiently identical.
-          # compareZipArchives will have printed an error message.
-          return 0;
-        }
-        # The zip files were compared successfully, and they both contain
-        # all of the same members, and all of their members' CRCs are
-        # identical.  For the purposes of this script, the zip files can be
-        # treated as identical, so reset $comparison.
-        $comparison = 0;
-      }
-    }
-    if ($comparison != 0) {
-      return complain(1, 'copyIfIdentical: files differ:',
-                      $source1->path(),
-                      $source2->path());
-    }
-  }
-
-  if ($gVerbosity >= 3 || $gDryRun) {
-    print('cp '.
-          join(' ',argumentEscape($source1->path(), $target))."\n");
-  }
-
-  if (!$gDryRun) {
-    my ($isExecutable);
-
-    # Set the execute bits (as allowed by the umask) on the new file if any
-    # execute bit is set on either old file.
-    $isExecutable = $source1->lIsExecutable() ||
-                    (defined($source2) && $source2->lIsExecutable());
-
-    if (!createUniqueFile($target, $isExecutable ? 0777 : 0666)) {
-      # createUniqueFile printed an error.
-      return 0;
-    }
-
-    if (!copy($source1->path(), $target)) {
-      complain(1, 'copyIfIdentical: copy: '.$!.' while copying',
-               $source1->path(),
-               $target);
-      unlink($target);
-      return 0;
-    }
-  }
-
-  return 1;
-}
-
-# slurp($file)
-#
-# Read the contents of $file into an array and return it.
-# Returns undef on error.
-sub slurp($) {
-  my $file = $_[0];
-  open FILE, $file or return undef;
-  my @lines = <FILE>;
-  close FILE;
-  return @lines;
-}
-
-# get_sorted($file)
-# Get the sorted lines of a file as a list, normalizing a newline on the last line if necessary.
-sub get_sorted($) {
-  my ($file) = @_;
-  my @lines = slurp($file);
-  my $lastline = $lines[-1];
-  if (!($lastline =~ /\n/)) {
-    $lines[-1] = $lastline . "\n";
-  }
-  return sort(@lines);
-}
-
-# compare_sorted($file1, $file2)
-#
-# Read the contents of both files into arrays, sort the arrays,
-# and then compare the two arrays for equality.
-#
-# Returns 0 if the sorted array contents are equal, or 1 if not.
-# Returns undef on error.
-sub compare_sorted($$) {
-  my ($file1, $file2) = @_;
-  my @lines1 = get_sorted($file1);
-  my @lines2 = get_sorted($file2);
-
-  return undef if !@lines1 || !@lines2;
-  return 1 unless scalar @lines1 == scalar @lines2;
-
-  for (my $i = 0; $i < scalar @lines1; $i++) {
-    return 1 if $lines1[$i] ne $lines2[$i];
-  }
-  return 0;
-}
-
-# copyIfIdenticalWhenSorted($source1, $source2, $target)
-#
-# $source1 and $source2 are FileAttrCache objects that are compared, and if
-# identical, copied to path string $target.  The comparison is done by
-# sorting the individual lines within the two files and comparing the results.
-#
-# Returns true on success, false for files that are not equivalent,
-# and undef if an error occurs.
-sub copyIfIdenticalWhenSorted($$$) {
-  my ($source1, $source2, $target);
-  ($source1, $source2, $target) = @_;
-
-  if ($gVerbosity >= 3 || $gDryRun) {
-    print('cmp -s '.
-          join(' ',argumentEscape($source1->path(), $source2->path()))."\n");
-  }
-  my ($comparison);
-  if (!defined($comparison = compare_sorted($source1->path(),
-                                            $source2->path())) ||
-      $comparison == -1) {
-    return complain(1, 'copyIfIdenticalWhenSorted: compare: '.$!
-                    .' while comparing:',
-                      $source1->path(),
-                      $source2->path());
-  }
-  if ($comparison != 0) {
-    return complain(1, 'copyIfIdenticalWhenSorted: files differ:',
-                    $source1->path(),
-                    $source2->path());
-  }
-
-  if ($gVerbosity >= 3 || $gDryRun) {
-    print('cp '.
-          join(' ',argumentEscape($source1->path(), $target))."\n");
-  }
-
-  if (!$gDryRun) {
-    my ($isExecutable);
-
-    # Set the execute bits (as allowed by the umask) on the new file if any
-    # execute bit is set on either old file.
-    $isExecutable = $source1->lIsExecutable() ||
-                    (defined($source2) && $source2->lIsExecutable());
-
-    if (!createUniqueFile($target, $isExecutable ? 0777 : 0666)) {
-      # createUniqueFile printed an error.
-      return 0;
-    }
-
-    if (!copy($source1->path(), $target)) {
-      complain(1, 'copyIfIdenticalWhenSorted: copy: '.$!
-               .' while copying',
-               $source1->path(),
-               $target);
-      unlink($target);
-      return 0;
-    }
-  }
-
-  return 1;
-}
-
-# createUniqueFile($path, $mode)
-#
-# Creates a new plain empty file at pathname $path, provided it does not
-# yet exist.  $mode is used as the file mode.  The actual file's mode will
-# be modified by the effective umask.  Returns false if the file could
-# not be created, setting $! to the error.  An error message is printed
-# in the event of failure.
-sub createUniqueFile($$) {
-  my ($path, $mode);
-  ($path, $mode) = @_;
-
-  my ($fh);
-  if (!sysopen($fh, $path, O_WRONLY | O_CREAT | O_EXCL, $mode)) {
-    return complain(1, 'createUniqueFile: open: '.$!.' for:',
-                    $path);
-  }
-  close($fh);
-
-  return 1;
-}
-
-# makeUniversal($pathPPC, $pathX86, $pathTarget)
-#
-# The top-level call.  $pathPPC, $pathX86, and $pathTarget are strings
-# identifying the ppc and x86 files or directories to merge and the location
-# to merge them to.  Returns false on failure and true on success.
-sub makeUniversal($$$) {
-  my ($pathTarget, $pathPPC, $pathX86);
-  ($pathPPC, $pathX86, $pathTarget) = @_;
-
-  my ($filePPC, $fileX86);
-  $filePPC = FileAttrCache->new($pathPPC);
-  $fileX86 = FileAttrCache->new($pathX86);
-
-  return makeUniversalInternal(1, $filePPC, $fileX86, $pathTarget);
-}
-
-# makeUniversalDirectory($dirPPC, $dirX86, $dirTarget)
-#
-# This is part of the heart of recursion.  $dirPPC and $dirX86 are
-# FileAttrCache objects designating the source ppc and x86 directories to
-# merge into a universal directory at $dirTarget, a string.  For each file
-# in $dirPPC and $dirX86, makeUniversalInternal is called.
-# makeUniversalInternal will call back into makeUniversalDirectory for
-# directories, thus completing the recursion.  If a failure is encountered
-# in ths function or in makeUniversalInternal or anything that it calls,
-# false is returned, otherwise, true is returned.
-#
-# If there are files present in one source directory but not both, the
-# value of $gOnlyOne controls the behavior.  If $gOnlyOne is 'copy', the
-# single source file is copied into $pathTarget.  If it is 'skip', it is
-# skipped.  If it is 'fail', such files will trigger makeUniversalDirectory
-# to fail.
-#
-# If either source directory is undef, it is treated as having no files.
-# This facilitates deep recursion when entire directories are only present
-# in one source when $gOnlyOne = 'copy'.
-sub makeUniversalDirectory($$$) {
-  my ($dirPPC, $dirX86, $dirTarget);
-  ($dirPPC, $dirX86, $dirTarget) = @_;
-
-  my ($dh, @filesPPC, @filesX86);
-
-  @filesPPC = ();
-  if (defined($dirPPC)) {
-    if (!opendir($dh, $dirPPC->path())) {
-      return complain(1, 'makeUniversalDirectory: opendir ppc: '.$!.' for:',
-                      $dirPPC->path());
-    }
-    @filesPPC = readdir($dh);
-    closedir($dh);
-  }
-
-  @filesX86 = ();
-  if (defined($dirX86)) {
-    if (!opendir($dh, $dirX86->path())) {
-      return complain(1, 'makeUniversalDirectory: opendir x86: '.$!.' for:',
-                      $dirX86->path());
-     }
-    @filesX86 = readdir($dh);
-    closedir($dh);
-  }
-
-  my (%common, $file, %onlyPPC, %onlyX86);
-
-  %onlyPPC = ();
-  foreach $file (@filesPPC) {
-    if ($file eq '.' || $file eq '..') {
-      next;
-    }
-    $onlyPPC{$file}=1;
-  }
-
-  %common = ();
-  %onlyX86 = ();
-  foreach $file (@filesX86) {
-    if ($file eq '.' || $file eq '..') {
-      next;
-    }
-    if ($onlyPPC{$file}) {
-      delete $onlyPPC{$file};
-      $common{$file}=1;
-    }
-    else {
-      $onlyX86{$file}=1;
-    }
-  }
-
-  # First, handle files common to both.
-  foreach $file (sort(keys(%common))) {
-    if (!makeUniversalInternal(0,
-                               FileAttrCache->new($dirPPC->path().'/'.$file),
-                               FileAttrCache->new($dirX86->path().'/'.$file),
-                               $dirTarget.'/'.$file)) {
-      # makeUniversalInternal will have printed an error.
-      return 0;
-    }
-  }
-
-  # Handle files found only in a single directory here.  There are three
-  # options, dictated by $gOnlyOne: fail if files are only present in
-  # one directory, skip any files only present in one directory, or copy
-  # these files straight over to the target directory.  In any event,
-  # a message will be printed indicating that the file trees don't match
-  # exactly.
-  if (keys(%onlyPPC)) {
-    complain(($gOnlyOne eq 'fail' ? 1 : 2),
-             ($gOnlyOne ne 'fail' ? 'warning: ' : '').
-             'makeUniversalDirectory: only in ppc '.
-             (argumentEscape($dirPPC->path()))[0].':',
-             argumentEscape(keys(%onlyPPC)));
-  }
-
-  if (keys(%onlyX86)) {
-    complain(($gOnlyOne eq 'fail' ? 1 : 2),
-             ($gOnlyOne ne 'fail' ? 'warning: ' : '').
-             'makeUniversalDirectory: only in x86 '.
-             (argumentEscape($dirX86->path()))[0].':',
-             argumentEscape(keys(%onlyX86)));
-  }
-
-  if ($gOnlyOne eq 'fail' && (keys(%onlyPPC) || keys(%onlyX86))) {
-    # Error message(s) printed above.
-    return 0;
-  }
-
-  if ($gOnlyOne eq 'copy') {
-    foreach $file (sort(keys(%onlyPPC))) {
-      if (!makeUniversalInternal(0,
-                                 FileAttrCache->new($dirPPC->path().'/'.$file),
-                                 undef,
-                                 $dirTarget.'/'.$file)) {
-        # makeUniversalInternal will have printed an error.
-        return 0;
-      }
-    }
-
-    foreach $file (sort(keys(%onlyX86))) {
-      if (!makeUniversalInternal(0,
-                                 undef,
-                                 FileAttrCache->new($dirX86->path().'/'.$file),
-                                 $dirTarget.'/'.$file)) {
-        # makeUniversalInternal will have printed an error.
-        return 0;
-      }
-    }
-  }
-
-  return 1;
-}
-
-# makeUniversalFile($sourcePPC, $sourceX86, $targetPath)
-#
-# Creates a universal file at pathname $targetPath based on a ppc image at
-# $sourcePPC and an x86 image at $sourceX86.  $sourcePPC and $sourceX86 are
-# both FileAttrCache objects.  Returns true on success and false on failure.
-# On failure, diagnostics will be printed to stderr.
-#
-# The source files may be either thin Mach-O images of the appropriate
-# architecture, or fat Mach-O files that contain images of the appropriate
-# architecture.
-#
-# This function wraps the lipo utility, see lipo(1).
-sub makeUniversalFile($$$) {
-  my ($sourcePPC, $sourceX86, $targetPath, @tempThinFiles, $thinPPC, $thinX86);
-  ($sourcePPC, $sourceX86, $targetPath) = @_;
-  $thinPPC = $sourcePPC;
-  $thinX86 = $sourceX86;
-
-  @tempThinFiles = ();
-
-  # The source files might already be fat.  They should be thinned out to only
-  # contain a single architecture.
- 
-  my ($isFatPPC, $isFatX86);
-
-  if(!defined($isFatPPC = $sourcePPC->isFat())) {
-    # isFat printed its own error
-    return 0;
-  }
-  elsif($isFatPPC) {
-    $thinPPC = FileAttrCache->new($targetPath.'.ppc');
-    push(@tempThinFiles, $thinPPC->path());
-    if (command($gConfig{'cmd_lipo'}, '-thin', 'ppc',
-                $sourcePPC->path(), '-output', $thinPPC->path()) != 0) {
-      unlink(@tempThinFiles);
-      return complain(1, 'lipo thin ppc failed for:',
-                      $sourcePPC->path(),
-                      $thinPPC->path());
-    }
-  }
-
-  if(!defined($isFatX86 = $sourceX86->isFat())) {
-    # isFat printed its own error
-    unlink(@tempThinFiles);
-    return 0;
-  }
-  elsif($isFatX86) {
-    $thinX86 = FileAttrCache->new($targetPath.'.x86');
-    push(@tempThinFiles, $thinX86->path());
-    if (command($gConfig{'cmd_lipo'}, '-thin', 'i386',
-                $sourceX86->path(), '-output', $thinX86->path()) != 0) {
-      unlink(@tempThinFiles);
-      return complain(1, 'lipo thin x86 failed for:',
-                      $sourceX86->path(),
-                      $thinX86->path());
-    }
-  }
-
-  # The image for each architecture in the fat file will be aligned on
-  # a specific boundary, default 4096 bytes, see lipo(1) -segalign.
-  # Since there's no tail-padding, the fat file will consume the least
-  # space on disk if the image that comes last exceeds the segment size
-  # by the smallest amount.
-  #
-  # This saves an average of 1kB per fat file over the naive approach of
-  # always putting one architecture first: average savings is 2kB per
-  # file, but the naive approach would have gotten it right half of the
-  # time.
-
-  my ($sizePPC, $sizeX86, $thinPPCForStat, $thinX86ForStat);
-
-  if (!$gDryRun) {
-    $thinPPCForStat = $thinPPC;
-    $thinX86ForStat = $thinX86;
-  }
-  else {
-    # Normally, fat source files will have been converted into temporary
-    # thin files.  During a dry run, that doesn't happen, so fake it up
-    # a little bit by always using the source file, fat or thin, for the
-    # stat.
-    $thinPPCForStat = $sourcePPC;
-    $thinX86ForStat = $sourceX86;
-  }
-
-  if (!defined($sizePPC = $thinPPCForStat->statSize())) {
-    unlink(@tempThinFiles);
-    return complain(1, 'stat ppc: '.$!.' for:',
-                    $thinPPCForStat->path());
-  }
-  if (!defined($sizeX86 = $thinX86ForStat->statSize())) {
-    unlink(@tempThinFiles);
-    return complain(1, 'stat x86: '.$!.' for:',
-                    $thinX86ForStat->path());
-  }
-
-  $sizePPC = $sizePPC % 4096;
-  $sizeX86 = $sizeX86 % 4096;
-
-  my (@thinFiles);
-
-  if ($sizePPC == 0) {
-    # PPC image ends on an alignment boundary, there will be no padding before
-    # starting the x86 image.
-    @thinFiles = ($thinPPC->path(), $thinX86->path());
-  }
-  elsif ($sizeX86 == 0 || $sizeX86 > $sizePPC) {
-    # x86 image ends on an alignment boundary, there will be no padding before
-    # starting the PPC image, or the x86 image exceeds its alignment boundary
-    # by more than the PPC image, so there will be less padding if the x86
-    # comes first.
-    @thinFiles = ($thinX86->path(), $thinPPC->path());
-  }
-  else {
-    # PPC image exceeds its alignment boundary by more than the x86 image, so
-    # there will be less padding if the PPC comes first.
-    @thinFiles = ($thinPPC->path(), $thinX86->path());
-  }
-
-  my ($isExecutable);
-  $isExecutable = $sourcePPC->lIsExecutable() ||
-                  $sourceX86->lIsExecutable();
-
-  if (!$gDryRun) {
-    # Ensure that the file does not yet exist.
-
-    # Set the execute bits (as allowed by the umask) on the new file if any
-    # execute bit is set on either old file.  Yes, it is possible to have
-    # proper Mach-O files without x-bits: think object files (.o) and static
-    # archives (.a).
-    if (!createUniqueFile($targetPath, $isExecutable ? 0777 : 0666)) {
-      # createUniqueFile printed an error.
-      unlink(@tempThinFiles);
-      return 0;
-    }
-  }
-
-  # Create the fat file.
-  if (command($gConfig{'cmd_lipo'}, '-create', @thinFiles,
-              '-output', $targetPath) != 0) {
-    unlink(@tempThinFiles, $targetPath);
-    return complain(1, 'lipo create fat failed for:',
-                    @thinFiles,
-                    $targetPath);
-  }
-
-  unlink(@tempThinFiles);
-
-  if (!$gDryRun) {
-    # lipo seems to think that it's free to set its own file modes that
-    # ignore the umask, which is bogus when the rest of this script
-    # respects the umask.
-    if (!chmod(($isExecutable ? 0777 : 0666) & ~umask(), $targetPath)) {
-      complain(1, 'makeUniversalFile: chmod: '.$!.' for',
-               $targetPath);
-      unlink($targetPath);
-      return 0;
-    }
-  }
-
-  return 1;
-}
-
-# makeUniversalInternal($isToplevel, $filePPC, $fileX86, $fileTargetPath)
-#
-# Given FileAttrCache objects $filePPC and $fileX86, compares filetypes
-# and performs the appropriate action to produce a universal file at
-# path string $fileTargetPath.  $isToplevel should be true if this is
-# the recursive base and false otherwise; this controls cleanup behavior
-# (cleanup is only performed at the base, because cleanup itself is
-# recursive).
-#
-# This handles regular files by determining whether they are Mach-O files
-# and calling makeUniversalFile if so and copyIfIdentical otherwise.  Symbolic
-# links are handled directly in this function by ensuring that the source link
-# targets are identical and creating a new link with the same target
-# at $fileTargetPath.  Directories are handled by calling
-# makeUniversalDirectory.
-#
-# One of $filePPC and $fileX86 is permitted to be undef.  In that case,
-# the defined source file is copied directly to the target if a regular
-# file, and symlinked appropriately if a symbolic link.  This facilitates
-# use of $gOnlyOne = 'copy', although no $gOnlyOne checks are made in this
-# function, they are all handled in makeUniversalDirectory.
-#
-# Returns true on success.  Returns false on failure, including failures
-# in other functions called.
-sub makeUniversalInternal($$$$) {
-  my ($filePPC, $fileTargetPath, $fileX86, $isToplevel);
-  ($isToplevel, $filePPC, $fileX86, $fileTargetPath) = @_;
-
-  my ($typePPC, $typeX86);
-  if (defined($filePPC) && !defined($typePPC = $filePPC->lstatType())) {
-    return complain(1, 'makeUniversal: lstat ppc: '.$!.' for:',
-                    $filePPC->path());
-  }
-  if (defined($fileX86) && !defined($typeX86 = $fileX86->lstatType())) {
-    return complain(1, 'makeUniversal: lstat x86: '.$!.' for:',
-                    $fileX86->path());
-  }
-
-  if (defined($filePPC) && defined($fileX86) && $typePPC != $typeX86) {
-    return complain(1, 'makeUniversal: incompatible types:',
-                    $filePPC->path(),
-                    $fileX86->path());
-  }
-
-  # $aSourceFile will contain a FileAttrCache object that will return
-  # the correct type data.  It's used because it's possible for one of
-  # the two source files to be undefined (indicating a straight copy).
-  my ($aSourceFile);
-  if (defined($filePPC)) { 
-    $aSourceFile = $filePPC;
-  }
-  else {
-    $aSourceFile = $fileX86;
-  }
-
-  if ($aSourceFile->lIsDir()) {
-    if ($gVerbosity >= 3 || $gDryRun) {
-      print('mkdir '.(argumentEscape($fileTargetPath))[0]."\n");
-    }
-    if (!$gDryRun && !mkdir($fileTargetPath)) {
-      return complain(1, 'makeUniversal: mkdir: '.$!.' for:',
-                      $fileTargetPath);
-    }
-
-    my ($rv);
-
-    if (!($rv = makeUniversalDirectory($filePPC, $fileX86, $fileTargetPath))) {
-      # makeUniversalDirectory printed an error.
-      if ($isToplevel) {
-        command($gConfig{'cmd_rm'},'-rf','--',$fileTargetPath);
-      }
-    }
-    else {
-      # Touch the directory when leaving it.  If unify is being run on an
-      # .app bundle, the .app might show up without an icon because the
-      # system might have found the .app before it was completely built.
-      # Touching it dirties it in LaunchServices' mind.
-      if ($gVerbosity >= 3) {
-        print('touch '.(argumentEscape($fileTargetPath))[0]."\n");
-      }
-      utime(undef, undef, $fileTargetPath);
-    }
-
-    return $rv;
-  }
-  elsif ($aSourceFile->lIsSymLink()) {
-    my ($linkPPC, $linkX86);
-    if (defined($filePPC) && !defined($linkPPC=readlink($filePPC->path()))) {
-      return complain(1, 'makeUniversal: readlink ppc: '.$!.' for:',
-                      $filePPC->path());
-    }
-    if (defined($fileX86) && !defined($linkX86=readlink($fileX86->path()))) {
-      return complain(1, 'makeUniversal: readlink x86: '.$!.' for:',
-                      $fileX86->path());
-    }
-    if (defined($filePPC) && defined($fileX86) && $linkPPC ne $linkX86) {
-      return complain(1, 'makeUniversal: symbolic links differ:',
-                      $filePPC->path(),
-                      $fileX86->path());
-    }
-
-    # $aLink here serves the same purpose as $aSourceFile in the enclosing
-    # block: it refers to the target of the symbolic link, whether there
-    # is one valid source or two.
-    my ($aLink);
-    if (defined($linkPPC)) {
-      $aLink = $linkPPC;
-    }
-    else {
-      $aLink = $linkX86;
-    }
-
-    if ($gVerbosity >= 3 || $gDryRun) {
-      print('ln -s '.
-            join(' ',argumentEscape($aLink, $fileTargetPath))."\n");
-    }
-    if (!$gDryRun && !symlink($aLink, $fileTargetPath)) {
-      return complain(1, 'makeUniversal: symlink: '.$!.' for:',
-                      $aLink,
-                      $fileTargetPath);
-    }
-
-    return 1;
-  }
-  elsif($aSourceFile->lIsRegularFile()) {
-    my ($machPPC, $machX86, $fileName);
-    if (!defined($filePPC) || !defined($fileX86)) {
-      # One of the source files isn't present.  The right thing to do is
-      # to just copy what does exist straight over, so skip Mach-O checks.
-      $machPPC = 0;
-      $machX86 = 0;
-      if (defined($filePPC)) {
-        $fileName = $filePPC;
-      } elsif (defined($fileX86)) {
-        $fileName = $fileX86;
-      } else {
-        complain(1, "The file must exist in at least one directory");
-        exit(1);
-      }
-    }
-    else {
-      # both files exist, pick the name of one.
-      $fileName = $fileX86;
-      if (!defined($machPPC=$filePPC->isMachO())) {
-        return complain(1, 'makeUniversal: isFileMachO ppc failed for:',
-                        $filePPC->path());
-      }
-      if (!defined($machX86=$fileX86->isMachO())) {
-        return complain(1, 'makeUniversal: isFileMachO x86 failed for:',
-                        $fileX86->path());
-      }
-    }
-
-    if ($machPPC != $machX86) {
-      return complain(1, 'makeUniversal: variant Mach-O attributes:',
-                      $filePPC->path(),
-                  $fileX86->path());
-    }
-
-    if ($machPPC) {
-      # makeUniversalFile will print an error if it fails.
-      return makeUniversalFile($filePPC, $fileX86, $fileTargetPath);
-    }
-
-    if (grep { $fileName->path() =~ m/$_/; } @gSortMatches) {
-      # Regular files, but should be compared with sorting first.
-      # copyIfIdenticalWhenSorted will print an error if it fails.
-      return copyIfIdenticalWhenSorted($filePPC, $fileX86, $fileTargetPath);
-    }
-
-    # Regular file.  copyIfIdentical will print an error if it fails.
-    return copyIfIdentical($filePPC, $fileX86, $fileTargetPath);
-  }
-
-  # Special file, don't know how to handle.
-  return complain(1, 'makeUniversal: cannot handle special file:',
-                  $filePPC->path(),
-                  $fileX86->path());
-}
-
-# usage()
-#
-# Give the user a hand.
-sub usage() {
-  print STDERR (
-"usage: unify <ppc-path> <x86-path> <universal-path>\n".
-"            [--dry-run]           (print what would be done)\n".
-"            [--only-one <action>] (skip, copy, fail; default=copy)\n".
-"            [--verbosity <level>] (0, 1, 2, 3; default=2)\n");
-  return;
-}
-
-# readZipCRCs($zipFile)
-#
-# $zipFile is the pathname to a zip file whose directory will be read.
-# A reference to a hash is returned, with the member pathnames from the
-# zip file as keys, and reasonably unique identifiers as values.  The
-# format of the values is not specified exactly, but does include the
-# member CRCs and sizes and differentiates between files and directories.
-# It specifically does not distinguish between modification times.  On
-# failure, prints a message and returns undef.
-sub readZipCRCs($) {
-  my ($zipFile);
-  ($zipFile) = @_;
-
-  my ($ze, $zip);
-  $zip = Archive::Zip->new();
-
-  if (($ze = $zip->read($zipFile)) != AZ_OK) {
-    complain(1, 'readZipCRCs: read error '.$ze.' for:',
-             $zipFile);
-    return undef;
-  }
-
-  my ($member, %memberCRCs, @memberList);
-  %memberCRCs = ();
-  @memberList = $zip->members();
-
-  foreach $member (@memberList) {
-    # Take a few of the attributes that identify the file and stuff them into
-    # the members hash.  Directories will show up with size 0 and crc32 0,
-    # so isDirectory() is used to distinguish them from empty files.
-    $memberCRCs{$member->fileName()} = join(',', $member->isDirectory() ? 1 : 0,
-                                                 $member->uncompressedSize(),
-                                                 $member->crc32String());
-  }
-
-  return {%memberCRCs};
-}
-
-{
-  # FileAttrCache allows various attributes about a file to be cached
-  # so that if they are needed again after first use, no system calls
-  # will be made and the program won't need to hit the disk.
-
-  package FileAttrCache;
-
-  # from /usr/include/mach-o/loader.h
-  use constant MH_MAGIC    => 0xfeedface;
-  use constant MH_CIGAM    => 0xcefaedfe;
-  use constant MH_MAGIC_64 => 0xfeedfacf;
-  use constant MH_CIGAM_64 => 0xcffaedfe;
-
-  use Fcntl(':DEFAULT', ':mode');
-
-  # FileAttrCache->new($path)
-  #
-  # Creates a new FileAttrCache object for the file at path $path and
-  # returns it.  The cache is not primed at creation time, values are
-  # fetched lazily as they are needed.
-  sub new($$) {
-    my ($class, $path, $proto, $this);
-    ($proto, $path) = @_;
-    if (!($class = ref($proto))) {
-      $class = $proto;
-    }
-    $this = {
-      'path'        => $path,
-      'lstat'       => undef,
-      'lstatErrno'  => 0,
-      'lstatInit'   => 0,
-      'magic'       => undef,
-      'magic2'       => undef,
-      'magicErrno'  => 0,
-      'magicErrMsg' => undef,
-      'magicInit'   => 0,
-      'stat'        => undef,
-      'statErrno'   => 0,
-      'statInit'    => 0,
-    };
-    bless($this, $class);
-    return($this);
-  }
-
-  # $FileAttrCache->isFat()
-  #
-  # Returns true if the file is a fat Mach-O file, false if it's not, and
-  # undef if an error occurs.  See /usr/include/mach-o/fat.h.
-  sub isFat($) {
-    my ($magic, $magic2, $this);
-    ($this) = @_;
-
-    # magic() caches, there's no separate cache because isFat() doesn't hit
-    # the disk other than by calling magic().
-
-    if (!defined($magic = $this->magic())) {
-      return undef;
-    }
-    $magic2 = $this->magic2();
-
-    # We have to sanity check the second four bytes, because Java class
-    # files use the same magic number as Mach-O fat binaries.
-    # This logic is adapted from file(1), which says that Mach-O uses
-    # these bytes to count the number of architectures within, while
-    # Java uses it for a version number. Conveniently, there are only
-    # 18 labelled Mach-O architectures, and Java's first released
-    # class format used the version 43.0.
-    if ($magic == 0xcafebabe && $magic2 < 20) {
-      return 1;
-    }
-
-    return 0;
-  }
-
-  # $FileAttrCache->isMachO()
-  #
-  # Returns true if the file is a Mach-O image (including a fat file), false
-  # if it's not, and undef if an error occurs.  See
-  # /usr/include/mach-o/loader.h and /usr/include/mach-o/fat.h.
-  sub isMachO($) {
-    my ($magic, $this);
-    ($this) = @_;
-
-    # magic() caches, there's no separate cache because isMachO() doesn't hit
-    # the disk other than by calling magic().
-
-    if (!defined($magic = $this->magic())) {
-      return undef;
-    }
-
-    # Accept Mach-O fat files or Mach-O thin files of either endianness.
-    if ($magic == MH_MAGIC ||
-        $magic == MH_CIGAM ||
-        $magic == MH_MAGIC_64 ||
-        $magic == MH_CIGAM_64 ||
-        $this->isFat()) {
-      return 1;
-    }
-
-    return 0;
-  }
-
-  # $FileAttrCache->isZip()
-  #
-  # Returns true if the file is a zip file, false if it's not, and undef if
-  # an error occurs.  See http://www.pkware.com/business_and_developers/developer/popups/appnote.txt .
-  sub isZip($) {
-    my ($magic, $this);
-    ($this) = @_;
-
-    # magic() caches, there's no separate cache because isFat() doesn't hit
-    # the disk other than by calling magic().
-
-    if (!defined($magic = $this->magic())) {
-      return undef;
-    }
-
-    if ($magic == 0x504b0304) {
-      return 1;
-    }
-
-    return 0;
-  }
-
-  # $FileAttrCache->lIsExecutable()
-  #
-  # Wraps $FileAttrCache->lstat(), returning true if the file is has any,
-  # execute bit set, false if none are set, or undef if an error occurs.
-  # On error, $! is set to lstat's errno.
-  sub lIsExecutable($) {
-    my ($mode, $this);
-    ($this) = @_;
-
-    if (!defined($mode = $this->lstatMode())) {
-      return undef;
-    }
-
-    return $mode & (S_IXUSR | S_IXGRP | S_IXOTH);
-  }
-
-  # $FileAttrCache->lIsDir()
-  #
-  # Wraps $FileAttrCache->lstat(), returning true if the file is a directory,
-  # false if it isn't, or undef if an error occurs.  Because lstat is used,
-  # this will return false even if the file is a symlink pointing to a
-  # directory.  On error, $! is set to lstat's errno.
-  sub lIsDir($) {
-    my ($type, $this);
-    ($this) = @_;
-
-    if (!defined($type = $this->lstatType())) {
-      return undef;
-    }
-
-    return S_ISDIR($type);
-  }
-
-  # $FileAttrCache->lIsRegularFile()
-  #
-  # Wraps $FileAttrCache->lstat(), returning true if the file is a regular,
-  # file, false if it isn't, or undef if an error occurs.  Because lstat is
-  # used, this will return false even if the file is a symlink pointing to a
-  # regular file.  On error, $! is set to lstat's errno.
-  sub lIsRegularFile($) {
-    my ($type, $this);
-    ($this) = @_;
-
-    if (!defined($type = $this->lstatType())) {
-      return undef;
-    }
-
-    return S_ISREG($type);
-  }
-
-  # $FileAttrCache->lIsSymLink()
-  #
-  # Wraps $FileAttrCache->lstat(), returning true if the file is a symbolic,
-  # link, false if it isn't, or undef if an error occurs.  On error, $! is
-  # set to lstat's errno.
-  sub lIsSymLink($) {
-    my ($type, $this);
-    ($this) = @_;
-   
-    if (!defined($type = $this->lstatType())) {
-      return undef;
-    }
-
-    return S_ISLNK($type);
-  }
-
-  # $FileAttrCache->lstat()
-  #
-  # Wraps the lstat system call, providing a cache to speed up multiple
-  # lstat calls for the same file.  See lstat(2) and lstat in perlfunc(1).
-  sub lstat($) {
-    my (@stat, $this);
-    ($this) = @_;
-
-    # Use the cached lstat result.
-    if ($$this{'lstatInit'}) {
-      if (defined($$this{'lstatErrno'})) {
-        $! = $$this{'lstatErrno'};
-      }
-      return @{$$this{'lstat'}};
-    }
-    $$this{'lstatInit'} = 1;
-
-    if (!(@stat = CORE::lstat($$this{'path'}))) {
-      $$this{'lstatErrno'} = $!;
-    }
-
-    $$this{'lstat'} = [@stat];
-    return @stat;
-  }
-
-  # $FileAttrCache->lstatMode()
-  #
-  # Wraps $FileAttrCache->lstat(), returning the mode bits from the st_mode
-  # field, or undef if an error occurs.  On error, $! is set to lstat's
-  # errno.
-  sub lstatMode($) {
-    my (@stat, $this);
-    ($this) = @_;
-
-    if (!(@stat = $this->lstat())) {
-      return undef;
-    }
-
-    return S_IMODE($stat[2]);
-  }
-
-  # $FileAttrCache->lstatType()
-  #
-  # Wraps $FileAttrCache->lstat(), returning the type bits from the st_mode
-  # field, or undef if an error occurs.  On error, $! is set to lstat's
-  # errno.
-  sub lstatType($) {
-    my (@stat, $this);
-    ($this) = @_;
-
-    if (!(@stat = $this->lstat())) {
-      return undef;
-    }
-
-    return S_IFMT($stat[2]);
-  }
-
-  # $FileAttrCache->magic()
-  #
-  # Returns the "magic number" for the file by reading its first four bytes
-  # as a big-endian unsigned 32-bit integer and returning the result.  If an
-  # error occurs, returns undef and prints diagnostic messages to stderr.  If
-  # the file is shorter than 32 bits, returns -1.  A cache is provided to
-  # speed multiple magic calls for the same file.
-  sub magic($) {
-    my ($this);
-    ($this) = @_;
-
-    # Use the cached magic result.
-    if ($$this{'magicInit'}) {
-      if (defined($$this{'magicErrno'})) {
-        if (defined($$this{'magicErrMsg'})) {
-          main::complain(1, 'FileAttrCache::magic: '.$$this{'magicErrMsg'}.' for:',
-                   $$this{'path'});
-        }
-        $! = $$this{'magicErrno'};
-      }
-      return $$this{'magic'};
-    }
-
-    $$this{'magicInit'} = 1;
-
-    my ($fh);
-    if (!sysopen($fh, $$this{'path'}, O_RDONLY)) {
-      $$this{'magicErrno'} = $!;
-      $$this{'magicErrMsg'} = 'open "'.$$this{'path'}.'": '.$!;
-      main::complain(1, 'FileAttrCache::magic: '.$$this{'magicErrMsg'}.' for:',
-               $$this{'path'});
-      return undef;
-    }
-
-    $! = 0;
-    my ($bytes, $magic, $bytes2, $magic2);
-    if (!defined($bytes = sysread($fh, $magic, 4))) {
-      $$this{'magicErrno'} = $!;
-      $$this{'magicErrMsg'} = 'read "'.$$this{'path'}.'": '.$!;
-      main::complain(1, 'FileAttrCache::magic: '.$$this{'magicErrMsg'}.' for:',
-               $$this{'path'});
-      close($fh);
-      return undef;
-    }
-    else {
-      $bytes2 = sysread($fh, $magic2, 4);
-    }
-
-    close($fh);
-
-    if ($bytes != 4) {
-      # The file is too short, didn't read a magic number.  This isn't really
-      # an error.  Return an unlikely value.
-      $$this{'magic'} = -1;
-      $$this{'magic2'} = -1;
-      return -1;
-    }
-    if ($bytes2 != 4) {
-      # File is too short to read a second 4 bytes.
-      $magic2 = -1;
-    }
-
-    $$this{'magic'} = unpack('N', $magic);
-    $$this{'magic2'} = unpack('N', $magic2);
-    return $$this{'magic'};
-  }
-
-  # $FileAttrCache->magic2()
-  #
-  # Returns the second four bytes of the file as a 32-bit little endian number.
-  # See magic(), above for more info.
-  sub magic2($) {
-    my ($this);
-    ($this) = @_;
-
-    # we do the actual work (and cache it) in magic().
-    if (!$$this{'magicInit'}) {
-      my $magic = $$this->magic();
-    }
-
-    return $$this{'magic2'};
-  }
-
-  # $FileAttrCache->path()
-  #
-  # Returns the file's pathname.
-  sub path($) {
-    my ($this);
-    ($this) = @_;
-    return $$this{'path'};
-  }
-
-  # $FileAttrCache->stat()
-  #
-  # Wraps the stat system call, providing a cache to speed up multiple
-  # stat calls for the same file.  If lstat() has already been called and
-  # the file is not a symbolic link, the cached lstat() result will be used.
-  # See stat(2) and lstat in perlfunc(1).
-  sub stat($) {
-    my (@stat, $this);
-    ($this) = @_;
-
-    # Use the cached stat result.
-    if ($$this{'statInit'}) {
-      if (defined($$this{'statErrno'})) {
-        $! = $$this{'statErrno'};
-      }
-      return @{$$this{'stat'}};
-    }
-
-    $$this{'statInit'} = 1;
-
-    # If lstat has already been called, and the file isn't a symbolic link,
-    # use the cached lstat result.
-    if ($$this{'lstatInit'} && !$$this{'lstatErrno'} &&
-        !S_ISLNK(${$$this{'lstat'}}[2])) {
-      $$this{'stat'} = $$this{'lstat'};
-      return @{$$this{'stat'}};
-    }
-
-    if (!(@stat = CORE::stat($$this{'path'}))) {
-      $$this{'statErrno'} = $!;
-    }
-
-    $$this{'stat'} = [@stat];
-    return @stat;
-  }
-
-  # $FileAttrCache->statSize()
-  #
-  # Wraps $FileAttrCache->stat(), returning the st_size field, or undef
-  # undef if an error occurs.  On error, $! is set to stat's errno.
-  sub statSize($) {
-    my (@stat, $this);
-    ($this) = @_;
-
-    if (!(@stat = $this->lstat())) {
-      return undef;
-    }
-
-    return $stat[7];
-  }
-}
--- a/js/src/old-configure.in
+++ b/js/src/old-configure.in
@@ -296,24 +296,16 @@ AC_SUBST(GNU_CC)
 AC_SUBST(GNU_CXX)
 
 dnl ========================================================
 dnl Checks for programs.
 dnl ========================================================
 if test "$COMPILE_ENVIRONMENT"; then
 
 dnl ========================================================
-dnl = Mac OS X toolchain support
-dnl ========================================================
-
-dnl The universal machinery sets UNIVERSAL_BINARY to inform packager.mk
-dnl that a universal binary is being produced.
-AC_SUBST(UNIVERSAL_BINARY)
-
-dnl ========================================================
 dnl = Mac OS X SDK support
 dnl ========================================================
 MACOS_SDK_DIR=
 MOZ_ARG_WITH_STRING(macos-sdk,
 [  --with-macos-sdk=dir    Location of platform SDK to use (Mac OS X only)],
     MACOS_SDK_DIR=$withval)
 
 dnl MACOS_SDK_DIR will be set to the SDK location whenever one is in use.
--- a/old-configure.in
+++ b/old-configure.in
@@ -429,41 +429,16 @@ AC_SUBST(WRAP_STL_INCLUDES)
 AC_SUBST(MOZ_MSVC_STL_WRAP_RAISE)
 
 dnl ========================================================
 dnl Checks for programs.
 dnl ========================================================
 if test "$COMPILE_ENVIRONMENT"; then
 
 dnl ========================================================
-dnl = Mac OS X toolchain support
-dnl ========================================================
-
-dnl The universal machinery sets UNIVERSAL_BINARY to inform packager.mk
-dnl that a universal binary is being produced.
-AC_SUBST(UNIVERSAL_BINARY)
-
-MOZ_ARG_WITH_STRING(unify-dist,
-[  --with-unify-dist=dir   Location of the dist directory to unify with at packaging time (Mac OS X universal build only)],
-    UNIFY_DIST=$withval)
-if test -n "$UNIVERSAL_BINARY"; then
-    if test -z "$UNIFY_DIST"; then
-        AC_MSG_ERROR([You need to provide the --with-unify-dist=dir argument when performing a universal build])
-    fi
-    case "$UNIFY_DIST" in
-    /*)
-        ;;
-    *)
-        UNIFY_DIST="${MOZ_BUILD_ROOT}/${UNIFY_DIST}"
-        ;;
-    esac
-fi
-AC_SUBST(UNIFY_DIST)
-
-dnl ========================================================
 dnl = Mac OS X SDK support
 dnl ========================================================
 MACOS_SDK_DIR=
 MOZ_ARG_WITH_STRING(macos-sdk,
 [  --with-macos-sdk=dir    Location of platform SDK to use (Mac OS X only)],
     MACOS_SDK_DIR=$withval)
 
 MACOS_PRIVATE_FRAMEWORKS_DIR_DEFAULTED=
--- a/python/mozbuild/mozbuild/base.py
+++ b/python/mozbuild/mozbuild/base.py
@@ -648,23 +648,17 @@ class MachCommandBase(MozbuildObject):
             if topobjdir:
                 # If we're inside a objdir and the found mozconfig resolves to
                 # another objdir, we abort. The reasoning here is that if you
                 # are inside an objdir you probably want to perform actions on
                 # that objdir, not another one. This prevents accidental usage
                 # of the wrong objdir when the current objdir is ambiguous.
                 config_topobjdir = dummy.resolve_mozconfig_topobjdir()
 
-                try:
-                    universal_bin = dummy.substs.get('UNIVERSAL_BINARY')
-                except:
-                    universal_bin = False
-
-                if config_topobjdir and not (samepath(topobjdir, config_topobjdir) or
-                        universal_bin and topobjdir.startswith(config_topobjdir)):
+                if config_topobjdir and not samepath(topobjdir, config_topobjdir):
                     raise ObjdirMismatchException(topobjdir, config_topobjdir)
         except BuildEnvironmentNotFoundException:
             pass
         except ObjdirMismatchException as e:
             print('Ambiguous object directory detected. We detected that '
                 'both %s and %s could be object directories. This is '
                 'typically caused by having a mozconfig pointing to a '
                 'different object directory from the current working '
--- a/python/mozbuild/mozbuild/mozinfo.py
+++ b/python/mozbuild/mozbuild/mozinfo.py
@@ -54,26 +54,22 @@ def build_dict(config, env=os.environ):
     # Build app name
     if 'MOZ_MULET' in substs and substs.get('MOZ_MULET') == "1":
         d["buildapp"] = "mulet"
     elif 'MOZ_BUILD_APP' in substs:
         d["buildapp"] = substs["MOZ_BUILD_APP"]
 
     # processor
     p = substs["TARGET_CPU"]
-    # for universal mac builds, put in a special value
-    if d["os"] == "mac" and "UNIVERSAL_BINARY" in substs and substs["UNIVERSAL_BINARY"] == "1":
-        p = "universal-x86-x86_64"
-    else:
-        # do some slight massaging for some values
-        #TODO: retain specific values in case someone wants them?
-        if p.startswith("arm"):
-            p = "arm"
-        elif re.match("i[3-9]86", p):
-            p = "x86"
+    # do some slight massaging for some values
+    #TODO: retain specific values in case someone wants them?
+    if p.startswith("arm"):
+        p = "arm"
+    elif re.match("i[3-9]86", p):
+        p = "x86"
     d["processor"] = p
     # hardcoded list of 64-bit CPUs
     if p in ["x86_64", "ppc64"]:
         d["bits"] = 64
     # hardcoded list of known 32-bit CPUs
     elif p in ["x86", "arm", "ppc"]:
         d["bits"] = 32
     # other CPUs will wind up with unknown bits
--- a/python/mozbuild/mozbuild/test/test_mozinfo.py
+++ b/python/mozbuild/mozbuild/test/test_mozinfo.py
@@ -91,39 +91,16 @@ class TestBuildDict(unittest.TestCase, B
             TARGET_CPU='x86_64',
             MOZ_WIDGET_TOOLKIT='cocoa',
         )))
         self.assertEqual('mac', d['os'])
         self.assertEqual('x86_64', d['processor'])
         self.assertEqual('cocoa', d['toolkit'])
         self.assertEqual(64, d['bits'])
 
-    def test_mac_universal(self):
-        d = build_dict(self._config(dict(
-            OS_TARGET='Darwin',
-            TARGET_CPU='i386',
-            MOZ_WIDGET_TOOLKIT='cocoa',
-            UNIVERSAL_BINARY='1',
-        )))
-        self.assertEqual('mac', d['os'])
-        self.assertEqual('universal-x86-x86_64', d['processor'])
-        self.assertEqual('cocoa', d['toolkit'])
-        self.assertFalse('bits' in d)
-
-        d = build_dict(self._config(dict(
-            OS_TARGET='Darwin',
-            TARGET_CPU='x86_64',
-            MOZ_WIDGET_TOOLKIT='cocoa',
-            UNIVERSAL_BINARY='1',
-        )))
-        self.assertEqual('mac', d['os'])
-        self.assertEqual('universal-x86-x86_64', d['processor'])
-        self.assertEqual('cocoa', d['toolkit'])
-        self.assertFalse('bits' in d)
-
     def test_android(self):
         d = build_dict(self._config(dict(
             OS_TARGET='Android',
             TARGET_CPU='arm',
             MOZ_WIDGET_TOOLKIT='android',
         )))
         self.assertEqual('android', d['os'])
         self.assertEqual('arm', d['processor'])
--- a/python/mozbuild/mozpack/test/python.ini
+++ b/python/mozbuild/mozpack/test/python.ini
@@ -5,9 +5,8 @@
 [test_files.py]
 [test_manifests.py]
 [test_mozjar.py]
 [test_packager.py]
 [test_packager_formats.py]
 [test_packager_l10n.py]
 [test_packager_unpack.py]
 [test_path.py]
-[test_unify.py]
deleted file mode 100644
--- a/python/mozbuild/mozpack/test/test_unify.py
+++ /dev/null
@@ -1,199 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-from mozbuild.util import ensureParentDir
-
-from mozpack.unify import (
-    UnifiedFinder,
-    UnifiedBuildFinder,
-)
-import mozunit
-from mozpack.test.test_files import TestWithTmpDir
-from mozpack.files import FileFinder
-from mozpack.mozjar import JarWriter
-from mozpack.test.test_files import MockDest
-from cStringIO import StringIO
-import os
-import sys
-from mozpack.errors import (
-    ErrorMessage,
-    AccumulatedErrors,
-    errors,
-)
-
-
-class TestUnified(TestWithTmpDir):
-    def create_one(self, which, path, content):
-        file = self.tmppath(os.path.join(which, path))
-        ensureParentDir(file)
-        open(file, 'wb').write(content)
-
-    def create_both(self, path, content):
-        for p in ['a', 'b']:
-            self.create_one(p, path, content)
-
-
-class TestUnifiedFinder(TestUnified):
-    def test_unified_finder(self):
-        self.create_both('foo/bar', 'foobar')
-        self.create_both('foo/baz', 'foobaz')
-        self.create_one('a', 'bar', 'bar')
-        self.create_one('b', 'baz', 'baz')
-        self.create_one('a', 'qux', 'foobar')
-        self.create_one('b', 'qux', 'baz')
-        self.create_one('a', 'test/foo', 'a\nb\nc\n')
-        self.create_one('b', 'test/foo', 'b\nc\na\n')
-        self.create_both('test/bar', 'a\nb\nc\n')
-
-        finder = UnifiedFinder(FileFinder(self.tmppath('a')),
-                               FileFinder(self.tmppath('b')),
-                               sorted=['test'])
-        self.assertEqual(sorted([(f, c.open().read())
-                                 for f, c in finder.find('foo')]),
-                         [('foo/bar', 'foobar'), ('foo/baz', 'foobaz')])
-        self.assertRaises(ErrorMessage, any, finder.find('bar'))
-        self.assertRaises(ErrorMessage, any, finder.find('baz'))
-        self.assertRaises(ErrorMessage, any, finder.find('qux'))
-        self.assertEqual(sorted([(f, c.open().read())
-                                 for f, c in finder.find('test')]),
-                         [('test/bar', 'a\nb\nc\n'),
-                          ('test/foo', 'a\nb\nc\n')])
-
-
-class TestUnifiedBuildFinder(TestUnified):
-    def test_unified_build_finder(self):
-        finder = UnifiedBuildFinder(FileFinder(self.tmppath('a')),
-                                    FileFinder(self.tmppath('b')))
-
-        # Test chrome.manifest unification
-        self.create_both('chrome.manifest', 'a\nb\nc\n')
-        self.create_one('a', 'chrome/chrome.manifest', 'a\nb\nc\n')
-        self.create_one('b', 'chrome/chrome.manifest', 'b\nc\na\n')
-        self.assertEqual(sorted([(f, c.open().read()) for f, c in
-                                 finder.find('**/chrome.manifest')]),
-                         [('chrome.manifest', 'a\nb\nc\n'),
-                          ('chrome/chrome.manifest', 'a\nb\nc\n')])
-
-        # Test buildconfig.html unification
-        self.create_one('a', 'chrome/browser/foo/buildconfig.html',
-                        '\n'.join([
-                            '<html>',
-                            '<body>',
-                            '<h1>about:buildconfig</h1>',
-                            '<div>foo</div>',
-                            '</body>',
-                            '</html>',
-                        ]))
-        self.create_one('b', 'chrome/browser/foo/buildconfig.html',
-                        '\n'.join([
-                            '<html>',
-                            '<body>',
-                            '<h1>about:buildconfig</h1>',
-                            '<div>bar</div>',
-                            '</body>',
-                            '</html>',
-                        ]))
-        self.assertEqual(sorted([(f, c.open().read()) for f, c in
-                                 finder.find('**/buildconfig.html')]),
-                         [('chrome/browser/foo/buildconfig.html', '\n'.join([
-                             '<html>',
-                             '<body>',
-                             '<h1>about:buildconfig</h1>',
-                             '<div>foo</div>',
-                             '<hr> </hr>',
-                             '<div>bar</div>',
-                             '</body>',
-                             '</html>',
-                         ]))])
-
-        # Test xpi file unification
-        xpi = MockDest()
-        with JarWriter(fileobj=xpi, compress=True) as jar:
-            jar.add('foo', 'foo')
-            jar.add('bar', 'bar')
-        foo_xpi = xpi.read()
-        self.create_both('foo.xpi', foo_xpi)
-
-        with JarWriter(fileobj=xpi, compress=True) as jar:
-            jar.add('foo', 'bar')
-        self.create_one('a', 'bar.xpi', foo_xpi)
-        self.create_one('b', 'bar.xpi', xpi.read())
-
-        errors.out = StringIO()
-        with self.assertRaises(AccumulatedErrors), errors.accumulate():
-            self.assertEqual([(f, c.open().read()) for f, c in
-                              finder.find('*.xpi')],
-                             [('foo.xpi', foo_xpi)])
-        errors.out = sys.stderr
-
-        # Test install.rdf unification
-        x86_64 = 'Darwin_x86_64-gcc3'
-        x86 = 'Darwin_x86-gcc3'
-        target_tag = '<{em}targetPlatform>{platform}</{em}targetPlatform>'
-        target_attr = '{em}targetPlatform="{platform}" '
-
-        rdf_tag = ''.join([
-            '<{RDF}Description {em}bar="bar" {em}qux="qux">',
-              '<{em}foo>foo</{em}foo>',
-              '{targets}',
-              '<{em}baz>baz</{em}baz>',
-            '</{RDF}Description>'
-        ])
-        rdf_attr = ''.join([
-            '<{RDF}Description {em}bar="bar" {attr}{em}qux="qux">',
-              '{targets}',
-              '<{em}foo>foo</{em}foo><{em}baz>baz</{em}baz>',
-            '</{RDF}Description>'
-        ])
-
-        for descr_ns, target_ns in (('RDF:', ''), ('', 'em:'), ('RDF:', 'em:')):
-            # First we need to infuse the above strings with our namespaces and
-            # platform values.
-            ns = { 'RDF': descr_ns, 'em': target_ns }
-            target_tag_x86_64 = target_tag.format(platform=x86_64, **ns)
-            target_tag_x86 = target_tag.format(platform=x86, **ns)
-            target_attr_x86_64 = target_attr.format(platform=x86_64, **ns)
-            target_attr_x86 = target_attr.format(platform=x86, **ns)
-
-            tag_x86_64 = rdf_tag.format(targets=target_tag_x86_64, **ns)
-            tag_x86 = rdf_tag.format(targets=target_tag_x86, **ns)
-            tag_merged = rdf_tag.format(targets=target_tag_x86_64 + target_tag_x86, **ns)
-            tag_empty = rdf_tag.format(targets="", **ns)
-
-            attr_x86_64 = rdf_attr.format(attr=target_attr_x86_64, targets="", **ns)
-            attr_x86 = rdf_attr.format(attr=target_attr_x86, targets="", **ns)
-            attr_merged = rdf_attr.format(attr="", targets=target_tag_x86_64 + target_tag_x86, **ns)
-
-            # This table defines the test cases, columns "a" and "b" being the
-            # contents of the install.rdf of the respective platform and
-            # "result" the exepected merged content after unification.
-            testcases = (
-              #_____a_____  _____b_____  ___result___#
-              (tag_x86_64,  tag_x86,     tag_merged  ),
-              (tag_x86_64,  tag_empty,   tag_empty   ),
-              (tag_empty,   tag_x86,     tag_empty   ),
-              (tag_empty,   tag_empty,   tag_empty   ),
-
-              (attr_x86_64, attr_x86,    attr_merged ),
-              (tag_x86_64,  attr_x86,    tag_merged  ),
-              (attr_x86_64, tag_x86,     attr_merged ),
-
-              (attr_x86_64, tag_empty,   tag_empty   ),
-              (tag_empty,   attr_x86,    tag_empty   )
-            )
-
-            # Now create the files from the above table and compare
-            results = []
-            for emid, (rdf_a, rdf_b, result) in enumerate(testcases):
-                filename = 'ext/id{0}/install.rdf'.format(emid)
-                self.create_one('a', filename, rdf_a)
-                self.create_one('b', filename, rdf_b)
-                results.append((filename, result))
-
-            self.assertEqual(sorted([(f, c.open().read()) for f, c in
-                                     finder.find('**/install.rdf')]), results)
-
-
-if __name__ == '__main__':
-    mozunit.main()
deleted file mode 100644
--- a/python/mozbuild/mozpack/unify.py
+++ /dev/null
@@ -1,231 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-from __future__ import absolute_import
-
-from mozpack.files import (
-    BaseFinder,
-    JarFinder,
-    ExecutableFile,
-    BaseFile,
-    GeneratedFile,
-)
-from mozpack.executables import (
-    MACHO_SIGNATURES,
-)
-from mozpack.mozjar import JarReader
-from mozpack.errors import errors
-from tempfile import mkstemp
-import mozpack.path as mozpath
-import struct
-import os
-import re
-import subprocess
-import buildconfig
-from collections import OrderedDict
-
-# Regular expressions for unifying install.rdf
-FIND_TARGET_PLATFORM = re.compile(r"""
-    <(?P<ns>[-._0-9A-Za-z]+:)?targetPlatform>       # The targetPlatform tag, with any namespace
-    (?P<platform>[^<]*)                             # The actual platform value
-    </(?P=ns)?targetPlatform>                       # The closing tag
-    """, re.X)
-FIND_TARGET_PLATFORM_ATTR = re.compile(r"""
-    (?P<tag><(?:[-._0-9A-Za-z]+:)?Description)      # The opening part of the <Description> tag
-    (?P<attrs>[^>]*?)\s+                            # The initial attributes
-    (?P<ns>[-._0-9A-Za-z]+:)?targetPlatform=        # The targetPlatform attribute, with any namespace
-    [\'"](?P<platform>[^\'"]+)[\'"]                 # The actual platform value
-    (?P<otherattrs>[^>]*?>)                         # The remaining attributes and closing angle bracket
-    """, re.X)
-
-def may_unify_binary(file):
-    '''
-    Return whether the given BaseFile instance is an ExecutableFile that
-    may be unified. Only non-fat Mach-O binaries are to be unified.
-    '''
-    if isinstance(file, ExecutableFile):
-        signature = file.open().read(4)
-        if len(signature) < 4:
-            return False
-        signature = struct.unpack('>L', signature)[0]
-        if signature in MACHO_SIGNATURES:
-            return True
-    return False
-
-
-class UnifiedExecutableFile(BaseFile):
-    '''
-    File class for executable and library files that to be unified with 'lipo'.
-    '''
-    def __init__(self, executable1, executable2):
-        '''
-        Initialize a UnifiedExecutableFile with a pair of ExecutableFiles to
-        be unified. They are expected to be non-fat Mach-O executables.
-        '''
-        assert isinstance(executable1, ExecutableFile)
-        assert isinstance(executable2, ExecutableFile)
-        self._executables = (executable1, executable2)
-
-    def copy(self, dest, skip_if_older=True):
-        '''
-        Create a fat executable from the two Mach-O executable given when
-        creating the instance.
-        skip_if_older is ignored.
-        '''
-        assert isinstance(dest, basestring)
-        tmpfiles = []
-        try:
-            for e in self._executables:
-                fd, f = mkstemp()
-                os.close(fd)
-                tmpfiles.append(f)
-                e.copy(f, skip_if_older=False)
-            lipo = buildconfig.substs.get('LIPO') or 'lipo'
-            subprocess.call([lipo, '-create'] + tmpfiles + ['-output', dest])
-        finally:
-            for f in tmpfiles:
-                os.unlink(f)
-
-
-class UnifiedFinder(BaseFinder):
-    '''
-    Helper to get unified BaseFile instances from two distinct trees on the
-    file system.
-    '''
-    def __init__(self, finder1, finder2, sorted=[], **kargs):
-        '''
-        Initialize a UnifiedFinder. finder1 and finder2 are BaseFinder
-        instances from which files are picked. UnifiedFinder.find() will act as
-        FileFinder.find() but will error out when matches can only be found in
-        one of the two trees and not the other. It will also error out if
-        matches can be found on both ends but their contents are not identical.
-
-        The sorted argument gives a list of mozpath.match patterns. File
-        paths matching one of these patterns will have their contents compared
-        with their lines sorted.
-        '''
-        assert isinstance(finder1, BaseFinder)
-        assert isinstance(finder2, BaseFinder)
-        self._finder1 = finder1
-        self._finder2 = finder2
-        self._sorted = sorted
-        BaseFinder.__init__(self, finder1.base, **kargs)
-
-    def _find(self, path):
-        '''
-        UnifiedFinder.find() implementation.
-        '''
-        files1 = OrderedDict()
-        for p, f in self._finder1.find(path):
-            files1[p] = f
-        files2 = set()
-        for p, f in self._finder2.find(path):
-            files2.add(p)
-            if p in files1:
-                if may_unify_binary(files1[p]) and \
-                        may_unify_binary(f):
-                    yield p, UnifiedExecutableFile(files1[p], f)
-                else:
-                    err = errors.count
-                    unified = self.unify_file(p, files1[p], f)
-                    if unified:
-                        yield p, unified
-                    elif err == errors.count:
-                        self._report_difference(p, files1[p], f)
-            else:
-                errors.error('File missing in %s: %s' %
-                             (self._finder1.base, p))
-        for p in [p for p in files1 if not p in files2]:
-            errors.error('File missing in %s: %s' % (self._finder2.base, p))
-
-    def _report_difference(self, path, file1, file2):
-        '''
-        Report differences between files in both trees.
-        '''
-        errors.error("Can't unify %s: file differs between %s and %s" %
-                     (path, self._finder1.base, self._finder2.base))
-        if not isinstance(file1, ExecutableFile) and \
-                not isinstance(file2, ExecutableFile):
-            from difflib import unified_diff
-            for line in unified_diff(file1.open().readlines(),
-                                     file2.open().readlines(),
-                                     os.path.join(self._finder1.base, path),
-                                     os.path.join(self._finder2.base, path)):
-                errors.out.write(line)
-
-    def unify_file(self, path, file1, file2):
-        '''
-        Given two BaseFiles and the path they were found at, check whether
-        their content match and return the first BaseFile if they do.
-        '''
-        content1 = file1.open().readlines()
-        content2 = file2.open().readlines()
-        if content1 == content2:
-            return file1
-        for pattern in self._sorted:
-            if mozpath.match(path, pattern):
-                if sorted(content1) == sorted(content2):
-                    return file1
-                break
-        return None
-
-
-class UnifiedBuildFinder(UnifiedFinder):
-    '''
-    Specialized UnifiedFinder for Mozilla applications packaging. It allows
-    "*.manifest" files to differ in their order, and unifies "buildconfig.html"
-    files by merging their content.
-    '''
-    def __init__(self, finder1, finder2, **kargs):
-        UnifiedFinder.__init__(self, finder1, finder2,
-                               sorted=['**/*.manifest'], **kargs)
-
-    def unify_file(self, path, file1, file2):
-        '''
-        Unify files taking Mozilla application special cases into account.
-        Otherwise defer to UnifiedFinder.unify_file.
-        '''
-        basename = mozpath.basename(path)
-        if basename == 'buildconfig.html':
-            content1 = file1.open().readlines()
-            content2 = file2.open().readlines()
-            # Copy everything from the first file up to the end of its <body>,
-            # insert a <hr> between the two files and copy the second file's
-            # content beginning after its leading <h1>.
-            return GeneratedFile(''.join(
-                content1[:content1.index('</body>\n')] +
-                ['<hr> </hr>\n'] +
-                content2[content2.index('<h1>about:buildconfig</h1>\n') + 1:]
-            ))
-        elif basename == 'install.rdf':
-            # install.rdf files often have em:targetPlatform (either as
-            # attribute or as tag) that will differ between platforms. The
-            # unified install.rdf should contain both em:targetPlatforms if
-            # they exist, or strip them if only one file has a target platform.
-            content1, content2 = (
-                FIND_TARGET_PLATFORM_ATTR.sub(lambda m: \
-                    m.group('tag') + m.group('attrs') + m.group('otherattrs') +
-                        '<%stargetPlatform>%s</%stargetPlatform>' % \
-                        (m.group('ns') or "", m.group('platform'), m.group('ns') or ""),
-                    f.open().read()
-                ) for f in (file1, file2)
-            )
-
-            platform2 = FIND_TARGET_PLATFORM.search(content2)
-            return GeneratedFile(FIND_TARGET_PLATFORM.sub(
-                lambda m: m.group(0) + platform2.group(0) if platform2 else '',
-                content1
-            ))
-        elif path.endswith('.xpi'):
-            finder1 = JarFinder(os.path.join(self._finder1.base, path),
-                                JarReader(fileobj=file1.open()))
-            finder2 = JarFinder(os.path.join(self._finder2.base, path),
-                                JarReader(fileobj=file2.open()))
-            unifier = UnifiedFinder(finder1, finder2, sorted=self._sorted)
-            err = errors.count
-            all(unifier.find(''))
-            if err == errors.count:
-                return file1
-            return None
-        return UnifiedFinder.unify_file(self, path, file1, file2)
--- a/toolkit/mozapps/installer/package-name.mk
+++ b/toolkit/mozapps/installer/package-name.mk
@@ -26,21 +26,17 @@ endif
 ifeq ($(OS_ARCH),WINNT)
 ifeq ($(TARGET_CPU),x86_64)
 MOZ_PKG_PLATFORM := win64
 else
 MOZ_PKG_PLATFORM := win32
 endif
 endif
 ifeq ($(OS_ARCH),Darwin)
-ifdef UNIVERSAL_BINARY
 MOZ_PKG_PLATFORM := mac
-else
-MOZ_PKG_PLATFORM := mac
-endif
 endif
 ifeq ($(TARGET_OS),linux-gnu)
 MOZ_PKG_PLATFORM := linux-$(TARGET_CPU)
 endif
 endif #MOZ_PKG_PLATFORM
 
 ifdef MOZ_PKG_SPECIAL
 MOZ_PKG_PLATFORM := $(MOZ_PKG_PLATFORM)-$(MOZ_PKG_SPECIAL)
--- a/toolkit/mozapps/installer/packager.mk
+++ b/toolkit/mozapps/installer/packager.mk
@@ -16,17 +16,17 @@ endif
 
 installer-stage: prepare-package
 ifndef MOZ_PKG_MANIFEST
 	$(error MOZ_PKG_MANIFEST unspecified!)
 endif
 	@rm -rf $(DEPTH)/installer-stage $(DIST)/xpt
 	@echo 'Staging installer files...'
 	@$(NSINSTALL) -D $(DEPTH)/installer-stage/core
-	@cp -av $(DIST)/$(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)/. $(DEPTH)/installer-stage/core
+	@cp -av $(DIST)/$(MOZ_PKG_DIR)$(_BINPATH)/. $(DEPTH)/installer-stage/core
 ifdef MOZ_SIGN_PREPARED_PACKAGE_CMD
 # The && true is necessary to make sure Pymake spins a shell
 	$(MOZ_SIGN_PREPARED_PACKAGE_CMD) $(DEPTH)/installer-stage && true
 endif
 	$(call MAKE_SIGN_EME_VOUCHER,$(DEPTH)/installer-stage/core)
 	@(cd $(DEPTH)/installer-stage/core && $(CREATE_PRECOMPLETE_CMD))
 
 ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
@@ -46,20 +46,19 @@ stage-package: $(MOZ_PKG_MANIFEST) $(MOZ
 		$(if $(filter-out 0,$(MOZ_PKG_FATAL_WARNINGS)),,--ignore-errors) \
 		$(if $(MOZ_PACKAGER_MINIFY),--minify) \
 		$(if $(MOZ_PACKAGER_MINIFY_JS),--minify-js \
 		  $(addprefix --js-binary ,$(JS_BINARY)) \
 		) \
 		$(if $(JARLOG_DIR),$(addprefix --jarlog ,$(wildcard $(JARLOG_FILE_AB_CD)))) \
 		$(if $(OPTIMIZEJARS),--optimizejars) \
 		$(if $(DISABLE_JAR_COMPRESSION),--disable-compression) \
-		$(addprefix --unify ,$(UNIFY_DIST)) \
-		$(MOZ_PKG_MANIFEST) $(DIST) $(DIST)/$(STAGEPATH)$(MOZ_PKG_DIR)$(if $(MOZ_PKG_MANIFEST),,$(_BINPATH)) \
+		$(MOZ_PKG_MANIFEST) $(DIST) $(DIST)/$(MOZ_PKG_DIR)$(if $(MOZ_PKG_MANIFEST),,$(_BINPATH)) \
 		$(if $(filter omni,$(MOZ_PACKAGER_FORMAT)),$(if $(NON_OMNIJAR_FILES),--non-resource $(NON_OMNIJAR_FILES)))
-	$(PYTHON) $(MOZILLA_DIR)/toolkit/mozapps/installer/find-dupes.py $(MOZ_PKG_DUPEFLAGS) $(DIST)/$(STAGEPATH)$(MOZ_PKG_DIR)
+	$(PYTHON) $(MOZILLA_DIR)/toolkit/mozapps/installer/find-dupes.py $(MOZ_PKG_DUPEFLAGS) $(DIST)/$(MOZ_PKG_DIR)
 ifndef MOZ_THUNDERBIRD
 	# Package mozharness
 	$(call py_action,test_archive, \
 		mozharness \
 		$(ABS_DIST)/$(PKG_PATH)$(MOZHARNESS_PACKAGE))
 endif # MOZ_THUNDERBIRD
 ifdef MOZ_PACKAGE_JSSHELL
 	# Package JavaScript Shell
@@ -77,18 +76,18 @@ ifdef MOZ_CODE_COVERAGE
 	# Package code coverage gcno tree
 	@echo 'Packaging code coverage data...'
 	$(RM) $(CODE_COVERAGE_ARCHIVE_BASENAME).zip
 	$(PYTHON) -mmozbuild.codecoverage.packager \
 		--output-file='$(DIST)/$(PKG_PATH)$(CODE_COVERAGE_ARCHIVE_BASENAME).zip'
 endif
 ifeq (Darwin, $(OS_ARCH))
 ifdef MOZ_ASAN
-	@echo "Rewriting ASan runtime dylib paths for all binaries in $(DIST)/$(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH) ..."
-	$(PYTHON) $(MOZILLA_DIR)/build/unix/rewrite_asan_dylib.py $(DIST)/$(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)
+	@echo "Rewriting ASan runtime dylib paths for all binaries in $(DIST)/$(MOZ_PKG_DIR)$(_BINPATH) ..."
+	$(PYTHON) $(MOZILLA_DIR)/build/unix/rewrite_asan_dylib.py $(DIST)/$(MOZ_PKG_DIR)$(_BINPATH)
 endif # MOZ_ASAN
 endif # Darwin
 
 prepare-package: stage-package
 
 make-package-internal: prepare-package make-sourcestamp-file make-buildinfo-file make-mozinfo-file
 	@echo 'Compressing...'
 	cd $(DIST) && $(MAKE_PACKAGE)
--- a/toolkit/mozapps/installer/packager.py
+++ b/toolkit/mozapps/installer/packager.py
@@ -18,17 +18,16 @@ from mozpack.files import (
     FileFinder,
     File,
 )
 from mozpack.copier import (
     FileCopier,
     Jarrer,
 )
 from mozpack.errors import errors
-from mozpack.unify import UnifiedBuildFinder
 import mozpack.path as mozpath
 import buildconfig
 from argparse import ArgumentParser
 import os
 from StringIO import StringIO
 import subprocess
 import platform
 import mozinfo
@@ -263,18 +262,16 @@ def main():
     parser.add_argument('--js-binary',
                         help='Path to js binary. This is used to verify '
                         'minified JavaScript. If this is not defined, '
                         'minification verification will not be performed.')
     parser.add_argument('--jarlog', default='', help='File containing jar ' +
                         'access logs')
     parser.add_argument('--optimizejars', action='store_true', default=False,
                         help='Enable jar optimizations')
-    parser.add_argument('--unify', default='',
-                        help='Base directory of another build to unify with')
     parser.add_argument('--disable-compression', action='store_false',
                         dest='compress', default=True,
                         help='Disable jar compression')
     parser.add_argument('manifest', default=None, nargs='?',
                         help='Manifest file name')
     parser.add_argument('source', help='Source directory')
     parser.add_argument('destination', help='Destination directory')
     parser.add_argument('--non-resource', nargs='+', metavar='PATTERN',
@@ -311,50 +308,32 @@ def main():
         del defines['MOZ_OMNIJAR']
 
     respath = ''
     if 'RESPATH' in defines:
         respath = SimpleManifestSink.normalize_path(defines['RESPATH'])
     while respath.startswith('/'):
         respath = respath[1:]
 
-    if args.unify:
-        def is_native(path):
-            path = os.path.abspath(path)
-            return platform.machine() in mozpath.split(path)
-
-        # Invert args.unify and args.source if args.unify points to the
-        # native architecture.
-        args.source, args.unify = sorted([args.source, args.unify],
-                                         key=is_native, reverse=True)
-        if is_native(args.source) and not buildconfig.substs['CROSS_COMPILE']:
-            launcher.tooldir = args.source
-    elif not buildconfig.substs['CROSS_COMPILE']:
+    if not buildconfig.substs['CROSS_COMPILE']:
         launcher.tooldir = mozpath.join(buildconfig.topobjdir, 'dist')
 
     with errors.accumulate():
         finder_args = dict(
             minify=args.minify,
             minify_js=args.minify_js,
         )
         if args.js_binary:
             finder_args['minify_js_verify_command'] = [
                 args.js_binary,
                 os.path.join(os.path.abspath(os.path.dirname(__file__)),
                     'js-compare-ast.js')
             ]
-        if args.unify:
-            finder = UnifiedBuildFinder(FileFinder(args.source,
-                                                   find_executables=True),
-                                        FileFinder(args.unify,
-                                                   find_executables=True),
-                                        **finder_args)
-        else:
-            finder = FileFinder(args.source, find_executables=True,
-                                **finder_args)
+        finder = FileFinder(args.source, find_executables=True,
+                            **finder_args)
         if 'NO_PKG_FILES' in os.environ:
             sinkformatter = NoPkgFilesRemover(formatter,
                                               args.manifest is not None)
         else:
             sinkformatter = formatter
         sink = SimpleManifestSink(finder, sinkformatter)
         if args.manifest:
             preprocess_manifest(sink, args.manifest, defines)
--- a/toolkit/mozapps/installer/upload-files-APK.mk
+++ b/toolkit/mozapps/installer/upload-files-APK.mk
@@ -90,22 +90,22 @@ OMNIJAR_NAME := $(notdir $(OMNIJAR_NAME)
 PKG_SUFFIX = .apk
 
 INNER_FENNEC_PACKAGE = \
   $(MAKE) -C $(GECKO_APP_AP_PATH) gecko-nodeps.ap_ && \
   $(PYTHON) -m mozbuild.action.package_fennec_apk \
     --verbose \
     --inputs \
       $(GECKO_APP_AP_PATH)/gecko-nodeps.ap_ \
-    --omnijar $(STAGEPATH)$(MOZ_PKG_DIR)/$(OMNIJAR_NAME) \
+    --omnijar $(MOZ_PKG_DIR)/$(OMNIJAR_NAME) \
     --classes-dex $(GECKO_APP_AP_PATH)/classes.dex \
-    --lib-dirs $(STAGEPATH)$(MOZ_PKG_DIR)/lib \
-    --assets-dirs $(STAGEPATH)$(MOZ_PKG_DIR)/assets \
-    --features-dirs $(STAGEPATH)$(MOZ_PKG_DIR)/features \
-    --root-files $(foreach f,$(ROOT_FILES),$(STAGEPATH)$(MOZ_PKG_DIR)/$(f)) \
+    --lib-dirs $(MOZ_PKG_DIR)/lib \
+    --assets-dirs $(MOZ_PKG_DIR)/assets \
+    --features-dirs $(MOZ_PKG_DIR)/features \
+    --root-files $(foreach f,$(ROOT_FILES),$(MOZ_PKG_DIR)/$(f)) \
     --output $(PACKAGE:.apk=-unsigned-unaligned.apk) && \
   $(call RELEASE_SIGN_ANDROID_APK,$(PACKAGE:.apk=-unsigned-unaligned.apk),$(PACKAGE))
 
 # Packaging produces many optional artifacts.
 package_fennec = \
   $(INNER_FENNEC_PACKAGE) && \
   $(INNER_ROBOCOP_PACKAGE) && \
   $(INNER_INSTALL_BOUNCER_PACKAGE)
@@ -114,17 +114,17 @@ package_fennec = \
 # (re-)signing.
 repackage_fennec = \
   $(MAKE) -C $(GECKO_APP_AP_PATH) gecko-nodeps.ap_ && \
   $(PYTHON) -m mozbuild.action.package_fennec_apk \
     --verbose \
     --inputs \
       $(UNPACKAGE) \
       $(GECKO_APP_AP_PATH)/gecko-nodeps.ap_ \
-    --omnijar $(STAGEPATH)$(MOZ_PKG_DIR)/$(OMNIJAR_NAME) \
+    --omnijar $(MOZ_PKG_DIR)/$(OMNIJAR_NAME) \
     --output $(PACKAGE:.apk=-unsigned-unaligned.apk) && \
   $(call RELEASE_SIGN_ANDROID_APK,$(PACKAGE:.apk=-unsigned-unaligned.apk),$(PACKAGE))
 
 INNER_MAKE_PACKAGE = $(if $(UNPACKAGE),$(repackage_fennec),$(package_fennec))
 
 # Language repacks root the resources contained in assets/omni.ja
 # under assets/, but the repacks expect them to be rooted at /.
 # Therefore, we we move the omnijar back to / so the resources are
--- a/toolkit/mozapps/installer/upload-files.mk
+++ b/toolkit/mozapps/installer/upload-files.mk
@@ -36,19 +36,16 @@ ifndef _APPNAME
 endif
 ifndef _BINPATH
 _BINPATH = /$(_APPNAME)/Contents/MacOS
 endif # _BINPATH
 ifndef _RESPATH
 # Resource path for the precomplete file
 _RESPATH = /$(_APPNAME)/Contents/Resources
 endif
-ifdef UNIVERSAL_BINARY
-STAGEPATH = universal/
-endif
 endif
 
 PACKAGE_BASE_DIR = $(ABS_DIST)
 PACKAGE       = $(PKG_PATH)$(PKG_BASENAME)$(PKG_SUFFIX)
 
 # JavaScript Shell packaging
 JSSHELL_BINS  = \
   js$(BIN_SUFFIX) \
@@ -99,17 +96,17 @@ ifeq ($(MOZ_PKG_FORMAT),TGZ)
   PKG_SUFFIX	= .tar.gz
   INNER_MAKE_PACKAGE 	= $(CREATE_FINAL_TAR) - $(MOZ_PKG_DIR) | gzip -vf9 > $(PACKAGE)
   INNER_UNMAKE_PACKAGE	= gunzip -c $(UNPACKAGE) | $(UNPACK_TAR)
 endif
 
 ifeq ($(MOZ_PKG_FORMAT),BZ2)
   PKG_SUFFIX	= .tar.bz2
   ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
-    INNER_MAKE_PACKAGE 	= $(CREATE_FINAL_TAR) - -C $(STAGEPATH)$(MOZ_PKG_DIR) $(_APPNAME) | bzip2 -vf > $(PACKAGE)
+    INNER_MAKE_PACKAGE 	= $(CREATE_FINAL_TAR) - -C $(MOZ_PKG_DIR) $(_APPNAME) | bzip2 -vf > $(PACKAGE)
   else
     INNER_MAKE_PACKAGE 	= $(CREATE_FINAL_TAR) - $(MOZ_PKG_DIR) | bzip2 -vf > $(PACKAGE)
   endif
   INNER_UNMAKE_PACKAGE	= bunzip2 -c $(UNPACKAGE) | $(UNPACK_TAR)
 endif
 
 ifeq ($(MOZ_PKG_FORMAT),ZIP)
   ifdef MOZ_EXTERNAL_SIGNING_FORMAT
@@ -213,17 +210,17 @@ endif #Create an RPM file
 ifeq ($(MOZ_PKG_FORMAT),APK)
 include $(MOZILLA_DIR)/toolkit/mozapps/installer/upload-files-$(MOZ_PKG_FORMAT).mk
 endif
 
 ifeq ($(MOZ_PKG_FORMAT),DMG)
   PKG_SUFFIX	= .dmg
 
   _ABS_MOZSRCDIR = $(shell cd $(MOZILLA_DIR) && pwd)
-  PKG_DMG_SOURCE = $(STAGEPATH)$(MOZ_PKG_DIR)
+  PKG_DMG_SOURCE = $(MOZ_PKG_DIR)
   INNER_MAKE_PACKAGE	= $(call py_action,make_dmg,'$(PKG_DMG_SOURCE)' '$(PACKAGE)')
   INNER_UNMAKE_PACKAGE	= \
     set -ex; \
     rm -rf $(ABS_DIST)/unpack.tmp; \
     mkdir -p $(ABS_DIST)/unpack.tmp; \
     $(_ABS_MOZSRCDIR)/build/package/mac_osx/unpack-diskimage $(UNPACKAGE) /tmp/$(MOZ_PKG_APPNAME)-unpack $(ABS_DIST)/unpack.tmp; \
     rsync -a '$(ABS_DIST)/unpack.tmp/$(_APPNAME)' $(MOZ_PKG_DIR); \
     if test -n '$(MOZ_PKG_MAC_DSSTORE)' ; then \
@@ -256,29 +253,29 @@ endif
 
 # For final GPG / authenticode signing / dmg signing if required
 ifdef MOZ_EXTERNAL_SIGNING_FORMAT
   MOZ_SIGN_PACKAGE_CMD=$(MOZ_SIGN_CMD) $(foreach f,$(MOZ_EXTERNAL_SIGNING_FORMAT),-f $(f))
 endif
 
 ifdef MOZ_SIGN_PREPARED_PACKAGE_CMD
   ifeq (Darwin, $(OS_ARCH))
-    MAKE_PACKAGE    = $(or $(call MAKE_SIGN_EME_VOUCHER,$(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)/$(MOZ_CHILD_PROCESS_NAME).app/Contents/MacOS,$(STAGEPATH)$(MOZ_PKG_DIR)$(_RESPATH)),true) \
-                      && (cd $(STAGEPATH)$(MOZ_PKG_DIR)$(_RESPATH) && $(CREATE_PRECOMPLETE_CMD)) \
+    MAKE_PACKAGE    = $(or $(call MAKE_SIGN_EME_VOUCHER,$(MOZ_PKG_DIR)$(_BINPATH)/$(MOZ_CHILD_PROCESS_NAME).app/Contents/MacOS,$(MOZ_PKG_DIR)$(_RESPATH)),true) \
+                      && (cd $(MOZ_PKG_DIR)$(_RESPATH) && $(CREATE_PRECOMPLETE_CMD)) \
                       && cd ./$(PKG_DMG_SOURCE) && $(MOZ_SIGN_PREPARED_PACKAGE_CMD) $(MOZ_MACBUNDLE_NAME) \
                       && cd $(PACKAGE_BASE_DIR) && $(INNER_MAKE_PACKAGE)
   else
     MAKE_PACKAGE    = $(MOZ_SIGN_PREPARED_PACKAGE_CMD) $(MOZ_PKG_DIR) \
-                      && $(or $(call MAKE_SIGN_EME_VOUCHER,$(STAGEPATH)$(MOZ_PKG_DIR)),true) \
-                      && (cd $(STAGEPATH)$(MOZ_PKG_DIR)$(_RESPATH) && $(CREATE_PRECOMPLETE_CMD)) \
+                      && $(or $(call MAKE_SIGN_EME_VOUCHER,$(MOZ_PKG_DIR)),true) \
+                      && (cd $(MOZ_PKG_DIR)$(_RESPATH) && $(CREATE_PRECOMPLETE_CMD)) \
                       && $(INNER_MAKE_PACKAGE)
   endif #Darwin
 
 else
-  MAKE_PACKAGE    = (cd $(STAGEPATH)$(MOZ_PKG_DIR)$(_RESPATH) && $(CREATE_PRECOMPLETE_CMD)) && $(INNER_MAKE_PACKAGE)
+  MAKE_PACKAGE    = (cd $(MOZ_PKG_DIR)$(_RESPATH) && $(CREATE_PRECOMPLETE_CMD)) && $(INNER_MAKE_PACKAGE)
 endif
 
 ifdef MOZ_SIGN_PACKAGE_CMD
   MAKE_PACKAGE    += && $(MOZ_SIGN_PACKAGE_CMD) '$(PACKAGE)'
 endif
 
 NO_PKG_FILES += \
 	core \
--- a/tools/update-packaging/Makefile.in
+++ b/tools/update-packaging/Makefile.in
@@ -9,26 +9,18 @@ STANDALONE_MAKEFILE := 1
 # input location for the build, usually $(DIST)
 # set this to $(DIST)/l10n-stage per override for L10n builds
 PACKAGE_BASE_DIR	= $(DIST)
 
 # Default output location for update archive
 STAGE_DIR      = $(ABS_DIST)/$(PKG_UPDATE_PATH)
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
-ifdef UNIVERSAL_BINARY
-ifneq (,$(filter %/l10n-stage,$(PACKAGE_BASE_DIR)))
 PACKAGE_DIR	= $(PACKAGE_BASE_DIR)/$(MOZ_PKG_DIR)/$(MOZ_MACBUNDLE_NAME)
 else
-PACKAGE_DIR	= $(PACKAGE_BASE_DIR)/universal/$(MOZ_PKG_DIR)/$(MOZ_MACBUNDLE_NAME)
-endif
-else
-PACKAGE_DIR	= $(PACKAGE_BASE_DIR)/$(MOZ_PKG_DIR)/$(MOZ_MACBUNDLE_NAME)
-endif
-else
 PACKAGE_DIR	= $(PACKAGE_BASE_DIR)/$(MOZ_PKG_DIR)
 endif
 
 MAR_BIN	= $(DIST)/host/bin/mar$(HOST_BIN_SUFFIX)
 MBSDIFF_BIN	= $(DIST)/host/bin/mbsdiff$(HOST_BIN_SUFFIX)
 
 OVERRIDE_DEFAULT_GOAL := full-update
 full-update:: complete-patch