summaryrefslogtreecommitdiffstats
path: root/packaging/macos/src/ink.sh
blob: fe4d19c1195c41ce6f76708d4e07ef4f810e390a (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
# SPDX-FileCopyrightText: 2021 René de Hesselle <dehesselle@web.de>
#
# SPDX-License-Identifier: GPL-2.0-or-later

### description ################################################################

# This file contains everything related to Inkscape.

### shellcheck #################################################################

# shellcheck shell=bash # no shebang as this file is intended to be sourced
# shellcheck disable=SC2034 # multipe vars only used outside this script

### dependencies ###############################################################

# Nothing here.

### variables ##################################################################

#----------------------------------------------- source directory and git branch

# There are 3 possible scenarios:
#
#   1. We're running inside Inkscape's CI:
#      The repository has already been cloned, set INK_DIR accordingly.
#
#   2. We're not running inside Inkscape's CI and INK_DIR has been set:
#      Use INK_DIR provided as-is, we expect the source to be there.
#
#   3. We're not running inside Inkscape's CI CI and INK_DIR has not been set:
#      Set INK_DIR to our default location, we'll clone the repo there.

if [ "$CI_PROJECT_NAME" = "inkscape" ]; then   # running in Inkscape's CI
  INK_DIR=$CI_PROJECT_DIR
else                                       # not running in Inkscape's CI
  # Use default directory if not provided.
  if [ -z "$INK_DIR" ]; then
    INK_DIR=$SRC_DIR/inkscape
  fi

  # Allow using a custom Inkscape repository and branch.
  if [ -z "$INK_URL" ]; then
    INK_URL=https://gitlab.com/inkscape/inkscape
  fi

  # Allow using a custom branch.
  if [ -z "$INK_BRANCH" ]; then
    INK_BRANCH=1.2.x
  fi
fi

INK_BLD_DIR=$BLD_DIR/$(basename "$INK_DIR")  # we build out-of-tree

#------------------------------------------------------------------ build number

INK_BUILD=${INK_BUILD:-0}

#------------------------------------ Python runtime to be bundled with Inkscape

# Inkscape will be bundled with its own (customized) Python 3 runtime to make
# the core extensions work out-of-the-box.

INK_PYTHON_VER_MAJOR=3
INK_PYTHON_VER_MINOR=10
INK_PYTHON_VER=$INK_PYTHON_VER_MAJOR.$INK_PYTHON_VER_MINOR
INK_PYTHON_URL="https://gitlab.com/api/v4/projects/26780227/packages/generic/\
python_macos/15/python_${INK_PYTHON_VER/./}_$(uname -m)_inkscape.tar.xz"
INK_PYTHON_ICON_URL="https://gitlab.com/inkscape/vectors/content/-/raw/\
5f4f4cdf/branding/projects/extensions_c1.svg"

#----------------------------------- Python packages to be bundled with Inkscape

# https://pypi.org/project/appdirs/
INK_PYTHON_PKG_APPDIRS=appdirs==1.4.4

# https://pypi.org/project/beautifulsoup4/
# https://pypi.org/project/soupsieve/
INK_PYTHON_PKG_BEAUTIFULSOUP4="\
  beautifulsoup4==4.10.0\
  soupsieve==2.3.1\
"

# https://pypi.org/project/CacheControl/
# https://pypi.org/project/certifi/
# https://pypi.org/project/charset-normalizer/
# https://pypi.org/project/idna/
# https://pypi.org/project/lockfile/
# https://pypi.org/project/msgpack/
# https://pypi.org/project/requests/
# https://pypi.org/project/urllib3/
INK_PYTHON_PKG_CACHECONTROL="\
  CacheControl==0.12.10\
  certifi==2021.10.8\
  charset_normalizer==2.0.11\
  idna==3.3\
  lockfile==0.12.2\
  msgpack==1.0.3\
  requests==2.27.1\
  urllib3==1.26.8\
"

# https://pypi.org/project/cssselect/
INK_PYTHON_PKG_CSSSELECT=cssselect==1.1.0

# https://pypi.org/project/lxml/
INK_PYTHON_PKG_LXML=lxml==4.7.1

# https://pypi.org/project/numpy/
INK_PYTHON_PKG_NUMPY=https://files.pythonhosted.org/packages/\
b4/85/8097082c4794d854e40f84639c83e33e516431faaeb9cecba39eba6921d5/\
numpy-1.22.1-cp310-cp310-macosx_10_9_universal2.whl

# https://pypi.org/project/Pillow/
INK_PYTHON_PKG_PILLOW=Pillow==9.0.0

# https://pypi.org/project/pycairo/
# https://pypi.org/project/PyGObject/
INK_PYTHON_PKG_PYGOBJECT="\
  PyGObject==3.42.1\
  pycairo==1.21.0\
"

# https://pypi.org/project/pyserial/
INK_PYTHON_PKG_PYSERIAL=pyserial==3.5

# https://pypi.org/project/scour/
# https://pypi.org/project/six/
INK_PYTHON_PKG_SCOUR="\
  scour==0.38.2\
  six==1.16.0\
  packaging==21.3 \
  pyparsing==3.0.7 \
"

#------------------------------------------- application bundle directory layout

INK_APP_DIR=$ARTIFACT_DIR/Inkscape.app

INK_APP_CON_DIR=$INK_APP_DIR/Contents
INK_APP_RES_DIR=$INK_APP_CON_DIR/Resources
INK_APP_FRA_DIR=$INK_APP_CON_DIR/Frameworks
INK_APP_BIN_DIR=$INK_APP_RES_DIR/bin
INK_APP_ETC_DIR=$INK_APP_RES_DIR/etc
INK_APP_EXE_DIR=$INK_APP_CON_DIR/MacOS
INK_APP_LIB_DIR=$INK_APP_RES_DIR/lib
INK_APP_SPK_DIR=$INK_APP_LIB_DIR/python$INK_PYTHON_VER/site-packages

INK_APP_PLIST=$INK_APP_CON_DIR/Info.plist

### functions ##################################################################

function ink_get_version
{
  local file=$INK_DIR/CMakeLists.txt
  local ver_major
  ver_major=$(grep INKSCAPE_VERSION_MAJOR "$file" | head -n 1 |
    awk '{ print $2+0 }')
  local ver_minor
  ver_minor=$(grep INKSCAPE_VERSION_MINOR "$file" | head -n 1 |
    awk '{ print $2+0 }')
  local ver_patch
  ver_patch=$(grep INKSCAPE_VERSION_PATCH "$file" | head -n 1 |
    awk '{ print $2+0 }')
  local ver_suffix
  ver_suffix=$(grep INKSCAPE_VERSION_SUFFIX "$file" | head -n 1 |
    awk '{ print $2 }')

  ver_suffix=${ver_suffix%\"*}  # remove "double quotes and all" from end
  ver_suffix=${ver_suffix#\"}   # remove "double quote" from beginning

  echo "$ver_major.$ver_minor.$ver_patch$ver_suffix"
}

function ink_get_repo_shorthash
{
  # do it the same way as in CMakeScripts/inkscape-version.cmake
  git -C "$INK_DIR" rev-parse --short HEAD
}

function ink_pipinstall
{
  local packages=$1     # name of variable that resolves to list of packages
  local options=$2      # optional

  # turn package names into filenames of our wheels
  local wheels
  for package in $(eval echo \$"$packages"); do
    if [ "${package::8}" = "https://" ]; then
      package=$(basename "$package")
    else
      package=$(eval echo "${package/==/-}"*.whl)
    fi

    # If present in TMP_DIR, use that. This is how the externally built
    # packages can be fed into this.
    if [ -f "$TMP_DIR/$package" ]; then
      wheels="$wheels $TMP_DIR/$package"
    else
      wheels="$wheels $PKG_DIR/$package"
    fi
  done

  local path_original=$PATH
  export PATH=$INK_APP_FRA_DIR/Python.framework/Versions/Current/bin:$PATH

  # shellcheck disable=SC2086 # we need word splitting here
  pip$INK_PYTHON_VER_MAJOR install \
    --prefix "$INK_APP_RES_DIR" \
    --ignore-installed \
    $options \
    $wheels

  export PATH=$path_original

  local ink_pipinstall_func
  ink_pipinstall_func=ink_pipinstall_$(echo "${packages##*_}" |
    tr "[:upper:]" "[:lower:]")

  if declare -F "$ink_pipinstall_func" > /dev/null; then
    $ink_pipinstall_func
  fi
}

function ink_pipinstall_lxml
{
  lib_change_paths \
    @loader_path/../../.. \
    "$INK_APP_LIB_DIR" \
    "$INK_APP_SPK_DIR"/lxml/etree.cpython-"${INK_PYTHON_VER/./}"-darwin.so \
    "$INK_APP_SPK_DIR"/lxml/objectify.cpython-"${INK_PYTHON_VER/./}"-darwin.so
}

function ink_pipinstall_numpy
{
  sed -i '' '1s|.*|#!/usr/bin/env python'"$INK_PYTHON_VER_MAJOR"'|' \
    "$INK_APP_BIN_DIR"/f2py
  sed -i '' '1s|.*|#!/usr/bin/env python'"$INK_PYTHON_VER_MAJOR"'|' \
    "$INK_APP_BIN_DIR"/f2py$INK_PYTHON_VER_MAJOR
  sed -i '' '1s|.*|#!/usr/bin/env python'"$INK_PYTHON_VER_MAJOR"'|' \
    "$INK_APP_BIN_DIR"/f2py$INK_PYTHON_VER

  find "$INK_APP_LIB_DIR/python$INK_PYTHON_VER/site-packages/numpy" \
    '(' -name "*.so" -o -name "*.dylib" ')' \
    -exec codesign --remove-signature {} \;

  find "$INK_APP_LIB_DIR/python$INK_PYTHON_VER/site-packages/numpy" \
    -name "*.a" -delete
}

function ink_pipinstall_pygobject
{
  lib_change_paths \
    @loader_path/../../.. \
    "$INK_APP_LIB_DIR" \
    "$INK_APP_SPK_DIR"/gi/_gi.cpython-"${INK_PYTHON_VER/./}"-darwin.so \
    "$INK_APP_SPK_DIR"/gi/_gi_cairo.cpython-"${INK_PYTHON_VER/./}"-darwin.so \
    "$INK_APP_SPK_DIR"/cairo/_cairo.cpython-"${INK_PYTHON_VER/./}"-darwin.so
}

function ink_pipinstall_pyserial
{
  find "$INK_APP_SPK_DIR"/serial -type f -name "*.pyc" -exec rm {} \;
  sed -i '' '1s|.*|#!/usr/bin/env python3|' \
    "$INK_APP_BIN_DIR"/pyserial-miniterm
  sed -i '' '1s|.*|#!/usr/bin/env python3|' "$INK_APP_BIN_DIR"/pyserial-ports
}

function ink_pipinstall_scour
{
  sed -i '' '1s|.*|#!/usr/bin/env python3|' "$INK_APP_BIN_DIR"/scour
}

function ink_download_python
{
  curl -o "$PKG_DIR"/"$(basename "${INK_PYTHON_URL%\?*}")" -L "$INK_PYTHON_URL"
  curl -o "$PKG_DIR"/"$(basename "$INK_PYTHON_ICON_URL")" \
    -L "$INK_PYTHON_ICON_URL"
}

function ink_install_python
{
  mkdir "$INK_APP_FRA_DIR"
  tar -C "$INK_APP_FRA_DIR" -xf "$PKG_DIR"/"$(basename "${INK_PYTHON_URL%\?*}")"

  # link it to INK_APP_BIN_DIR so it'll be in PATH for the app
  mkdir -p "$INK_APP_BIN_DIR"
  # shellcheck disable=SC2086 # it's an integer
  ln -sf ../../Frameworks/Python.framework/Versions/Current/bin/\
python$INK_PYTHON_VER_MAJOR "$INK_APP_BIN_DIR"

  # create '.pth' file inside Framework to include our site-packages directory
  # shellcheck disable=SC2086 # it's an integer
  echo "../../../../../../../Resources/lib/python$INK_PYTHON_VER/site-packages"\
    > "$INK_APP_FRA_DIR"/Python.framework/Versions/Current/lib/\
python$INK_PYTHON_VER/site-packages/inkscape.pth

  # use custom icon for Python.app
  svg2icns \
    "$PKG_DIR/$(basename "$INK_PYTHON_ICON_URL")" \
    "$INK_APP_FRA_DIR/Python.framework/Resources/Python.app/Contents/\
Resources/PythonInterpreter.icns" \
    8
}

function ink_build_wheels
{
  jhb run pip3 install wheel

  for package_set in ${!INK_PYTHON_PKG_*}; do
    local packages
    for package in $(eval echo \$"$package_set"); do
      if [ "${package::8}" = "https://" ]; then
        curl -L -o "$PKG_DIR/$(basename "$package")" "$package"
      else
        packages="$packages $package"
      fi
    done

    if [ -n "$packages" ]; then
      # shellcheck disable=SC2086 # we need word splitting here
      jhb run pip3 wheel --no-binary :all: $packages -w "$PKG_DIR"
      packages=""
    fi
  done
}

### main #######################################################################

# Nothing here.