#!/bin/bash
#
# Copyright 2016 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# libtool.sh runs the command passed to it using "xcrunwrapper libtool".
#
# It creates symbolic links for all input files with a path-hash appended
# to their original name (foo.o becomes foo_{md5sum}.o). This is to circumvent
# a bug in the original libtool that arises when two input files have the same
# base name (even if they are in different directories).

set -eu

# A trick to allow invoking this script in multiple contexts.
if [ -z ${MY_LOCATION+x} ]; then
  if [ -d "$0.runfiles/" ]; then
    MY_LOCATION="$0.runfiles/bazel_tools/tools/objc"
  else
    MY_LOCATION="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  fi
fi

WRAPPER="${MY_LOCATION}/xcrunwrapper.sh"

TEMPDIR="$(mktemp -d "${TMPDIR:-/tmp}/libtool.XXXXXXXX")"
trap "rm -rf \"${TEMPDIR}\"" EXIT

# 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 -qs "${ORIGINAL_NAME}")"
  SYMLINK_NAME="${TEMPDIR}/$(basename "${ORIGINAL_NAME%.o}_${ORIGINAL_HASH}.o")"
  if [[ ! -e "$SYMLINK_NAME" ]]; then
    case "${ORIGINAL_NAME}" in
      /*) ln -sf "$ORIGINAL_NAME" "$SYMLINK_NAME" ;;
      *) ln -sf "$(pwd)/$ORIGINAL_NAME" "$SYMLINK_NAME" ;;
    esac
  fi
  echo "$SYMLINK_NAME"
}

ARGS=()

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.
  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"
      rm -f "${HASHED_FILELIST}"
      # Use python helper script for fast md5 calculation of many strings.
      python "${MY_LOCATION}/make_hashed_objlist.py" \
        "${ARG}" "${HASHED_FILELIST}" "${TEMPDIR}"
      ARGS+=("${HASHED_FILELIST}")
      ;;
   # Output flag
    -o)
     ARGS+=("${ARG}")
     ARG="$1"
     shift
     ARGS+=("${ARG}")
     OUTPUTFILE="${ARG}"
     ;;
   # Flags with no args
    -static|-s|-a|-c|-L|-T|-no_warning_for_no_symbols)
      ARGS+=("${ARG}")
      ;;
   # Single-arg flags
   -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
     ;;
   # Archive inputs can remain untouched, as they come from other targets.
   *.a)
     ARGS+=("${ARG}")
     ;;
   # Remaining args are input objects
   *)
     ARGS+=("$(echo "$(hash_objfile "${ARG}")")")
     ;;
   esac
done

# Ensure 0 timestamping for hermetic results.
export ZERO_AR_DATE=1

"${WRAPPER}" libtool "${ARGS[@]}"

# Prevents a pre-Xcode-8 bug in which passing zero-date archive files to ld
# would cause ld to error.
touch "$OUTPUTFILE"
