• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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