Skip to content

Commit

Permalink
Workaround for duplicate input basename bug in apple's libtool.
Browse files Browse the repository at this point in the history
Creates a symlink for each input file to libtool, with a md5 hash suffix, and passes these symlinks instead.

--
MOS_MIGRATED_REVID=121279266
  • Loading branch information
c-parsons authored and damienmg committed May 3, 2016
1 parent b8e1755 commit 910a907
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 9 deletions.
76 changes: 71 additions & 5 deletions src/tools/xcode/xcrunwrapper/xcrunwrapper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,29 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
# xcrunwrapper runs the command passed to it using xcrun.
# It replaces __BAZEL_XCODE_DEVELOPER_DIR__ with $DEVELOPER_DIR (or reasonable
# default) and __BAZEL_XCODE_SDKROOT__ with a valid path based on SDKROOT (or
# reasonable default).
# xcrunwrapper runs the command passed to it using xcrun. The first arg
# passed is the name of the tool to be invoked via xcrun. (For example, libtool
# or clang).
# xcrunwrapper replaces __BAZEL_XCODE_DEVELOPER_DIR__ with $DEVELOPER_DIR (or
# reasonable default) and __BAZEL_XCODE_SDKROOT__ with a valid path based on
# SDKROOT (or reasonable default).
# These values (__BAZEL_XCODE_*) are a shared secret withIosSdkCommands.java.

set -eu

TOOLNAME=$1
shift

# Creates a symbolic link to the input argument file and returns the symlink
# file path.
function hash_objfile() {
ORIGINAL_NAME="$1"
ORIGINAL_HASH="$(/sbin/md5 -q "${ORIGINAL_NAME}")"
SYMLINK_NAME="${ORIGINAL_NAME//.o/_${ORIGINAL_HASH}.o}"
ln -s "$(basename "$ORIGINAL_NAME")" "$SYMLINK_NAME"
echo "$SYMLINK_NAME"
}

# Pick values for DEVELOPER_DIR and SDKROOT as appropriate (if they weren't set)

WRAPPER_DEVDIR="${DEVELOPER_DIR:-}"
Expand All @@ -47,8 +62,59 @@ if [[ -z "${WRAPPER_SDKROOT:-}" ]] ; then
WRAPPER_SDKROOT="$(/usr/bin/xcrun --show-sdk-path --sdk ${WRAPPER_SDK})"
fi

ARGS=("$TOOLNAME")
while [[ $# -gt 0 ]]; do
ARG="$1"
shift

# Libtool artifact symlinking. Apple's libtool has a bug when there are two
# input files with the same basename. We thus create symlinks that are named
# with a hash suffix for each input, and pass them to libtool.
# See b/28186497.
# TODO(b/28347228): Handle this in a separate wrapper.
if [ "$TOOLNAME" = "libtool" ] ; then
case "${ARG}" in
# Filelist flag, need to symlink each input in the contents of file and
# pass a new filelist which contains the symlinks.
-filelist)
ARGS+=("${ARG}")
ARG="$1"
shift
HASHED_FILELIST="${ARG//.objlist/_hashes.objlist}"
while read INPUT_FILE || [ -n "$INPUT_FILE" ]; do
echo "$(hash_objfile "${INPUT_FILE}")" >> "$HASHED_FILELIST"
done < "${ARG}"
ARGS+=("${HASHED_FILELIST}")
;;
# Flags with no args
-static|-s|-a|-c|-L|-T|-no_warning_for_no_symbols)
ARGS+=("${ARG}")
;;
# Single-arg flags
-o|-arch_only|-syslibroot)
ARGS+=("${ARG}")
ARG="$1"
shift
ARGS+=("${ARG}")
;;
# Any remaining flags are unexpected and may ruin flag parsing.
-*)
echo "Unrecognized libtool flag ${ARG}"
exit 1
;;
# Remaining args are input objects
*)
ARGS+=("$(echo "$(hash_objfile "${ARG}")")")
;;
esac
else
ARGS+=("${ARG}")
fi
done

