author | Mike Shal <mshal@mozilla.com> |
Fri, 10 Feb 2017 16:52:17 -0500 | |
changeset 392306 | d88a5673b1f7b5e3c8c37383e43c5c1b599ed89c |
parent 392305 | ea59e2cff30b8f5ed1f121b97d1417b51be152a5 |
child 392307 | 33d14dcc5ca1b9fff0ecfc0bd0a12e105f896156 |
push id | 1468 |
push user | asasaki@mozilla.com |
push date | Mon, 05 Jun 2017 19:31:07 +0000 |
treeherder | mozilla-release@0641fc6ee9d1 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | glandium |
bugs | 1339182 |
milestone | 54.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/Makefile.in +++ b/Makefile.in @@ -259,23 +259,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 @@ -185,11 +185,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 @@ -25,21 +25,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