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
|