diff options
Diffstat (limited to 'packaging/macos/030-funcs.sh')
-rw-r--r-- | packaging/macos/030-funcs.sh | 406 |
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 +} |