File: portable-utils.in

package info (click to toggle)
musescore2 2.3.2%2Bdfsg4-15
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye
  • size: 168,212 kB
  • sloc: cpp: 262,317; xml: 176,707; sh: 3,377; ansic: 1,384; python: 356; makefile: 227; perl: 82; pascal: 78
file content (323 lines) | stat: -rw-r--r-- 11,826 bytes parent folder | download | duplicates (7)
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
#!/bin/bash

# This file exists to keep AppRun as small as possible.
# TODO: rewrite this file in a more portable language (e.g. C)

function main() {
  case "$1" in
    -h|--help )
      showHelp
      ;;
    install )
      installResources "$2" "$3" || errorMsg "Unable to install to '${prefix}'"
      ;;
    uninstall|remove )
      removeResources "$2" "$3" || errorMsg "Unable to remove from '${prefix}'"
      ;;
    man|manual|manpage )
      man "${APPDIR}/share/man/man1/mscore@MSCORE_INSTALL_SUFFIX@.1.gz"
      ;;
    check-depends|check-dependencies )
      checkDependencies "$2"
      ;;
    * )
      echo "Unknown option: '$1'."
      return 1
  esac
}

function showHelp() {
cat <<EOF
$(printVersion centered)

 This portable version of MuseScore has all of MuseScore's normal features, but
it does not need to be installed. There is an option to install it if you would
like full integration with other applications and the desktop environment.

Usage: $(basename "${APPIMAGE}") [options] [scorefile]

Special options for MuseScore Portable AppImage:
  -h, --help                    Displays this help and the normal help (below).
  man, manual, manpage          Displays MuseScore's man page.
  install [-i] [PREFIX]         Installs resources for desktop integration.
  remove, uninstall [PREFIX]    Removes resources from desktop environment.
  check-depends [exes-only]     Displays system information for developers.

Ordinary MuseScore options:
$("${APPDIR}/bin/mscore@MSCORE_INSTALL_SUFFIX@" --help | tail -n +5)
EOF
}

function printVersion() {
  local pretty=$(sed -rn "s|^Name=([^#]*)|\1|p" "${APPDIR}/mscore@MSCORE_INSTALL_SUFFIX@.desktop")
  local long=$("${APPDIR}/bin/mscore@MSCORE_INSTALL_SUFFIX@" --long-version 2>&1 | tail -n 1)
  if [ "$1" == "centered" ]; then
    printf "%*s\n" "$(((${#pretty}+80)/2))" "$pretty"
    printf "%*s\n" "$(((${#long}+80)/2))" "$long"
    return
  fi
  echo $pretty
  echo $long
}

function readYes() {
  read -s -n 1 answer
  if [ "$answer" == "n" ] || [ "$answer" == "N" ] ; then
    echo " N"
    return 1
  fi
  echo " Y" # Default is Y
  return 0
}