# Subsitute toolkit path placeholders.
UPDATEDARGS=()
for ARG in "$@" ; do
for ARG in "${ARGS[@]}" ; do
ARG="${ARG//__BAZEL_XCODE_DEVELOPER_DIR__/${WRAPPER_DEVDIR}}"
ARG="${ARG//__BAZEL_XCODE_SDKROOT__/${WRAPPER_SDKROOT}}"
UPDATEDARGS+=("${ARG}")
Expand Down
34 changes: 30 additions & 4 deletions tools/objc/j2objc_dead_code_pruner.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import multiprocessing
import os
import Queue
import re
import shutil
import subprocess
import threading
Expand Down Expand Up @@ -295,10 +296,34 @@ def PruneSourceFiles(input_files, output_files, dependency_mapping_files,
file_shutil)


def MatchObjectNamesInArchive(xcrunwrapper, archive, object_names):
"""Returns object names matching their identity in an archive file.
The linker that blaze uses appends an md5 hash to object file
names prior to inclusion in the archive file. Thus, object names
such as 'foo.o' need to be matched to their appropriate name in
the archive file, such as 'foo_<hash>.o'.
Args:
xcrunwrapper: A wrapper script over xcrun.
archive: The location of the archive file.
object_names: The expected basenames of object files to match,
sans extension. For example 'foo' (not 'foo.o').
Returns:
A list of basenames of matching members of the given archive
"""
ar_contents_cmd = '%s ar -t %s' % (xcrunwrapper, archive)
real_object_names = subprocess.check_output(ar_contents_cmd, shell=True)
expected_object_name_regex = '^(?:%s)_[0-9a-f]{32}.o' % (
'|'.join([re.escape(name) for name in object_names]))
return re.findall(expected_object_name_regex, real_object_names,
flags=re.MULTILINE)


def PruneArchiveFile(input_archive, output_archive, dummy_archive,
dependency_mapping_files, header_mapping_files,
archive_source_mapping_files, entry_classes, xcrunwrapper,
file_open=open, proc_exe=subprocess.check_call):
file_open=open):
"""Remove unreachable objects from archive file.
Args:
Expand All @@ -315,7 +340,6 @@ def PruneArchiveFile(input_archive, output_archive, dummy_archive,
xcrunwrapper: A wrapper script over xcrun.
file_open: Reference to the builtin open function so it may be
overridden for testing.
proc_exe: Object that can execute a command line process.
"""
reachability_file_mapping = BuildReachabilityTree(
dependency_mapping_files, file_open)
Expand All @@ -335,7 +359,7 @@ def PruneArchiveFile(input_archive, output_archive, dummy_archive,
for source_file in source_files:
if os.path.splitext(source_file)[0] not in reachable_files_set:
unreachable_object_names.append(
os.path.basename(os.path.splitext(source_file)[0]) + '.o')
os.path.basename(os.path.splitext(source_file)[0]))

# There are unreachable objects in the archive to prune
if unreachable_object_names:
Expand All @@ -350,6 +374,8 @@ def PruneArchiveFile(input_archive, output_archive, dummy_archive,
# Make the output archive editable
j2objc_cmd += 'chmod +w %s;' % (output_archive)
# Remove the unreachable objects from the archive
unreachable_object_names = MatchObjectNamesInArchive(
xcrunwrapper, input_archive, unreachable_object_names)
j2objc_cmd += '%s ar -d -s %s %s;' % (
xcrunwrapper, output_archive, ' '.join(unreachable_object_names))
# Update the table of content of the archive file
Expand All @@ -362,7 +388,7 @@ def PruneArchiveFile(input_archive, output_archive, dummy_archive,
else:
j2objc_cmd = 'cp %s %s' % (input_archive, output_archive)

proc_exe(j2objc_cmd, stderr=subprocess.STDOUT, shell=True)
subprocess.check_output(j2objc_cmd, stderr=subprocess.STDOUT, shell=True)


if __name__ == '__main__':
Expand Down

0 comments on commit 910a907

Please sign in to comment.