summaryrefslogtreecommitdiffstats
path: root/packaging/macos/030-funcs.sh
diff options
context:
space:
mode:
Diffstat (limited to 'packaging/macos/030-funcs.sh')
-rw-r--r--packaging/macos/030-funcs.sh406
1 files changed, 406 insertions, 0 deletions
diff --git a/packaging/macos/030-funcs.sh b/packaging/macos/030-funcs.sh
new file mode 100644
index 0000000..3b08141
--- /dev/null
+++ b/packaging/macos/030-funcs.sh
@@ -0,0 +1,406 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# This file is part of the build pipeline for Inkscape on macOS.
+#
+# ### 030-funcs.sh ###
+# This file contains all the functions used by the other scripts. It helps
+# modularizing functionality and keeping the scripts that do the real work
+# as readable as possible.
+# This file does not include the "vars" files it requires itself (on purpose,
+# for flexibility reasons), the script that wants to use these functions
+# needs to do that. The suggested way is to always source all the "0nn-*.sh"
+# files in order.
+
+[ -z $FUNCS_INCLUDED ] && FUNCS_INCLUDED=true || return # include guard
+
+### get repository version string ##############################################
+
+function get_repo_version
+{
+ local repo=$1
+ # do it the same way as in CMakeScripts/inkscape-version.cmake
+ echo $(git -C $repo rev-parse --short HEAD)
+}
+
+### get Inkscape version from CMakeLists.txt ###################################
+
+function get_inkscape_version
+{
+ local file=$INK_DIR/CMakeLists.txt
+ local ver_major=$(grep INKSCAPE_VERSION_MAJOR $file | head -n 1 | awk '{ print $2+0 }')
+ local ver_minor=$(grep INKSCAPE_VERSION_MINOR $file | head -n 1 | awk '{ print $2+0 }')
+ local ver_patch=$(grep INKSCAPE_VERSION_PATCH $file | head -n 1 | awk '{ print $2+0 }')
+ local ver_suffix=$(grep INKSCAPE_VERSION_SUFFIX $file | head -n 1 | awk '{ print $2 }')
+
+ ver_suffix=${ver_suffix%\"*} # remove "double quote and everything after" from end
+ ver_suffix=${ver_suffix#\"} # remove "double quote" from beginning
+
+ echo $ver_major.$ver_minor.$ver_patch$ver_suffix
+}
+
+### get compression flag by filename extension #################################
+
+function get_comp_flag
+{
+ local file=$1
+
+ local extension=${file##*.}
+
+ case $extension in
+ bz2) echo "j" ;;
+ gz) echo "z" ;;
+ tgz) echo "z" ;;
+ xz) echo "J" ;;
+ *) echo "ERROR unknown extension $extension"
+ esac
+}
+
+### download and extract source tarball ########################################
+
+function install_source
+{
+ local url=$1
+ local target_dir=$2 # optional: target directory, defaults to $SRC_DIR
+ local options=$3 # optional: additional options for 'tar'
+
+ [ ! -d $TMP_DIR ] && mkdir -p $TMP_DIR
+ local log=$(mktemp $TMP_DIR/$FUNCNAME.XXXX)
+ [ -z $target_dir ] && target_dir=$SRC_DIR
+ [ ! -d $SRC_DIR ] && mkdir -p $SRC_DIR
+
+ cd $target_dir
+
+ # This downloads a file and pipes it directly into tar (file is not saved
+ # to disk) to extract it. Output from stderr is saved temporarily to
+ # determine the directory the files have been extracted to.
+ curl -L $(preferCachedUrl $url) | tar xv$(get_comp_flag $url) $options 2>$log
+ cd $(head -1 $log | awk '{ print $2 }')
+ [ $? -eq 0 ] && rm $log || echo "$FUNCNAME: check $log"
+}
+
+### download a file and save to disk ###########################################
+
+function save_file
+{
+ local url=$1
+ local target_dir=$2 # optional argument, defaults to $SRC_DIR
+
+ [ -z $target_dir ] && target_dir=$SRC_DIR
+
+ local file=$target_dir/$(basename $url)
+ if [ -f $file ]; then
+ echo "$FUNCNAME: file $file exists"
+ else
+ curl -o $file -L $url
+ fi
+}
+
+### make, make install in jhbuild environment ##################################
+
+function make_makeinstall
+{
+ jhbuild run make
+ jhbuild run make install
+}
+
+### configure, make, make install in jhbuild environment #######################
+
+function configure_make_makeinstall
+{
+ local flags="$*"
+
+ jhbuild run ./configure --prefix=$OPT_DIR $flags
+ make_makeinstall
+}
+
+### cmake, make, make install in jhbuild environment ###########################
+
+function cmake_make_makeinstall
+{
+ local flags="$*"
+
+ mkdir builddir
+ cd builddir
+ jhbuild run cmake -DCMAKE_INSTALL_PREFIX=$OPT_DIR $flags ..
+ make_makeinstall
+}
+
+### create and mount ramdisk ###################################################
+
+# There is a more common approach to do this using
+# diskutil eraseVolume HFS+ VolName $(hdiutil attach -nomount ram://<size>)
+# but that always attaches the ramdisk below '/Volumes'.
+# To have full control, we need to do it as follows.
+
+function create_ramdisk
+{
+ local dir=$1 # mountpoint
+ local size=$2 # unit is GiB
+
+ if [ $(mount | grep $dir | wc -l) -eq 0 ]; then
+ local device=$(hdiutil attach -nomount ram://$(expr $size \* 1024 \* 2048))
+ newfs_hfs -v "RAMDISK" $device
+ mount -o noatime,nobrowse -t hfs $device $dir
+ fi
+}
+
+### insert line into a textfile ################################################
+
+# usage:
+# insert_before <filename> <insert before this pattern> <line to insert>
+
+function insert_before
+{
+ local file=$1
+ local pattern=$2
+ local line=$3
+
+ local file_tmp=$(mktemp)
+ awk "/${pattern}/{print \"$line\"}1" $file > $file_tmp
+ cat $file_tmp > $file # we don't 'mv' to preserve permissions
+ rm $file_tmp
+}
+
+### escape replacement string for sed ##########################################
+
+# Escape slashes, backslashes and ampersands in strings to be used s as
+# replacement strings when using 'sed'. Newlines are not taken into
+# consideartion here.
+# reference: https://stackoverflow.com/a/2705678
+
+function escape_sed
+{
+ local string="$*"
+
+ echo "$string" | sed -e 's/[\/&]/\\&/g'
+}
+
+### replace line that matches pattern ##########################################
+
+function replace_line
+{
+ local file=$1
+ local pattern=$2
+ local replacement=$3
+
+ sed -i '' "s/.*${pattern}.*/$(escape_sed $replacement)/" $file
+}
+
+### relocate a library dependency ##############################################
+
+function relocate_dependency
+{
+ local target=$1 # fully qualified path and library name to new location
+ local library=$2 # library to be modified (change 'source' to 'target'I
+
+ local source_lib=${target##*/} # get library filename from target location
+ local source=$(otool -L $library | grep $source_lib | awk '{ print $1 }')
+
+ install_name_tool -change $source $target $library
+}
+
+### relocate all neighbouring libraries in a directory #########################
+
+function relocate_neighbouring_libs
+{
+ local dir=$1
+
+ for lib in $dir/*.dylib; do
+ for linked_lib in $(otool -L $lib | tail -n +3 | awk '{ print $1 }'); do
+ if [ -f $APP_LIB_DIR/$(basename $linked_lib) ]; then
+ relocate_dependency @loader_path/$(basename $linked_lib) $lib
+ fi
+ done
+ done
+}
+
+### 'readlink -f' replacement ##################################################
+
+# This is what the oneliner setting SELF_DIR (see top of file) is based on.
+
+function readlinkf
+{
+ # 'readlink -f' replacement: https://stackoverflow.com/a/1116890
+ # 'do while' replacement: https://stackoverflow.com/a/16491478
+
+ local file=$1
+
+ # iterate down a (possible) chain of symlinks
+ while
+ [ ! -z $(readlink $file) ] && file=$(readlink $file)
+ cd $(dirname $file)
+ file=$(basename $file)
+ [ -L "$file" ]
+ do
+ :
+ done
+
+ # Compute the canonicalized name by finding the physical path
+ # for the directory we're in and appending the target file.
+ echo $(pwd -P)/$file
+}
+
+### create disk image ##########################################################
+
+function create_dmg
+{
+ local app=$1
+ local dmg=$2
+ local cfg=$3
+
+ # set application
+ sed -i '' "s/PLACEHOLDERAPPLICATION/$(escape_sed $app)/" $cfg
+
+ # set disk image icon (if it exists)
+ local icon=$SRC_DIR/$(basename -s .py $cfg).icns
+ if [ -f $icon ]; then
+ sed -i '' "s/PLACEHOLDERICON/$(escape_sed $icon)/" $cfg
+ fi
+
+ # set background image (if it exists)
+ local background=$SRC_DIR/$(basename -s .py $cfg).png
+ if [ -f $background ]; then
+ sed -i '' "s/PLACEHOLDERBACKGROUND/$(escape_sed $background)/" $cfg
+ fi
+
+ # create disk image
+ dmgbuild -s $cfg "$(basename -s .app $app)" $dmg
+}
+
+### run script via Terminal.app ################################################
+
+# This is for commands that otherwise don't behave when running in CI.
+# It requires a running desktop session (i.e. logged in user) and special
+# permissions on newer macOS versions.
+# Adapted from: https://stackoverflow.com/a/27970527
+# https://gist.github.com/masci/ff51d9cf40a87a80094c
+
+function run_in_terminal
+{
+ local command=$1
+
+ #local window_id=$(uuidgen) # this would be really unique but...
+ local window_id=$(date +%s) # ...seconds would help more with debugging
+
+ osascript <<EOF
+tell application "Terminal"
+ set _tab to do script "echo -n -e \"\\\033]0;$window_id\\\007\"; $command; exit"
+ delay 1
+ repeat while _tab is busy
+ delay 1
+ end repeat
+ close (every window whose name contains "$window_id")
+end tell
+EOF
+}
+
+### replacements for plain echo: ok, error, info, warning ######################
+
+function echo_ok
+{
+ echo -e "✅ $*"
+}
+
+function echo_err
+{
+ echo -e "❌ $*"
+}
+
+function echo_warn
+{
+ echo -e "⚠️ $*"
+}
+
+function echo_info
+{
+ echo -e "ℹ️ $*"
+}
+
+function echo_act # action
+{
+ echo -e "➡️ $*"
+}
+
+### create a ramdisk and return the device #####################################
+
+function create_ram_device
+{
+ local size_gib=$1 # unit is GiB
+ local name=$2 # volume name
+
+ [ -z $name ] && name=$(uuidgen | md5 | head -c 8) # generate random name
+
+ local size_sectors=$(expr $size_gib \* 1024 \* 2048)
+ local device=$(hdiutil attach -nomount ram://$size_sectors)
+ newfs_hfs -v "$name" $device >/dev/null
+
+ echo $device
+}
+
+### attach a disk image and return the device ##################################
+
+function create_dmg_device
+{
+ local dmg=$1
+ local options=$2 # optional arguments for hdiutil
+
+ local device=$(hdiutil attach -nomount $dmg $options | grep "^/dev/disk" | \
+ grep "Apple_HFS" | awk '{ print $1 }')
+
+ echo $device
+}
+
+### replace URL ################################################################
+
+function preferCachedUrl
+{
+ local url=$1
+
+ # This is a placeholder function you can use to replace URLs with locally
+ # mirrored ones.
+
+ echo $url
+}
+
+### install Python package with Python.framework ###############################
+
+function pip_install
+{
+ local package=$1
+
+ local PATH_ORIGINAL=$PATH
+ export PATH=$APP_FRA_DIR/Python.framework/Versions/Current/bin:$PATH
+
+ pip$PY3_MAJOR install \
+ --prefix=$APP_RES_DIR \
+ --ignore-installed \
+ $package
+
+ export PATH=$PATH_ORIGINAL
+}
+
+### this function is called on "trap ERR" ######################################
+
+# macOS' old bash 3.x has a bug that causes the line number of the error
+# to point to the function entry instead of the command inside the function.
+
+function catch_error
+{
+ local file=$1
+ local line=$2
+ local func=$3
+ local command=$4
+ local rc=$5
+
+ [ -z $func ] && func="main" || true
+
+ ((ERRTRACE_COUNT++))
+
+ case $ERRTRACE_COUNT in
+ 1) echo -e "\033[97m\033[101m\033[1m[$file:$line] $func failed with rc=$rc\033[39m\033[49m\033[0m"
+ echo -e "\033[93m$command\033[39m"
+ ;;
+ *) echo -e "[$file:$line] <- $func"
+ ;;
+ esac
+}