function installResources() {
  local interactive=""
  if [ "$2" == "-i" ]; then
    interactive=true
  elif [ "$1" == "-i" ]; then
    interactive=true
    shift
  fi
  if [ "$1" != "" ]; then
    # User specified a directory
    local prefix="$1"
    local bin="$prefix/bin"
    local bin_str="PREFIX/bin"
    local question="The default location might be better. Proceed anyway [Y/n]?"
    local cancelled="Cancelled: rerun without a PREFIX to use the default."
  elif [ "${EUID}" == "0" ]; then
    # Running as root (sudo)
    local prefix="/usr/local"
    local bin="$prefix/bin"
    local bin_str="PREFIX/bin"
    local question="Install resources for all users [Y/n]?"
    local cancelled="Cancelled: rerun without root (sudo) to install for one user only."
  else
    # Not running as root
    prefix="${HOME}/.local"
    local bin="$prefix/bin"
    local bin_str="PREFIX/bin"
    local question="Install resources for one user only (${USER}) [Y/n]?"
    local cancelled="Cancelled: rerun as root (sudo) to install for all users."
  fi
cat <<EOF
Installation step 1 of 3.
PREFIX is '$prefix'.
Preparing to install resources to:
     PREFIX/share/applications/*
     PREFIX/share/icons/*
     PREFIX/share/man/*
     PREFIX/share/mime/*
EOF
  if [ "$interactive" ]; then
    printf "$question"
    readYes || { echo "$cancelled" && exit 0 ;}
  fi
  cd "${APPDIR}"
  xargs < "install_manifest.txt" -I '%%%' cp -P --parents '%%%' -t "$prefix" \
    || errorMsg "Could not copy files" fatal
  updateCache "$prefix" || errorMsg "Had trouble updating resource caches"
  cd "$prefix"
  echo "Resources installed to '$PWD'."
cat <<EOF
Step 2 of 3.
MuseScore is at: ${APPIMAGE}
EOF
  if [ ! "$interactive" ]; then
    echo "Moving it to 'PREFIX/bin'."
    mkdir -p "${bin}" && cp -r "${APPIMAGE}" -t "${bin}/" \
    && APPIMAGE="${bin}/$(basename "${APPIMAGE}")" \
    || errorMsg "Couldn't move to '${bin}'"
  else
cat <<EOF
You can leave it there, but it is recommended that you move it to 'PREFIX/bin'.
Options: move (m) or copy (c) MuseScore, do nothing (n), or show help (h)?
EOF
    local answer
    while [ true ]; do
      read answer
      case "${answer[0]}" in
        N|n )
          break
          ;;
        M|C|m|c )
          mkdir -p "${bin}" && cp -r "${APPIMAGE}" -t "${bin}/" \
          && APPIMAGE="${bin}/$(basename "${APPIMAGE}")" \
          || errorMsg "Couldn't move to '${bin}'"
          break
          ;;
        H|h )
less <<EOF
Moving or copying MuseScore to 'PREFIX/bin' has these benefits:

 1) It will have the same permissions as the resources. (I.e. it will be
    available to the same user(s) as the resources were installed for.)

 2) If 'PREFIX/bin' is in your PATH environment variable then you can launch
    MuseScore by typing '$(basename ${APPIMAGE})' instead of the full path.
    The default locations for PREFIX are in PATH on most systems.

You should move rather than copy unless you want to keep another copy at the
current location. (E.g if you're installing MuseScore from a USB stick.)

If you choose not to move or copy MuseScore then you will still be able to
launch it by clicking on its icon or by typing the full path

  '${APPIMAGE}'

but it is not guaranteed to be available to the same users as the resources are.
EOF
          ;;
      esac
      echo "Please enter 'm', 'c', 'n', or 'h'. ('h' shows help)"
    done
  fi
  sed -ri "s|^Exec=[^#%]*(.*)\$|Exec=${APPIMAGE} \1|" "share/applications/mscore@MSCORE_INSTALL_SUFFIX@.desktop"
  echo "Finished installing MuseScore to $prefix"
cat <<EOF
Step 3 of 3.
Symlinks can be created to make it easier to launch MuseScore from
the command line. (Symlinks are like shortcuts or aliases.)
EOF
  [ "$interactive" ] && printf "Create symlinks 'mscore@MSCORE_INSTALL_SUFFIX@' and 'musescore@MSCORE_INSTALL_SUFFIX@' [Y/n]?"
  if [ ! "$interactive" ] || readYes ; then
    cd bin
    ln -s "$(basename ${APPIMAGE})" mscore@MSCORE_INSTALL_SUFFIX@
    ln -s "$(basename ${APPIMAGE})" musescore@MSCORE_INSTALL_SUFFIX@
  fi
  if [ ! "$(which "$(basename "${APPIMAGE}")")" ]; then
cat <<EOF
INFORMATION: MuseScore is not in PATH. If you want to run MuseScore from
the command line you will have to type the full file path, like this:

  ${APPIMAGE}

This does not affect you if you launch MuseScore by clicking on the icon.
EOF
  fi
}

function removeResources() {
  [ "$1" == "-i" ] && shift # ignore option. Remove is always interactive
  if [ "$1" != "" ]; then
    # User specified a directory
    prefix="$1"
    echo -n "Remove resources from ${prefix} [Y/n]?"
  elif [ "${EUID}" == "0" ]; then
    prefix=/usr/local
    echo -n "Running as root. Remove MuseScore resources from '$prefix' for all users [Y/n]?"
  else
    prefix=~/.local
    echo -n "Not running as root. Remove MuseScore resources from '$prefix' for current user only [Y/n]?"
  fi
  readYes || return 0
  cd "$prefix" && <"${APPDIR}/install_manifest.txt" xargs rm || return 1
  rm "$(dirname "${APPIMAGE}")/mscore@MSCORE_INSTALL_SUFFIX@"
  rm "$(dirname "${APPIMAGE}")/musescore@MSCORE_INSTALL_SUFFIX@"
  <"${APPDIR}/install_manifest.txt" xargs "${APPDIR}/bin/rm-empty-dirs"
  updateCache $prefix
  echo -ne "Resources removed from ${PWD}.\nRemove MuseScore itself (delete ${APPIMAGE}) [Y/n]?"
  readYes || { echo -e "MuseScore remains at ${APPIMAGE}.\nYou may delete it yourself or install again at any time." && return 0 ; }
  rm "${APPIMAGE}" && echo "Successfully removed MuseScore from $prefix"
  rmdir "$(dirname "${APPIMAGE}")"
  return 0
}

function checkDependencies() {
  export LC_ALL=C # Using `sort` a lot. Order depends on locale so override it.
  tmp="$(mktemp -d)"
  cd "${APPDIR}"
  find . -executable -type f \! -name "lib*.so*" > "${tmp}/exes.txt"
  find . -name "lib*.so*"    \! -type l          > "${tmp}/libs.txt"

  num_exes=$(<"${tmp}/exes.txt" xargs -n1 basename 2>/dev/null | tee "${tmp}/exes2.txt" | wc -l)
  num_libs=$(<"${tmp}/libs.txt" xargs -n1 basename 2>/dev/null | tee "${tmp}/libs2.txt" | wc -l)

  echo "AppImage contains ${num_exes} executables and ${num_libs} libraries." >&2

  if [ "$1" == "exes-only" ]; then
    echo "Checking dependencies for executables..." >&2
    include_libs=""
    num_includes="${num_exes}"
  else
    echo "Checking dependencies for executables and libraries..." >&2
    include_libs="${tmp}/libs.txt"
    num_includes="$((${num_libs}+${num_exes}))"
  fi

  # Check dependencies against system. See 'checkFile()' function.
  export -f checkFile && echo 0 > "${tmp}/.counter"
  cat "${tmp}/exes.txt" "${include_libs}" | xargs -n1 -I '%%%' bash -c \
    'checkFile "${0}" "${1}" "${2}"' "%%%" "${tmp}" "${num_includes}" \; \
    | sort | uniq > "${tmp}/deps.txt"
  echo "Processing results." >&2

  mv "${tmp}/libs2.txt" "${tmp}/libs.txt"
  mv "${tmp}/exes2.txt" "${tmp}/exes.txt"

  # Have only checked system libraries. Now consider those in package:
  <"${tmp}/libs.txt" xargs -n1 -I '%%%' sed -i 's|^%%% => not found$|%%% => package|' "${tmp}/deps.txt"
  <"${tmp}/libs.txt" xargs -n1 -I '%%%' sed -i 's|%%%$|%%% => both|' "${tmp}/deps.txt"

  # Remaining dependencies must be system:
  sed -ri 's/^(.*[^(not found|package|both)])$/\1 => system/' "${tmp}/deps.txt"

  # TODO: Might want to ignore some indirect dependencies. E.g. MuseScore depends on libX.so,
  # and libX.so depends on libY.so, but MuseScore doesn't need any features from libY.so.

  num_package=$(prepResult "${tmp}/deps.txt" ' => package$'   "${tmp}/package.txt")
  num_system=$(prepResult  "${tmp}/deps.txt" ' => system$'    "${tmp}/system.txt")
  num_both=$(prepResult    "${tmp}/deps.txt" ' => both$'      "${tmp}/both.txt")
  num_neither=$(prepResult "${tmp}/deps.txt" ' => not found$' "${tmp}/neither.txt")

  # Any libraries included in package that don't appear in 'deps.txt' **might**
  # not actually be needed. Careful: they might be needed by a plugin!
  num_extra=$(<"${tmp}/libs.txt" xargs -n1 -I '%%%' sh -c \
          "grep -q '%%%' \"${tmp}/deps.txt\" || echo '%%%'" \
           | sort -f | tee "${tmp}/extra.txt" | wc -l)

  cat <(echo "# Package:" && printVersion && printf "\n# System:\n" && uname -srmo) /etc/*release* \
      <(printf "\n# In package only: ${num_package}\n") "${tmp}/package.txt" \
      <(printf "\n# System only: ${num_system}\n")      "${tmp}/system.txt" \
      <(printf "\n# Provided by both: ${num_both}\n")   "${tmp}/both.txt" \
      <(printf "\n# Provided by neither: ${num_neither}\n") "${tmp}/neither.txt" \
      <(printf "\n# Extra: (in package but unlinked. Possibly needed by plugins) ${num_extra}\n") "${tmp}/extra.txt" \
    | less

  rm -r "${tmp}"
}

# PROBLEM: We want to check dependecies provided by the system, but `ldd` looks
# in the current directory first so will find the other package libraries first.
# SOLUTION: Copy lib to tmp and test it there, delete and repeat with next, etc.
function checkFile() {
  counter=$(($(cat "$2/.counter")+1)) && echo ${counter} > "$2/.counter"
  printf "Done ${counter} of $3.\r" >&2
  cp "$1" "$2"
  file="$(basename "$1")"
  LANG=C LD_LIBRARY_PATH="" bin/ldd-recursive -uniq "$2/${file}" 2>/dev/null
  rm "$2/${file}"
}

function prepResult() {
  sed -n "s|$2||p" "$1" | sort -f | tee "$3" | wc -l
}

function updateCache() {
  local ret=0
  update-mime-database "$1/share/mime" ; ret=$(($ret+$?))
  gtk-update-icon-cache -f -t "$1/share/icons/hicolor" ; ret=$(($ret+$?))
  update-desktop-database "$1/share/applications"; ret=$(($ret+$?))
  return $ret
}

function errorMsg() {
cat <<EOF
$1. Things to check:
  - do the files and/or directories exist?
  - do you have the right privileges?
EOF
[ "$2" == "fatal" ] && echo "Error: $1. Terminating." && exit 1
}

main "$@" || exit 1