summaryrefslogtreecommitdiffstats
path: root/packaging/macos/030-funcs.sh
blob: 3b0814196767ba3b8b304c267afe1c0ca025d353 (plain)
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
}