# 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://) # 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 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 </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 }