summaryrefslogtreecommitdiffstats
path: root/packaging/macos/jhb/usr
diff options
context:
space:
mode:
Diffstat (limited to 'packaging/macos/jhb/usr')
-rwxr-xr-xpackaging/macos/jhb/usr/bin/archive205
-rwxr-xr-xpackaging/macos/jhb/usr/bin/bootstrap135
-rwxr-xr-xpackaging/macos/jhb/usr/bin/jhb63
-rwxr-xr-xpackaging/macos/jhb/usr/bin/run-parts73
-rw-r--r--packaging/macos/jhb/usr/src/bash_d/README.md9
-rw-r--r--packaging/macos/jhb/usr/src/bash_d/ansi.sh76
-rw-r--r--packaging/macos/jhb/usr/src/bash_d/assert.sh42
-rw-r--r--packaging/macos/jhb/usr/src/bash_d/bash_d.sh98
-rw-r--r--packaging/macos/jhb/usr/src/bash_d/echo.sh52
-rw-r--r--packaging/macos/jhb/usr/src/bash_d/error.sh45
-rw-r--r--packaging/macos/jhb/usr/src/bash_d/lib.sh174
-rw-r--r--packaging/macos/jhb/usr/src/bash_d/readlinkf.sh27
12 files changed, 999 insertions, 0 deletions
diff --git a/packaging/macos/jhb/usr/bin/archive b/packaging/macos/jhb/usr/bin/archive
new file mode 100755
index 0000000..f84f0c8
--- /dev/null
+++ b/packaging/macos/jhb/usr/bin/archive
@@ -0,0 +1,205 @@
+#!/usr/bin/env bash
+#
+# SPDX-FileCopyrightText: 2021 René de Hesselle <dehesselle@web.de>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+### description ################################################################
+
+# This is a helper to create release archives and mount them.
+
+### shellcheck #################################################################
+
+# Nothing here.
+
+### dependencies ###############################################################
+
+#------------------------------------------------------ source jhb configuration
+
+source "$(dirname "${BASH_SOURCE[0]}")"/../../etc/jhb.conf.sh
+
+#------------------------------------------- source common functions from bash_d
+
+# bash_d is already available (it's part of jhb configuration)
+
+bash_d_include error
+
+### variables ##################################################################
+
+# Nothing here.
+
+### functions ##################################################################
+
+function create_tar
+{
+ local file=$ARTIFACT_DIR/$RELEASE_ARCHIVE
+
+ echo_i "creating $file"
+
+ tar -C "$WRK_DIR" -cp "$(basename "$VER_DIR")" |
+ XZ_OPT=-T0 "$BIN_DIR"/xz > "$file"
+
+ shasum -a 256 "$file" > "$file".sha256
+ cat "$file".sha256
+}
+
+function create_dmg
+{
+ local file=$ARTIFACT_DIR/$RELEASE_ARCHIVE
+
+ ( # create dmg
+ local vol_name
+ vol_name=$(basename "$VER_DIR")
+
+ cd "$WRK_DIR" || exit 1
+ hdiutil create -fs HFS+ -ov -format UDBZ \
+ -srcfolder "$vol_name" \
+ -volname "$vol_name" \
+ "$file"
+ )
+
+ ( # create and print checksum
+ cd "$(dirname "$file")" || exit 1
+ shasum -a 256 "$(basename "$file")" > "$file".sha256
+ cat "$file".sha256
+ )
+}
+
+function mount_dmg
+{
+ local file=$REP_DIR/$RELEASE_ARCHIVE
+ local mountpoint=$VER_DIR
+
+ if [ ! -d "$mountpoint" ]; then
+ mkdir -p "$mountpoint"
+ fi
+
+ echo_i "mounting $(basename "$file") may take some time"
+ local device
+ device=$(hdiutil attach -nomount "$file" | grep "^/dev/disk" |
+ grep "Apple_HFS" | awk '{ print $1 }')
+ echo_i "$file attached to $device"
+ mount -o nobrowse,noowners,noquarantine,ro -t hfs "$device" "$mountpoint"
+ echo_i "$device mounted to $mountpoint"
+}
+
+function unmount_dmg
+{
+ local mountpoint=$VER_DIR
+
+ while : ; do # unmount everything (in reverse order)
+ local disk
+ disk=$(mount | grep "$mountpoint" | tail -n1 | awk '{ print $1 }')
+ disk=${disk%s[0-9]} # cut off slice specification
+
+ if [ ${#disk} -eq 0 ]; then
+ break # nothing to do here
+ else
+ # We loop over the 'eject' since it occasionally timeouts.
+ while ! diskutil eject "$disk" > /dev/null; do
+ echo_w "retrying to eject $disk in 5 seconds"
+ sleep 5
+ done
+
+ echo_i "ejected $disk"
+ fi
+ done
+}
+
+function download_dmg
+{
+ if [ ! -d "$REP_DIR" ]; then
+ mkdir -p "$REP_DIR"
+ fi
+
+ for url in "${RELEASE_URLS[@]}"; do
+ local partial_download=$REP_DIR/${FUNCNAME[0]} # TMP_DIR not available yet
+ # download (at least) 100 kb of data
+ curl -L "$url" 2>/dev/null | head -c 100000 > "$partial_download"
+ # if we got 100 kb, it's not a "404 file not found"
+ if [ "$(stat -f%z "$partial_download")" -eq 100000 ]; then
+ echo_i "downloading $url"
+ curl -o "$REP_DIR/$RELEASE_ARCHIVE" -L -C - "$url"
+ break
+ fi
+ done
+
+ rm "$partial_download"
+}
+
+function install_dmg
+{
+ local overlay_size=${1:-1} # unit GiB, default 1
+
+ local file=$REP_DIR/$RELEASE_ARCHIVE
+
+ # download and mount read-only disk image
+ if [ -f "$file" ]; then
+ echo_i "using $file"
+ else
+ download_dmg
+ fi
+ mount_dmg
+
+ # create writable overlay
+ local device
+ device=$(hdiutil attach -nomount ram://$((overlay_size * 1024 * 2048)) | xargs)
+ newfs_hfs -v "$RELEASE_OVERLAY" "$device" >/dev/null
+ echo_i "$overlay_size GiB ram attached to $device"
+ mount -o nobrowse,rw,union -t hfs "$device" "$VER_DIR"
+ echo_i "$device union-mounted at $VER_DIR"
+
+ # Sadly, there are some limitations involved with union-mounting:
+ # - Files are not visible to macOS' versions of 'ls' or 'find'.
+ # (The GNU versions do work though.)
+ # - You cannot write in a location without having written to its
+ # parent location first. That's why we need to pre-create all directories
+ # below.
+ #
+ # (Shadow-mounting a dmg is not a feasible alternative due to its
+ # bad write-performance.)
+
+ # Create and run a script for mass-creating directories.
+ echo_i "setting up directories in overlay"
+ gfind "$VER_DIR" -mindepth 1 -type d \
+ ! -path "$BLD_DIR/*" ! -path "$SRC_DIR/*" \
+ -exec echo "mkdir {}" \; > "$VER_DIR"/create_dirs.sh
+ chmod 755 "$VER_DIR"/create_dirs.sh
+ "$VER_DIR"/create_dirs.sh
+ rm "$VER_DIR"/create_dirs.sh
+}
+
+function uninstall_dmg
+{
+ unmount_dmg
+}
+
+function main
+{
+ local command=$1
+ local option=$2
+
+ case "$command" in
+ create_dmg)
+ create_dmg
+ ;;
+ create_tar)
+ create_tar
+ ;;
+ install_dmg)
+ install_dmg "$option" # option: overlay size in GiB, defaul is 1
+ ;;
+ uninstall_dmg)
+ uninstall_dmg
+ ;;
+ *)
+ echo_e "usage: $0 {create_dmg|create_tar|install_dmg|uninstall_dmg}"
+ ;;
+ esac
+}
+
+### main #######################################################################
+
+error_trace_enable
+
+main "$@"
diff --git a/packaging/macos/jhb/usr/bin/bootstrap b/packaging/macos/jhb/usr/bin/bootstrap
new file mode 100755
index 0000000..3d46dbb
--- /dev/null
+++ b/packaging/macos/jhb/usr/bin/bootstrap
@@ -0,0 +1,135 @@
+#!/usr/bin/env bash
+#
+# SPDX-FileCopyrightText: 2021 René de Hesselle <dehesselle@web.de>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+### description ################################################################
+
+# This script bootstraps JHBuild so it's ready to build any module.
+
+### shellcheck #################################################################
+
+# Nothing here.
+
+### dependencies ###############################################################
+
+#------------------------------------------------------ source jhb configuration
+
+# You can install a custom configuration file by passing it as first
+# argument. It has to be named '*.conf.sh'.
+
+source "$(dirname "${BASH_SOURCE[0]}")"/../../etc/jhb.conf.sh "$1"
+
+#------------------------------------------- source common functions from bash_d
+
+# bash_d is already available (it's part of jhb configuration)
+
+bash_d_include error
+
+### variables ##################################################################
+
+SELF_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1; pwd)
+
+FORCE_BUILD_FROM_SOURCE=${FORCE_BUILD_FROM_SOURCE:-false}
+
+### functions ##################################################################
+
+function is_release_usable
+{
+ local url=$1
+
+ local partial_download="$TMP_DIR/${FUNCNAME[0]}".tar.xz
+ local rc=1
+
+ # download at least 100 kb of data
+ curl -L "$url" 2>/dev/null | head -c 100000 > "$partial_download"
+ # if we got 100 kb, it's not a "404 file not found"
+ if [ "$(stat -f%z "$partial_download")" -eq 100000 ]; then
+ # look inside: dir needs to match our VER_DIR to be usable
+ local dir
+ dir=$(basename "$(tar -tvJf "$partial_download" 2>/dev/null |
+ head -n 1 | awk '{ print $NF }')")
+ if [ "$dir" = "$(basename "$VER_DIR")" ]; then
+ rc=0
+ fi
+ fi
+
+ rm "$partial_download"
+
+ return $rc
+}
+
+### main #######################################################################
+
+error_trace_enable
+
+#-------------------------------------------------------- print main directories
+
+echo_i "WRK_DIR = $WRK_DIR"
+echo_i "VER_DIR = $VER_DIR"
+
+#--------------------------------------------------------- perform system checks
+
+if sys_wrkdir_is_usable &&
+ sdkroot_exists &&
+ sys_usrlocal_is_clean; then
+
+ # these checks may issue warnings but have have no consequences otherwise
+ sys_macos_is_recommended || true
+ sys_sdk_is_recommended || true
+else
+ exit 1 # cannot continue
+fi
+
+#--------------------------------------------------- initialize directory layout
+
+if [ "$SELF_DIR" = "$USR_DIR"/bin ]; then
+ : # we are already running inside target directory layout
+else
+ # sync repository into target structure, remove everything git-related
+ rsync -a "$SELF_DIR"/../../../jhb/ "$VER_DIR"/
+ find "$VER_DIR" -type f -name ".gitignore" -delete
+ rm -rf "$VER_DIR"/.git
+ rm "$VER_DIR"/.gitmodules
+fi
+
+#------------------------------------------- check if binary release can be used
+
+for URL in "${RELEASE_URLS[@]}"; do
+ if is_release_usable "$URL" && ! $FORCE_BUILD_FROM_SOURCE; then
+ echo_i "using $URL"
+ curl -L "$URL" | tar -C "$WRK_DIR" -xJ
+ exit $? # we can quit here and now, nothing further to do
+ fi
+done
+
+echo_i "building everything from scratch"
+
+#---------------------------------------------------------------- install ccache
+
+ccache_install
+ccache_configure
+
+#------------------------------------------ log relevant versions to release.log
+
+sys_create_log
+
+#------------------------------------------------- install and configure JHBuild
+
+jhbuild_install
+jhbuild_configure
+
+#------------------------------------------------------- run bootstrap procedure
+
+jhbuild bootstrap-gtk-osx
+
+#--------------------------------------------------------- install GNU utilities
+
+# GNU versions of various utilites make life significantly easier on macOS.
+
+jhbuild build coreutils findutils sed tar
+
+#-------------------------------------------------------------- install dmgbuild
+
+dmgbuild_install
diff --git a/packaging/macos/jhb/usr/bin/jhb b/packaging/macos/jhb/usr/bin/jhb
new file mode 100755
index 0000000..b268339
--- /dev/null
+++ b/packaging/macos/jhb/usr/bin/jhb
@@ -0,0 +1,63 @@
+#!/usr/bin/env bash
+#
+# SPDX-FileCopyrightText: 2021 René de Hesselle <dehesselle@web.de>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+### description ################################################################
+
+# This script is a wrapper around the jhbuild binary to run it in our
+# configured environment (etc/jhb.conf).
+
+### shellcheck #################################################################
+
+# Nothing here.
+
+### dependencies ###############################################################
+
+#---------------------------------------------------------- source configuration
+
+source "$(dirname "${BASH_SOURCE[0]}")"/../../etc/jhb.conf.sh
+
+#------------------------------------------- source common functions from bash_d
+
+# bash_d is already available (it's part of etc/jhb.conf)
+
+bash_d_include error
+
+### variables ##################################################################
+
+# Nothing here.
+
+### functions ##################################################################
+
+# Nothing here.
+
+### main #######################################################################
+
+if $CI; then # break in CI, otherwise we get interactive prompt by JHBuild
+ error_trace_enable
+fi
+
+case "$1" in
+ debug)
+ echo_d "doing nothing"
+ ;;
+ configure)
+ jhbuild_configure "$2" # e.g. 'jhbuild/myapp.modules'
+ ccache_configure
+ ;;
+ *)
+ if sys_wrkdir_is_usable &&
+ sdkroot_exists &&
+ sys_usrlocal_is_clean; then
+
+ # these checks may issue warnings but have have no consequences otherwise
+ sys_macos_is_recommended || true
+ sys_sdk_is_recommended || true
+ else
+ exit 1 # cannot continue
+ fi
+ "$USR_DIR"/bin/jhbuild "$@"
+ ;;
+esac \ No newline at end of file
diff --git a/packaging/macos/jhb/usr/bin/run-parts b/packaging/macos/jhb/usr/bin/run-parts
new file mode 100755
index 0000000..ff0470c
--- /dev/null
+++ b/packaging/macos/jhb/usr/bin/run-parts
@@ -0,0 +1,73 @@
+#!/usr/bin/env bash
+
+### description ################################################################
+
+# A partial replacement for Debian's "run-parts" tool. Execute files in a
+# directory in their lexical order with the specialty of being able to
+# use symlinks to create an order without executing the files twice.
+
+### shellcheck #################################################################
+
+# Nothing here.
+
+### dependencies ###############################################################
+
+# Nothing here.
+
+### variables ##################################################################
+
+# Nothing here.
+
+### functions ##################################################################
+
+function main
+{
+ local mode=$1
+ local dir=$2
+
+ local file_pattern
+
+ if [ -d "$dir" ]; then
+ # reconstruct to make it proper (e.g. remove superfluous slashes)
+ dir=$(dirname "$dir")/$(basename "$dir")
+ else
+ # split into directory and file
+ file_pattern=$(basename "$dir")
+ dir=$(dirname "$dir")
+ fi
+
+ file_pattern=${file_pattern:-*} # default pattern is "*"
+
+ local linked_files
+ linked_files=$(find "$dir" -type l -exec readlink {} +)
+
+ for file in "$dir"/$file_pattern; do # requires 'shopt -s nullglob'
+ # Only process files that have no symlinks (in that same directory) pointing
+ # at them.
+ if [[ "$linked_files" != *$(basename "$file")* ]]; then
+ case "$mode" in
+ list)
+ echo "$file"
+ ;;
+ run)
+ $file
+ ;;
+ esac
+ fi
+ done
+}
+
+### main #######################################################################
+
+set -e
+
+shopt -s nullglob # in case no files are found
+
+case "$1" in
+ list)
+ main "list" "$2"
+ ;;
+ *)
+ main "run" "$1"
+ ;;
+esac
diff --git a/packaging/macos/jhb/usr/src/bash_d/README.md b/packaging/macos/jhb/usr/src/bash_d/README.md
new file mode 100644
index 0000000..fc97cc4
--- /dev/null
+++ b/packaging/macos/jhb/usr/src/bash_d/README.md
@@ -0,0 +1,9 @@
+# `bash_d`
+
+Conceived as part of my (unpublished) dotfiles on macOS, this collection of functions outgrew its originally limited scope and I began developing this into the spiritual successor of [bashlib](https://github.com/dehesselle/bashlib).
+
+This is far from finished and comes with no support and without documentation at this point, but I need to put this online now so I can start using this in other projects.
+
+## License
+
+[GPL](LICENSE) \ No newline at end of file
diff --git a/packaging/macos/jhb/usr/src/bash_d/ansi.sh b/packaging/macos/jhb/usr/src/bash_d/ansi.sh
new file mode 100644
index 0000000..5ecc5ff
--- /dev/null
+++ b/packaging/macos/jhb/usr/src/bash_d/ansi.sh
@@ -0,0 +1,76 @@
+# SPDX-FileCopyrightText: 2021 René de Hesselle <dehesselle@web.de>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+### description ################################################################
+
+# This file contains ANSI control codes to control terminal output.
+
+### shellcheck #################################################################
+
+# shellcheck shell=bash # no shebang as this file is intended to be sourced
+# shellcheck disable=SC2034 # there are a lot of unexported vars here
+
+### includes ###################################################################
+
+# Nothing here.
+
+### variables ##################################################################
+
+ANSI_ENABLED=${ANSI_ENABLED:-true} # allow ANSI escape sequences...
+ANSI_TERM_ONLY=${ANSI_TERM_ONLY:-true} # ...but only when running in a terminal
+
+ANSI_CURSOR_LEFT="\033[1D"
+
+# 0: normal
+# 1: bold
+# 2: faint
+# |
+# \033[1;32m
+# |
+# color
+ANSI_FG_BLACK="\033[0;30m"
+ANSI_FG_BLACK_BOLD="\033[1;30m"
+ANSI_FG_BLACK_BRIGHT="\033[0;90m"
+ANSI_FG_BLUE="\033[0;34m"
+ANSI_FG_BLUE_BOLD="\033[1;34m"
+ANSI_FG_BLUE_BRIGHT="\033[0;94m"
+ANSI_FG_CYAN="\033[0;36m"
+ANSI_FG_CYAN_BOLD="\033[1;36m"
+ANSI_FG_CYAN_BRIGHT="\033[0;96m"
+ANSI_FG_GREEN="\033[0;32m"
+ANSI_FG_GREEN_BOLD="\033[1;32m"
+ANSI_FG_GREEN_BRIGHT="\033[0;92m"
+ANSI_FG_MAGENTA="\033[0;35m"
+ANSI_FG_MAGENTA_BOLD="\033[1;35m"
+ANSI_FG_MAGENTA_BRIGHT="\033[0;95m"
+ANSI_FG_RED="\033[0;31m"
+ANSI_FG_RED_BOLD="\033[1;31m"
+ANSI_FG_RED_BRIGHT="\033[0;91m"
+ANSI_FG_RESET="\033[0;0m"
+ANSI_FG_WHITE="\033[0;37m"
+ANSI_FG_WHITE_BOLD="\033[1;37m"
+ANSI_FG_WHITE_BRIGHT="\033[0;97m"
+ANSI_FG_YELLOW="\033[0;33m"
+ANSI_FG_YELLOW_BOLD="\033[1;33m"
+ANSI_FG_YELLOW_BRIGHT="\033[0;93m"
+
+### functions ##################################################################
+
+function ansi_test_colors
+{
+ for color in ${!ANSI_FG_*}; do
+ echo -e "${!color}This is $color"
+ done
+}
+
+### aliases ####################################################################
+
+# This performs the following check:
+# - usage of ANSI is generally enabled AND
+# - we are either running in a terminal OR we don't care at all
+alias ansi_is_usable='$ANSI_ENABLED && ([ -t 1 ] || ! $ANSI_TERM_ONLY)'
+
+### main #######################################################################
+
+# Nothing here.
diff --git a/packaging/macos/jhb/usr/src/bash_d/assert.sh b/packaging/macos/jhb/usr/src/bash_d/assert.sh
new file mode 100644
index 0000000..6b65138
--- /dev/null
+++ b/packaging/macos/jhb/usr/src/bash_d/assert.sh
@@ -0,0 +1,42 @@
+# SPDX-FileCopyrightText: 2021 René de Hesselle <dehesselle@web.de>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+### description ################################################################
+
+# This file provides a mechanism to restrict sourcing a script to specific
+# platforms.
+
+### shellcheck #################################################################
+
+# shellcheck shell=bash # no shebang as this file is intended to be sourced
+
+### dependencies ###############################################################
+
+bash_d_include echo
+
+### variables ##################################################################
+
+ASSERT_RC=0
+
+### functions ##################################################################
+
+# Nothing here.
+
+### aliases ####################################################################
+
+# Bash version
+alias assert_bash4_or_above='[ ${BASH_VERSINFO[0]} -lt 4 ] && echo_e "$(basename ${BASH_SOURCE[0]}) will be unavailable (depends on bash >= 4)" && return $LINENO || true'
+
+# Git
+alias assert_git='[ ! -x "$(command -v git)" ] && echo_e "$(basename ${BASH_SOURCE[0]}) will be unavailable (depends on git)" && return $LINENO || true'
+
+# Unix platforms
+alias assert_darwin='[ "$(uname)" != "Darwin" ] && echo_e "Darwin required" && return $LINENO || true'
+alias assert_linux='[ "$(uname)" != "Linux" ] && echo_e "Linux required" && return $LINENO || true'
+
+alias assert_ok='ASSERT_RC=$?; [ $ASSERT_RC -ne 0 ] && echo_e "assert_ok rc=$ASSERT_RC" && return $LINENO'
+
+### main #######################################################################
+
+# Nothing here.
diff --git a/packaging/macos/jhb/usr/src/bash_d/bash_d.sh b/packaging/macos/jhb/usr/src/bash_d/bash_d.sh
new file mode 100644
index 0000000..e0715b4
--- /dev/null
+++ b/packaging/macos/jhb/usr/src/bash_d/bash_d.sh
@@ -0,0 +1,98 @@
+# SPDX-FileCopyrightText: 2021 René de Hesselle <dehesselle@web.de>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+### description ################################################################
+
+# Provide an include guard to be used in every script to protect them from
+# being sourced multiple times.
+
+### shellcheck #################################################################
+
+# shellcheck shell=bash # no shebang as this file is intended to be sourced
+
+### dependencies ###############################################################
+
+# All of these need to be "or true" because they fail on the first run.
+
+bash_d_include echo 2>/dev/null || true
+bash_d_include assert 2>/dev/null || true
+
+### variables ##################################################################
+
+BASH_D_DIR=${BASH_D_DIR:-$(dirname "${BASH_SOURCE[0]}")}
+
+if [ "$BASH_D_FAIL_ON_INCLUDE_ERROR" != "false" ]; then
+ BASH_D_FAIL_ON_INCLUDE_ERROR=true
+fi
+
+### functions ##################################################################
+
+function bash_d_include_all
+{
+ local includes
+ includes=$(mktemp "$TMP/${FUNCNAME[0]}".XXXXXX)
+
+ echo "BASH_D_FAIL_ON_INCLUDE_ERROR=false" >> "$includes"
+ for file in "$BASH_D_DIR"/*.sh; do
+ echo "bash_d_include $(basename "$file")" >> "$includes"
+ done
+ echo "BASH_D_FAIL_ON_INCLUDE_ERROR=true" >> "$includes"
+
+ # shellcheck disable=SC1090 # dynamic sourcing on purpose
+ source "$includes"
+ rm "$includes"
+}
+
+function bash_d_is_included
+{
+ local file=$1
+
+ #shellcheck disable=SC2076 # we want literal match, not regex
+ if [[ " ${BASH_D_INCLUDE_FILES[*]} " =~ \
+ " $BASH_D_DIR/$(basename -s .sh "$file").sh " ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+### aliases ####################################################################
+
+# shellcheck disable=SC2142 # too much trickery for shellcheck to digest
+alias bash_d_include=\
+'declare -a BASH_D_INCLUDE_FILES; '\
+'BASH_D_INCLUDE_FILE=$(sed -n ${LINENO}p ${BASH_SOURCE[0]} | awk '"'"'{ print $2 }'"'"'); '\
+'BASH_D_INCLUDE_FILE=$BASH_D_DIR/$(basename -s .sh $BASH_D_INCLUDE_FILE).sh; '\
+'if [[ " ${BASH_D_INCLUDE_FILES[@]} " =~ " ${BASH_D_INCLUDE_FILE} " ]]; then '\
+' : ; '\
+'else '\
+' if [ "$BASH_D_INCLUDE_FILE" = "$BASH_D_DIR/bash_d.sh" ] && '\
+' [ ${#BASH_D_INCLUDE_FILES[@]} -gt 0 ] || '\
+' source $BASH_D_INCLUDE_FILE; then '\
+' BASH_D_INCLUDE_FILE=$(sed -n ${LINENO}p ${BASH_SOURCE[0]} | awk '"'"'{ print $2 }'"'"'); '\
+' BASH_D_INCLUDE_FILE=$BASH_D_DIR/$(basename -s .sh $BASH_D_INCLUDE_FILE).sh; '\
+' BASH_D_INCLUDE_FILES+=("$BASH_D_INCLUDE_FILE"); '\
+' else '\
+' if alias | grep "echo_e" >/dev/null; then '\
+' echo_e "$BASH_D_INCLUDE_FILE will be unavailable"; '\
+' else '\
+' >&2 echo "error: $BASH_D_INCLUDE_FILE will be unavailable"; '\
+' fi; '\
+' if [ "$BASH_D_INCLUDE_FILE" != "$BASH_D_DIR/bash_d.sh" ] && $BASH_D_FAIL_ON_INCLUDE_ERROR; then '\
+' exit 1; '\
+' fi '\
+' fi '\
+'fi '\
+'# '
+
+### main #######################################################################
+
+if [ ! -d "$BASH_D_DIR" ]; then
+ echo "error: BASH_D_DIR=$BASH_D_DIR is invalid [${BASH_SOURCE[0]}]"
+ exit 1
+fi
+
+shopt -s expand_aliases
+
+bash_d_include bash_d # Need to rerun so complete setting us up.
diff --git a/packaging/macos/jhb/usr/src/bash_d/echo.sh b/packaging/macos/jhb/usr/src/bash_d/echo.sh
new file mode 100644
index 0000000..d160db0
--- /dev/null
+++ b/packaging/macos/jhb/usr/src/bash_d/echo.sh
@@ -0,0 +1,52 @@
+# SPDX-FileCopyrightText: 2021 René de Hesselle <dehesselle@web.de>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+### description ################################################################
+
+# Provide colorful convenience functions for echo.
+
+### shellcheck #################################################################
+
+# shellcheck shell=bash # no shebang as this file is intended to be sourced
+
+### includes ###################################################################
+
+bash_d_include ansi
+
+### variables ##################################################################
+
+# Nothing here.
+
+### functions ##################################################################
+
+function echo_message__
+{
+ local funcname=$1 # empty if outside function
+ local filename=$2
+ local type=$3
+ local color=$4
+ local args=${*:5}
+
+ if [ -z "$funcname" ] || [ "$funcname" = "source" ]; then
+ funcname=$(basename "$filename")
+ fi
+
+ if ansi_is_usable; then
+ echo -e "${color}$type:$ANSI_FG_RESET $args ${ANSI_FG_BLACK_BRIGHT}[$funcname]$ANSI_FG_RESET"
+ else
+ echo "$type: $args [$funcname]"
+ fi
+}
+
+### aliases ####################################################################
+
+alias echo_d='>&2 echo_message__ "$FUNCNAME" "${BASH_SOURCE[0]}" "debug" "$ANSI_FG_BLUE_BOLD"'
+alias echo_e='>&2 echo_message__ "$FUNCNAME" "${BASH_SOURCE[0]}" "error" "$ANSI_FG_RED_BRIGHT"'
+alias echo_i='>&2 echo_message__ "$FUNCNAME" "${BASH_SOURCE[0]}" "info" "$ANSI_FG_BLUE_BRIGHT"'
+alias echo_o='>&2 echo_message__ "$FUNCNAME" "${BASH_SOURCE[0]}" "ok" "$ANSI_FG_GREEN_BRIGHT"'
+alias echo_w='>&2 echo_message__ "$FUNCNAME" "${BASH_SOURCE[0]}" "warning" "$ANSI_FG_YELLOW_BRIGHT"'
+
+### main #######################################################################
+
+# Nothing here.
diff --git a/packaging/macos/jhb/usr/src/bash_d/error.sh b/packaging/macos/jhb/usr/src/bash_d/error.sh
new file mode 100644
index 0000000..69ec0be
--- /dev/null
+++ b/packaging/macos/jhb/usr/src/bash_d/error.sh
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# https://github.com/dehesselle/bash_d
+
+### includes ###################################################################
+
+bash_d_include ansi.sh
+bash_d_include echo.sh
+
+### variables ##################################################################
+
+# Nothing here.
+
+### functions ##################################################################
+
+function error_catch
+{
+ local rc=$1
+
+ local index=0
+ local output
+
+ while output=$(caller $index); do
+ if [ $index -eq 0 ]; then
+ if ansi_is_usable; then
+ echo_e "rc=$rc $ANSI_FG_YELLOW_BRIGHT$BASH_COMMAND$ANSI_FG_RESET"
+ else
+ echo "rc=$rc $BASH_COMMAND"
+ fi
+ fi
+
+ echo_e $output
+ ((index+=1))
+ done
+
+ exit $rc
+}
+
+### aliases ####################################################################
+
+alias error_trace_enable='set -o errtrace; trap '\''error_catch ${?}'\'' ERR'
+alias error_trace_disable='trap - ERR'
+
+### main #######################################################################
+
+# Nothing here. \ No newline at end of file
diff --git a/packaging/macos/jhb/usr/src/bash_d/lib.sh b/packaging/macos/jhb/usr/src/bash_d/lib.sh
new file mode 100644
index 0000000..c1f385e
--- /dev/null
+++ b/packaging/macos/jhb/usr/src/bash_d/lib.sh
@@ -0,0 +1,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.
diff --git a/packaging/macos/jhb/usr/src/bash_d/readlinkf.sh b/packaging/macos/jhb/usr/src/bash_d/readlinkf.sh
new file mode 100644
index 0000000..135a886
--- /dev/null
+++ b/packaging/macos/jhb/usr/src/bash_d/readlinkf.sh
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# https://github.com/dehesselle/bash_d
+
+### description ################################################################
+
+# This is a replacement for GNU's '-f' extension to 'readlink' which is not
+# part of the BSD version.
+
+### includes ###################################################################
+
+# Nothing here.
+
+### variables ##################################################################
+
+# Nothing here.
+
+### functions ##################################################################
+
+# Nothing here.
+
+### aliases ####################################################################
+
+alias readlinkf='perl -e '"'"'use Cwd "abs_path"; print abs_path(@ARGV[0])'"'"' --'
+
+### main #######################################################################
+
+# Nothing here.