xref: /aosp_15_r20/external/bazelbuild-rules_cc/cc/private/toolchain/osx_cc_wrapper.sh.tpl (revision eed53cd41c5909d05eedc7ad9720bb158fd93452)
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
30INSTALL_NAME_TOOL="/usr/bin/install_name_tool"
31
32LIBS=
33LIB_DIRS=
34RPATHS=
35OUTPUT=
36
37function parse_option() {
38    local -r opt="$1"
39    if [[ "${OUTPUT}" = "1" ]]; then
40        OUTPUT=$opt
41    elif [[ "$opt" =~ ^-l(.*)$ ]]; then
42        LIBS="${BASH_REMATCH[1]} $LIBS"
43    elif [[ "$opt" =~ ^-L(.*)$ ]]; then
44        LIB_DIRS="${BASH_REMATCH[1]} $LIB_DIRS"
45    elif [[ "$opt" =~ ^-Wl,-rpath,\@loader_path/(.*)$ ]]; then
46        RPATHS="${BASH_REMATCH[1]} ${RPATHS}"
47    elif [[ "$opt" = "-o" ]]; then
48        # output is coming
49        OUTPUT=1
50    fi
51}
52
53# let parse the option list
54for i in "$@"; do
55    if [[ "$i" = @* ]]; then
56        while IFS= read -r opt
57        do
58            parse_option "$opt"
59        done < "${i:1}" || exit 1
60    else
61        parse_option "$i"
62    fi
63done
64
65# Set-up the environment
66%{env}
67
68# Call the C++ compiler
69%{cc} "$@"
70
71function get_library_path() {
72    for libdir in ${LIB_DIRS}; do
73        if [ -f ${libdir}/lib$1.so ]; then
74            echo "${libdir}/lib$1.so"
75        elif [ -f ${libdir}/lib$1.dylib ]; then
76            echo "${libdir}/lib$1.dylib"
77        fi
78    done
79}
80
81# A convenient method to return the actual path even for non symlinks
82# and multi-level symlinks.
83function get_realpath() {
84    local previous="$1"
85    local next=$(readlink "${previous}")
86    while [ -n "${next}" ]; do
87        previous="${next}"
88        next=$(readlink "${previous}")
89    done
90    echo "${previous}"
91}
92
93# Get the path of a lib inside a tool
94function get_otool_path() {
95    # the lib path is the path of the original lib relative to the workspace
96    get_realpath $1 | sed 's|^.*/bazel-out/|bazel-out/|'
97}
98
99# Do replacements in the output
100for rpath in ${RPATHS}; do
101    for lib in ${LIBS}; do
102        unset libname
103        if [ -f "$(dirname ${OUTPUT})/${rpath}/lib${lib}.so" ]; then
104            libname="lib${lib}.so"
105        elif [ -f "$(dirname ${OUTPUT})/${rpath}/lib${lib}.dylib" ]; then
106            libname="lib${lib}.dylib"
107        fi
108        # ${libname-} --> return $libname if defined, or undefined otherwise. This is to make
109        # this set -e friendly
110        if [[ -n "${libname-}" ]]; then
111            libpath=$(get_library_path ${lib})
112            if [ -n "${libpath}" ]; then
113                ${INSTALL_NAME_TOOL} -change $(get_otool_path "${libpath}") \
114                    "@loader_path/${rpath}/${libname}" "${OUTPUT}"
115            fi
116        fi
117    done
118done
119
120