summaryrefslogtreecommitdiffstats
path: root/packaging/macos/jhb/usr/src/bash_d/lib.sh
blob: c1f385e1516978939ea436281048b6b152c32ebf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# SPDX-FileCopyrightText: 2021 René de Hesselle <dehesselle@web.de>
#
# SPDX-License-Identifier: GPL-2.0-or-later

### description ################################################################

# Provide convenience wrappers for install_name_tool.

### shellcheck #################################################################

# shellcheck shell=bash # no shebang as this file is intended to be sourced

### dependencies ###############################################################

assert_darwin
bash_d_include echo
bash_d_include readlinkf

### variables ##################################################################

LIB_RESET_ID=keep   # options: basename, canonical, keep

### functions ##################################################################

function lib_change_path
{
  # Compared to install_name_tool, this function
  #   - requires less arguments as 'source' can be deducted from 'target'
  #   - can apply the requested changes to multiple binaries at once

  local target=$1         # new path to dynamically linked library
  local binaries=${*:2}   # binaries to modify

  local source_lib=${target##*/}   # get library filename from target location

  for binary in $binaries; do   # won't work if spaces in paths
    if [[ $binary == *.so ]] ||
       [[ $binary == *.dylib ]] ||
       [ $(file $binary | grep "shared library" | wc -l) -eq 1 ]; then
      lib_reset_id $binary
    fi

    local source=$(otool -L $binary | grep "$source_lib " | awk '{ print $1 }')
    if [ -z $source ]; then
      echo_w "no $source_lib in $binary"
    else
      # Reconstructing 'target' as it might have been specified as regex.
      target=$(dirname $target)/$(basename $source)

      install_name_tool -change $source $target $binary
    fi
  done
}

function lib_change_paths
{
  # This is a wrapper ontop lib_change_path: given a directory 'lib_dir' that
  # contains the libraries, all (matching) libraries linked in 'binary' can be
  # changed at once to a specified 'target' path.

  local target=$1         # new path to dynamically linked library
  local lib_dir=$2
  local binaries=${*:3}

  for binary in $binaries; do
    for linked_lib in $(otool -L $binary | tail -n +2 | awk '{ print $1 }'); do
      if [ "$(basename $binary)" != "$(basename $linked_lib)" ] &&
         [ -f $lib_dir/$(basename $linked_lib) ]; then
        lib_change_path $target/$(basename $linked_lib) $binary
      fi
    done
  done
}

function lib_change_siblings
{
  # This is a wrapper ontop lib_change_path: all libraries inside a given
  # 'lib_dir' that are linked to libraries located in that same 'lib_dir' can
  # be automatically adjusted.

  local lib_dir=$1

  for lib in $lib_dir/*.dylib; do
    lib_reset_id $lib
    for linked_lib in $(otool -L $lib | tail -n +2 | awk '{ print $1 }'); do
      if [ "$(basename $lib)" != "$(basename $linked_lib)" ] &&
         [ -f $lib_dir/$(basename $linked_lib) ]; then
        lib_change_path @loader_path/$(basename $linked_lib) $lib
      fi
    done
  done
}

function lib_reset_id
{
  local lib=$1

  case "$LIB_RESET_ID" in
    basename)
      install_name_tool -id $(basename $lib) $lib
      ;;
    canonical)
      install_name_tool -id $(readlinkf $lib) $lib
      ;;
    keep)
      : # don't do anything
      ;;
    *)
      echo_e "invalid value for LIB_RESET_ID: $LIB_RESET_ID"
      ;;
  esac
}

function lib_add_rpath
{
  local rpath=$1
  local binary=$2

  install_name_tool -add_rpath "$rpath" "$binary"
}

function lib_clear_rpath
{
  local binary=$1

  for rpath in $(otool -l $binary | grep -A2 LC_RPATH | grep -E "^[ ]+path" | awk '{ print $2 }'); do
    install_name_tool -delete_rpath $rpath $binary
  done
}

function lib_replace_path
{
  local source=$1
  local target=$2
  local binary=$3

  for lib in $(lib_get_linked $binary); do
    if [[ $lib =~ $source ]]; then
      lib_change_path @rpath/$(basename $lib) $binary
    fi
  done
}

function lib_get_linked
{
  local binary=$1   # can be executable or library

  #echo_d "binary: $binary"

  local filter   # we need to distinguish between executable and library

  local file_type
  file_type=$(file "$binary")
  if   [[ $file_type = *"shared library"* ]]; then
    filter="-v $(otool -D "$binary" | tail -n 1)"  # exclude library id
  elif [[ $file_type = *"executable"* ]]; then
    filter="-E [.]+"                               # include everything
  else
    echo_w "neither shared library nor executable: $binary"
    return 1
  fi

  # since we're not echoing this, output will be newline-separated
  # shellcheck disable=SC2086 # need word splitting for arguments
  otool -L "$binary" | grep " " | grep $filter | awk '{ print $1 }'
}

### aliases ####################################################################

# Nothing here.

### main #######################################################################

# Nothing here.