1#!/bin/bash 2# 3# Copyright 2015 The Bazel Authors. All rights reserved. 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17# OS X relpath is not really working. This is a wrapper script around gcc 18# to simulate relpath behavior. 19# 20# This wrapper uses install_name_tool to replace all paths in the binary 21# (bazel-out/.../path/to/original/library.so) by the paths relative to 22# the binary. It parses the command line to behave as rpath is supposed 23# to work. 24# 25# See https://blogs.oracle.com/dipol/entry/dynamic_libraries_rpath_and_mac 26# on how to set those paths for Mach-O binaries. 27# 28set -eu 29 30LIBS= 31LIB_PATHS= 32LIB_DIRS= 33RPATHS= 34OUTPUT= 35 36function parse_option() { 37 local -r opt="$1" 38 if [[ "${OUTPUT}" = "1" ]]; then 39 OUTPUT=$opt 40 elif [[ "$opt" =~ ^-l(.*)$ ]]; then 41 LIBS="${BASH_REMATCH[1]} $LIBS" 42 elif [[ "$opt" =~ ^(.*)\.so$ ]]; then 43 LIB_PATHS="${opt} $LIB_PATHS" 44 elif [[ "$opt" =~ ^(.*)\.dylib$ ]]; then 45 LIB_PATHS="${opt} $LIB_PATHS" 46 elif [[ "$opt" =~ ^-L(.*)$ ]]; then 47 LIB_DIRS="${BASH_REMATCH[1]} $LIB_DIRS" 48 elif [[ "$opt" =~ ^\@loader_path/(.*)$ ]]; then 49 RPATHS="${BASH_REMATCH[1]} ${RPATHS}" 50 elif [[ "$opt" = "-o" ]]; then 51 # output is coming 52 OUTPUT=1 53 fi 54} 55 56# let parse the option list 57for i in "$@"; do 58 if [[ "$i" = @* && -r "${i:1}" ]]; then 59 while IFS= read -r opt 60 do 61 parse_option "$opt" 62 done < "${i:1}" || exit 1 63 else 64 parse_option "$i" 65 fi 66done 67 68# Set-up the environment 69%{env} 70 71# Call the C++ compiler 72%{cc} "$@" 73 74# Generate an empty file if header processing succeeded. 75if [[ "${OUTPUT}" == *.h.processed ]]; then 76 echo -n > "${OUTPUT}" 77fi 78 79function get_library_path() { 80 for libdir in ${LIB_DIRS}; do 81 if [ -f ${libdir}/lib$1.so ]; then 82 echo "${libdir}/lib$1.so" 83 elif [ -f ${libdir}/lib$1.dylib ]; then 84 echo "${libdir}/lib$1.dylib" 85 fi 86 done 87} 88 89# A convenient method to return the actual path even for non symlinks 90# and multi-level symlinks. 91function get_realpath() { 92 local previous="$1" 93 local next=$(readlink "${previous}") 94 while [ -n "${next}" ]; do 95 previous="${next}" 96 next=$(readlink "${previous}") 97 done 98 echo "${previous}" 99} 100 101# Get the path of a lib inside a tool 102function get_otool_path() { 103 # the lib path is the path of the original lib relative to the workspace 104 get_realpath $1 | sed 's|^.*/bazel-out/|bazel-out/|' 105} 106 107function call_install_name() { 108 /usr/bin/xcrun install_name_tool -change $(get_otool_path "$1") \ 109 "@loader_path/$2/$3" "${OUTPUT}" 110} 111 112# Do replacements in the output 113for rpath in ${RPATHS}; do 114 for lib in ${LIBS}; do 115 unset libname 116 if [ -f "$(dirname ${OUTPUT})/${rpath}/lib${lib}.so" ]; then 117 libname="lib${lib}.so" 118 elif [ -f "$(dirname ${OUTPUT})/${rpath}/lib${lib}.dylib" ]; then 119 libname="lib${lib}.dylib" 120 fi 121 # ${libname-} --> return $libname if defined, or undefined otherwise. This is to make 122 # this set -e friendly 123 if [[ -n "${libname-}" ]]; then 124 libpath=$(get_library_path ${lib}) 125 if [ -n "${libpath}" ]; then 126 call_install_name "${libpath}" "${rpath}" "${libname}" 127 fi 128 fi 129 done 130 for libpath in ${LIB_PATHS}; do 131 if [ -f "$libpath" ]; then 132 libname=$(basename "$libpath") 133 if [ -f "$(dirname ${OUTPUT})/${rpath}/${libname}" ]; then 134 call_install_name "${libpath}" "${rpath}" "${libname}" 135 fi 136 fi 137 done 138done 139