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
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
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
}
|