diff --git a/create_appimage.sh b/create_appimage.sh index 5cf187f..9b80ac3 100755 --- a/create_appimage.sh +++ b/create_appimage.sh @@ -3,8 +3,7 @@ if [[ "$BUILDARCH" == "x64" ]]; then # install a dep needed for this process sudo apt-get install desktop-file-utils - # download pkg2appimage from github - curl -LO "https://github.com/AppImage/pkg2appimage/raw/master/pkg2appimage" + cd .. - bash -e pkg2appimage ../VSCodium-AppImage-Recipe.yml + bash -e src/resources/linux/appimage/pkg2appimage VSCodium-AppImage-Recipe.yml fi diff --git a/src/resources/linux/appimage/AppRun b/src/resources/linux/appimage/AppRun new file mode 100644 index 0000000..95f908a --- /dev/null +++ b/src/resources/linux/appimage/AppRun @@ -0,0 +1,11 @@ +#!/bin/sh +HERE="$(dirname "$(readlink -f "${0}")")" +export UNION_PRELOAD="${HERE}" +export PATH="${HERE}"/usr/bin/:"${HERE}"/usr/sbin/:"${HERE}"/usr/games/:"${HERE}"/bin/:"${HERE}"/sbin/:"${PATH}" +export LD_LIBRARY_PATH="${HERE}"/usr/lib/:"${HERE}"/usr/lib/i386-linux-gnu/:"${HERE}"/usr/lib/x86_64-linux-gnu/:"${HERE}"/usr/lib32/:"${HERE}"/usr/lib64/:"${HERE}"/lib/:"${HERE}"/lib/i386-linux-gnu/:"${HERE}"/lib/x86_64-linux-gnu/:"${HERE}"/lib32/:"${HERE}"/lib64/:"${LD_LIBRARY_PATH}" +export XDG_DATA_DIRS="${HERE}"/usr/share/:"${XDG_DATA_DIRS}" +export PERLLIB="${HERE}"/usr/share/perl5/:"${HERE}"/usr/lib/perl5/:"${PERLLIB}" +export GSETTINGS_SCHEMA_DIR="${HERE}"/usr/share/glib-2.0/schemas/:"${GSETTINGS_SCHEMA_DIR}" +export QT_PLUGIN_PATH="${HERE}"/usr/lib/qt4/plugins/:"${HERE}"/usr/lib/i386-linux-gnu/qt4/plugins/:"${HERE}"/usr/lib/x86_64-linux-gnu/qt4/plugins/:"${HERE}"/usr/lib32/qt4/plugins/:"${HERE}"/usr/lib64/qt4/plugins/:"${HERE}"/usr/lib/qt5/plugins/:"${HERE}"/usr/lib/i386-linux-gnu/qt5/plugins/:"${HERE}"/usr/lib/x86_64-linux-gnu/qt5/plugins/:"${HERE}"/usr/lib32/qt5/plugins/:"${HERE}"/usr/lib64/qt5/plugins/:"${QT_PLUGIN_PATH}" +EXEC=$(grep -e '^Exec=.*' "${HERE}"/*.desktop | head -n 1 | cut -d "=" -f 2- | sed -e 's|%.||g') +exec ${EXEC} "$@" \ No newline at end of file diff --git a/src/resources/linux/appimage/functions.sh b/src/resources/linux/appimage/functions.sh new file mode 100644 index 0000000..f7fc19b --- /dev/null +++ b/src/resources/linux/appimage/functions.sh @@ -0,0 +1,349 @@ +# This file is supposed to be sourced by each Recipe +# that wants to use the functions contained herein +# like so: +# wget -q https://github.com/AppImage/AppImages/raw/${PKG2AICOMMIT}/functions.sh -O ./functions.sh +# . ./functions.sh + +# RECIPE=$(realpath "$0") + +# Specify a certain commit if you do not want to use master +# by using: +# export PKG2AICOMMIT=<git sha> +if [ -z "$PKG2AICOMMIT" ] ; then + PKG2AICOMMIT=master +fi + +# Options for apt-get to use local files rather than the system ones +OPTIONS="-o Debug::NoLocking=1 +-o APT::Cache-Limit=125829120 +-o Dir::Etc::sourcelist=./sources.list +-o Dir::State=./tmp +-o Dir::Cache=./tmp +-o Dir::State::status=./status +-o Dir::Etc::sourceparts=- +-o APT::Get::List-Cleanup=0 +-o APT::Get::AllowUnauthenticated=1 +-o Debug::pkgProblemResolver=true +-o Debug::pkgDepCache::AutoInstall=true +-o APT::Install-Recommends=0 +-o APT::Install-Suggests=0 +" + +# Detect if we are running inside Docker +grep docker /proc/1/cgroup >/dev/null && export DOCKER_BUILD=1 || true + +# Detect system architecture to know which binaries of AppImage tools +# should be downloaded and used. +case "$(uname -i)" in + x86_64|amd64) +# echo "x86-64 system architecture" + SYSTEM_ARCH="x86_64";; + i?86) +# echo "x86 system architecture" + SYSTEM_ARCH="i686";; +# arm*) +# echo "ARM system architecture" +# SYSTEM_ARCH="";; + unknown|AuthenticAMD|GenuineIntel) +# uname -i not answer on debian, then: + case "$(uname -m)" in + x86_64|amd64) +# echo "x86-64 system architecture" + SYSTEM_ARCH="x86_64";; + i?86) +# echo "x86 system architecture" + SYSTEM_ARCH="i686";; + esac ;; + *) + echo "Unsupported system architecture" + exit 1;; +esac + +# Either get the file from remote or from a static place. +# critical for builds without network access like in Open Build Service +cat_file_from_url() +{ + cat_excludelist="wget -q $1 -O -" + [ -e "$STATIC_FILES/${1##*/}" ] && cat_excludelist="cat $STATIC_FILES/${1##*/}" + $cat_excludelist +} + +git_pull_rebase_helper() +{ + git reset --hard HEAD + git pull +} + +# Patch /usr to ././ in ./usr +# to make the contents of usr/ relocateable +# (this requires us to cd ./usr before running the application; AppRun does that) +patch_usr() +{ + find usr/ -type f -executable -exec sed -i -e "s|/usr|././|g" {} \; +} + +# Download AppRun and make it executable +get_apprun() +{ + cp ${HERE}/AppRun . + chmod a+x AppRun +} + +# Copy the library dependencies of all exectuable files in the current directory +# (it can be beneficial to run this multiple times) +copy_deps() +{ + PWD=$(readlink -f .) + FILES=$(find . -type f -executable -or -name *.so.* -or -name *.so | sort | uniq ) + for FILE in $FILES ; do + ldd "${FILE}" | grep "=>" | awk '{print $3}' | xargs -I '{}' echo '{}' >> DEPSFILE + done + DEPS=$(cat DEPSFILE | sort | uniq) + for FILE in $DEPS ; do + if [ -e $FILE ] && [[ $(readlink -f $FILE)/ != $PWD/* ]] ; then + cp -v --parents -rfL $FILE ./ || true + fi + done + rm -f DEPSFILE +} + +# Move ./lib/ tree to ./usr/lib/ +move_lib() +{ + mkdir -p ./usr/lib ./lib && find ./lib/ -exec cp -v --parents -rfL {} ./usr/ \; && rm -rf ./lib + mkdir -p ./usr/lib ./lib64 && find ./lib64/ -exec cp -v --parents -rfL {} ./usr/ \; && rm -rf ./lib64 +} + +# Delete blacklisted files +delete_blacklisted() +{ + BLACKLISTED_FILES=$(cat_file_from_url https://github.com/AppImage/pkg2appimage/raw/${PKG2AICOMMIT}/excludelist | sed 's|#.*||g') + echo $BLACKLISTED_FILES + for FILE in $BLACKLISTED_FILES ; do + FILES="$(find . -name "${FILE}" -not -path "./usr/optional/*")" + for FOUND in $FILES ; do + rm -vf "$FOUND" "$(readlink -f "$FOUND")" + done + done + + # Do not bundle developer stuff + rm -rf usr/include || true + rm -rf usr/lib/cmake || true + rm -rf usr/lib/pkgconfig || true + find . -name '*.la' | xargs -i rm {} +} + +# Echo highest glibc version needed by the executable files in the current directory +glibc_needed() +{ + find . -name *.so -or -name *.so.* -or -type f -executable -exec strings {} \; | grep ^GLIBC_2 | sed s/GLIBC_//g | sort --version-sort | uniq | tail -n 1 + # find . -name *.so -or -name *.so.* -or -type f -executable -exec readelf -s '{}' 2>/dev/null \; | sed -n 's/.*@GLIBC_//p'| awk '{print $1}' | sort --version-sort | tail -n 1 +} +# Add desktop integration +# Usage: get_desktopintegration name_of_desktop_file_and_exectuable +get_desktopintegration() +{ + # REALBIN=$(grep -o "^Exec=.*" *.desktop | sed -e 's|Exec=||g' | cut -d " " -f 1 | head -n 1) + # cat_file_from_url https://raw.githubusercontent.com/AppImage/AppImageKit/deprecated/AppImageAssistant/desktopintegration > ./usr/bin/$REALBIN.wrapper + # chmod a+x ./usr/bin/$REALBIN.wrapper + echo "The desktopintegration script is deprecated. Please advise users to use https://github.com/AppImage/appimaged instead." + # sed -i -e "s|^Exec=$REALBIN|Exec=$REALBIN.wrapper|g" $1.desktop +} + +# Generate AppImage; this expects $ARCH, $APP and $VERSION to be set +generate_appimage() +{ + # Download AppImageAssistant + URL="https://github.com/AppImage/AppImageKit/releases/download/6/AppImageAssistant_6-${SYSTEM_ARCH}.AppImage" + wget -c "$URL" -O AppImageAssistant + chmod a+x ./AppImageAssistant + + # if [[ "$RECIPE" == *ecipe ]] ; then + # echo "#!/bin/bash -ex" > ./$APP.AppDir/Recipe + # echo "# This recipe was used to generate this AppImage." >> ./$APP.AppDir/Recipe + # echo "# See http://appimage.org for more information." >> ./$APP.AppDir/Recipe + # echo "" >> ./$APP.AppDir/Recipe + # cat $RECIPE >> ./$APP.AppDir/Recipe + # fi + # + # Detect the architecture of what we are packaging. + # The main binary could be a script, so let's use a .so library + BIN=$(find . -name *.so* -type f | head -n 1) + INFO=$(file "$BIN") + if [ -z $ARCH ] ; then + if [[ $INFO == *"x86-64"* ]] ; then + ARCH=x86_64 + elif [[ $INFO == *"i686"* ]] ; then + ARCH=i686 + elif [[ $INFO == *"armv6l"* ]] ; then + ARCH=armhf + else + echo "Could not automatically detect the architecture." + echo "Please set the \$ARCH environment variable." + exit 1 + fi + fi + + mkdir -p ../out || true + rm ../out/$APP"-"$VERSION".glibc"$GLIBC_NEEDED"-"$ARCH".AppImage" 2>/dev/null || true + GLIBC_NEEDED=$(glibc_needed) + ./AppImageAssistant ./$APP.AppDir/ ../out/$APP"-"$VERSION".glibc"$GLIBC_NEEDED"-"$ARCH".AppImage" +} + +# Generate AppImage type 2 +# Additional parameters given to this routine will be passed on to appimagetool +# +# If the environment variable NO_GLIBC_VERSION is set, the required glibc version +# will not be added to the AppImage filename +generate_type2_appimage() +{ + # Get the ID of the last successful build on Travis CI + # ID=$(wget -q https://api.travis-ci.org/repos/AppImage/appimagetool/builds -O - | head -n 1 | sed -e 's|}|\n|g' | grep '"result":0' | head -n 1 | sed -e 's|,|\n|g' | grep '"id"' | cut -d ":" -f 2) + # Get the transfer.sh URL from the logfile of the last successful build on Travis CI + # Only Travis knows why build ID and job ID don't match and why the above doesn't give both... + # URL=$(wget -q "https://s3.amazonaws.com/archive.travis-ci.org/jobs/$((ID+1))/log.txt" -O - | grep "https://transfer.sh/.*/appimagetool" | tail -n 1 | sed -e 's|\r||g') + # if [ -z "$URL" ] ; then + # URL=$(wget -q "https://s3.amazonaws.com/archive.travis-ci.org/jobs/$((ID+2))/log.txt" -O - | grep "https://transfer.sh/.*/appimagetool" | tail -n 1 | sed -e 's|\r||g') + # fi + if [ -z "$(which appimagetool)" ] ; then + URL="https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-${SYSTEM_ARCH}.AppImage" + wget -c "$URL" -O appimagetool + chmod a+x ./appimagetool + appimagetool=$(readlink -f appimagetool) + else + appimagetool=$(which appimagetool) + fi + if [ "$DOCKER_BUILD" ]; then + appimagetool_tempdir=$(mktemp -d) + mv appimagetool "$appimagetool_tempdir" + pushd "$appimagetool_tempdir" &>/dev/null + ls -al + ./appimagetool --appimage-extract + rm appimagetool + appimagetool=$(readlink -f squashfs-root/AppRun) + popd &>/dev/null + _appimagetool_cleanup() { [ -d "$appimagetool_tempdir" ] && rm -r "$appimagetool_tempdir"; } + trap _appimagetool_cleanup EXIT + fi + + if [ -z ${NO_GLIBC_VERSION+true} ]; then + GLIBC_NEEDED=$(glibc_needed) + VERSION_EXPANDED=$VERSION.glibc$GLIBC_NEEDED + else + VERSION_EXPANDED=$VERSION + fi + + set +x + GLIBC_NEEDED=$(glibc_needed) + if ( [ ! -z "$KEY" ] ) && ( ! -z "$TRAVIS" ) ; then + wget https://github.com/AppImage/AppImageKit/files/584665/data.zip -O data.tar.gz.gpg + ( set +x ; echo $KEY | gpg2 --batch --passphrase-fd 0 --no-tty --skip-verify --output data.tar.gz --decrypt data.tar.gz.gpg ) + tar xf data.tar.gz + sudo chown -R $USER .gnu* + mv $HOME/.gnu* $HOME/.gnu_old ; mv .gnu* $HOME/ + VERSION=$VERSION_EXPANDED "$appimagetool" $@ -n -s --bintray-user $BINTRAY_USER --bintray-repo $BINTRAY_REPO -v ./$APP.AppDir/ + else + VERSION=$VERSION_EXPANDED "$appimagetool" $@ -n --bintray-user $BINTRAY_USER --bintray-repo $BINTRAY_REPO -v ./$APP.AppDir/ + fi + set -x + mkdir -p ../out/ || true + mv *.AppImage* ../out/ +} + +# Generate status file for use by apt-get; assuming that the recipe uses no newer +# ingredients than what would require more recent dependencies than what we assume +# to be part of the base system +generate_status() +{ + mkdir -p ./tmp/archives/ + mkdir -p ./tmp/lists/partial + touch tmp/pkgcache.bin tmp/srcpkgcache.bin + if [ -e "${HERE}/usr/share/pkg2appimage/excludedeblist" ] ; then + EXCLUDEDEBLIST="${HERE}/usr/share/pkg2appimage/excludedeblist" + else + wget -q -c "https://github.com/AppImage/AppImages/raw/${PKG2AICOMMIT}/excludedeblist" + EXCLUDEDEBLIST=excludedeblist + fi + rm status 2>/dev/null || true + for PACKAGE in $(cat excludedeblist | cut -d "#" -f 1) ; do + printf "Package: $PACKAGE\nStatus: install ok installed\nArchitecture: all\nVersion: 9:999.999.999\n\n" >> status + done +} + +# Find the desktop file and copy it to the AppDir +get_desktop() +{ + find usr/share/applications -iname "*${LOWERAPP}.desktop" -exec cp {} . \; || true +} + +fix_desktop() { + # fix trailing semicolons + for key in Actions Categories Implements Keywords MimeType NotShowIn OnlyShowIn; do + sed -i '/'"$key"'.*[^;]$/s/$/;/' $1 + done +} + +# Find the icon file and copy it to the AppDir +get_icon() +{ + find ./usr/share/pixmaps/$LOWERAPP.png -exec cp {} . \; 2>/dev/null || true + find ./usr/share/icons -path *64* -name $LOWERAPP.png -exec cp {} . \; 2>/dev/null || true + find ./usr/share/icons -path *128* -name $LOWERAPP.png -exec cp {} . \; 2>/dev/null || true + find ./usr/share/icons -path *512* -name $LOWERAPP.png -exec cp {} . \; 2>/dev/null || true + find ./usr/share/icons -path *256* -name $LOWERAPP.png -exec cp {} . \; 2>/dev/null || true + ls -lh $LOWERAPP.png || true +} + +# Find out the version +get_version() +{ + THEDEB=$(find ../*.deb -name $LOWERAPP"_*" | head -n 1) + if [ -z "$THEDEB" ] ; then + echo "Version could not be determined from the .deb; you need to determine it manually" + fi + VERSION=$(echo $THEDEB | cut -d "~" -f 1 | cut -d "_" -f 2 | cut -d "-" -f 1 | sed -e 's|1%3a||g' | sed -e 's|.dfsg||g' ) + echo $VERSION +} + +# transfer.sh +transfer() { if [ $# -eq 0 ]; then echo "No arguments specified. Usage:\necho transfer /tmp/test.md\ncat /tmp/test.md | transfer test.md"; return 1; fi +tmpfile=$( mktemp -t transferXXX ); if tty -s; then basefile=$(basename "$1" | sed -e 's/[^a-zA-Z0-9._-]/-/g'); curl --progress-bar --upload-file "$1" "https://transfer.sh/$basefile" >> $tmpfile; else curl --progress-bar --upload-file "-" "https://transfer.sh/$1" >> $tmpfile ; fi; cat $tmpfile; rm -f $tmpfile; } + +# Patch binary files; fill with padding if replacement is shorter than original +# http://everydaywithlinux.blogspot.de/2012/11/patch-strings-in-binary-files-with-sed.html +# Example: patch_strings_in_file foo "/usr/local/lib/foo" "/usr/lib/foo" +patch_strings_in_file() { + local FILE="$1" + local PATTERN="$2" + local REPLACEMENT="$3" + # Find all unique strings in FILE that contain the pattern + STRINGS=$(strings ${FILE} | grep ${PATTERN} | sort -u -r) + if [ "${STRINGS}" != "" ] ; then + echo "File '${FILE}' contain strings with '${PATTERN}' in them:" + for OLD_STRING in ${STRINGS} ; do + # Create the new string with a simple bash-replacement + NEW_STRING=${OLD_STRING//${PATTERN}/${REPLACEMENT}} + # Create null terminated ASCII HEX representations of the strings + OLD_STRING_HEX="$(echo -n ${OLD_STRING} | xxd -g 0 -u -ps -c 256)00" + NEW_STRING_HEX="$(echo -n ${NEW_STRING} | xxd -g 0 -u -ps -c 256)00" + if [ ${#NEW_STRING_HEX} -le ${#OLD_STRING_HEX} ] ; then + # Pad the replacement string with null terminations so the + # length matches the original string + while [ ${#NEW_STRING_HEX} -lt ${#OLD_STRING_HEX} ] ; do + NEW_STRING_HEX="${NEW_STRING_HEX}00" + done + # Now, replace every occurrence of OLD_STRING with NEW_STRING + echo -n "Replacing ${OLD_STRING} with ${NEW_STRING}... " + hexdump -ve '1/1 "%.2X"' ${FILE} | \ + sed "s/${OLD_STRING_HEX}/${NEW_STRING_HEX}/g" | \ + xxd -r -p > ${FILE}.tmp + chmod --reference ${FILE} ${FILE}.tmp + mv ${FILE}.tmp ${FILE} + echo "Done!" + else + echo "New string '${NEW_STRING}' is longer than old" \ + "string '${OLD_STRING}'. Skipping." + fi + done + fi +} diff --git a/src/resources/linux/appimage/pkg2appimage b/src/resources/linux/appimage/pkg2appimage new file mode 100644 index 0000000..b3596a3 --- /dev/null +++ b/src/resources/linux/appimage/pkg2appimage @@ -0,0 +1,471 @@ +#!/usr/bin/env bash + +# env + +HERE="$(dirname "$(readlink -f "${0}")")" + +# Use privately bundled apt-get and dpkg-deb if available; can be got on trusty using +# apt download apt libapt-pkg4.12 libbz2-1.0 liblzma5 multiarch-support zlib1g dpkg +if [ -e "${HERE}/libunionpreload.so" ] ; then + export UNION_PRELOAD="${HERE}" + export LD_PRELOAD="${HERE}/libunionpreload.so" + export PATH="${HERE}"/usr/bin/:"${HERE}"/usr/sbin/:"${HERE}"/usr/games/:"${HERE}"/bin/:"${HERE}"/sbin/:"${PATH}" + export LD_LIBRARY_PATH="${HERE}"/usr/lib/:"${HERE}"/usr/lib/i386-linux-gnu/:"${HERE}"/usr/lib/x86_64-linux-gnu/:"${HERE}"/usr/lib32/:"${HERE}"/usr/lib64/:"${HERE}"/lib/:"${HERE}"/lib/i386-linux-gnu/:"${HERE}"/lib/x86_64-linux-gnu/:"${HERE}"/lib32/:"${HERE}"/lib64/:"${LD_LIBRARY_PATH}" + export XDG_DATA_DIRS="${HERE}"/usr/share/:"${XDG_DATA_DIRS}" +fi + +# Specify a certain commit if you do not want to use master +# by using: +# export PKG2AICOMMIT=<git sha> +if [ -z "$PKG2AICOMMIT" ] ; then + PKG2AICOMMIT=master +fi + +usage() { + if [ -z "$APPIMAGE" ] ; then + MYSELF="$0" + else + MYSELF="$APPIMAGE" + fi + echo "usage:" + echo " $MYSELF [--no-di] META-NAME|YAMLFILE" + echo "" + echo "options:" + echo " --di enable legacy desktop integration (unsupported)" + exit 1 +} + +if [ $# -eq 0 ] || [ "x${!#}" = "x--di" ] ; then + usage +fi +if [ $# -eq 2 ] && [ "x$1" != "x--di" ] ; then + usage +fi + +if [ "x$1" = "x--di" ] ; then + ENABLE_DI="yes" +else + ENABLE_DI="no" +fi + +# Halt on errors +set -e +set -x + +# Check dependencies +which wget >/dev/null 2>&1 || ( echo wget missing && exit 1 ) +which grep >/dev/null 2>&1 || ( echo grep missing && exit 1 ) +which sed >/dev/null 2>&1 || ( echo sed missing && exit 1 ) +which cut >/dev/null 2>&1 || ( echo cut missing && exit 1 ) +which file >/dev/null 2>&1 || ( echo file missing && exit 1 ) +which desktop-file-validate >/dev/null 2>&1 || ( echo desktop-file-validate missing && exit 1 ) + +# If the yaml file doesn't exist locally, get it from GitHub +if [ ! -f "${!#}" ] ; then + YAMLFILE=/tmp/_recipe.yml + rm -f "$YAMLFILE" + wget -q "https://github.com/AppImage/AppImages/raw/${PKG2AICOMMIT}/recipes/${!#}.yml" -O "$YAMLFILE" +else + YAMLFILE=$(readlink -f "${!#}") +fi + +# Lightweight bash-only dpkg-scanpackages replacement +scanpackages() { + for deb in *.deb ; do + dpkg -I $deb | sed 's/^ *//g' | grep -i -E '(package|version|installed-size|architecture|depends|priority):' + echo "Filename: $(readlink -f $deb)" + echo "MD5sum: $(md5sum -b $deb | cut -d' ' -f1)" + echo "SHA1: $(sha1sum -b $deb | cut -d' ' -f1)" + echo "SHA256: $(sha256sum -b $deb | cut -d' ' -f1)" + echo + done +} + +# Function to parse yaml +# https://gist.github.com/epiloque/8cf512c6d64641bde388 +# based on https://gist.github.com/pkuczynski/8665367 +parse_yaml() { + local prefix=$2 + local s + local w + local fs + s='[[:blank:]]*' + w='[a-zA-Z0-9_]*' + fs="$(echo @|tr @ '\034')" + sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \ + -e "s|^\($s\)\($w\)$s[:-]$s\(.*\)$s\$|\1$fs\2$fs\3|p" "$1" | + awk -F"$fs" '{ + indent = length($1)/2; + vname[indent] = $2; + for (i in vname) {if (i > indent) {delete vname[i]}} + if (length($3) > 0) { + vn=""; for (i=0; i<indent; i++) {vn=(vn)(vname[i])("_")} + printf("%s%s%s=(\"%s\")\n", "'"$prefix"'",vn, $2, $3); + } + }' | sed 's/_=/+=/g' +} + +# Read yaml file +parse_yaml $YAMLFILE "_" +eval $(parse_yaml $YAMLFILE "_") + +if [ ! -z $_enable_di ]; then + ENABLE_DI="$_enable_di" +fi + +# Execute multiple script lines together as one +# shell_execute filename key_of_group_of_commands +shell_execute() { + if [ -f /tmp/recipe_script ] ; then + rm /tmp/recipe_script + fi + parse_yaml $YAMLFILE "_" | grep "^$2+=" > /tmp/recipe_script + sed -i -e 's|^'$2'+=("||g' /tmp/recipe_script + sed -i -e 's|")$||g' /tmp/recipe_script + bash -ex /tmp/recipe_script + rm /tmp/recipe_script +} + +APP=$_app +LOWERAPP=${APP,,} +if [ ! -z $_lowerapp ] ; then + LOWERAPP=$_lowerapp +fi + +mkdir -p ./$APP/$APP.AppDir/usr/lib +cd ./$APP/ + +if [ -d "./$APP.AppDir/" ] ; then + rm -rf ./$APP.AppDir/ +fi + +# Source the bundled functions.sh if it exists +# in "${HERE}/usr/share/pkg2appimage/functions.sh" +# or source a user-provided functions.sh if the environment +# variable FUNCTIONS_SH was set and the file exists +if [ -e "${HERE}/usr/share/pkg2appimage/functions.sh" ] ; then + . "${HERE}/usr/share/pkg2appimage/functions.sh" +elif [ -z "$FUNCTIONS_SH" ] ; then + if [ ! -e "${HERE}/functions.sh" ] ; then + echo "functions.sh IS NOT FOUND, PLEASE CONTACT CODIUM APPIMAGE MAINTAINER!!!" + fi + cp ${HERE}/functions.sh ./functions.sh + . ./functions.sh +else + if [ -e "$FUNCTIONS_SH" ] ; then + . "$FUNCTIONS_SH" + fi +fi + +# If there is an ARCH environment variable, then use that +# architecture to for apt-get. Not that for the AppImage to be +# operable, we also need to embed a matching AppImage runtime +# and ingredients of that architecture. Debian packages +# should be available for most architectures, e.g., oldstable +# has "armhf" +if [ ! -z $ARCH] ; then + OPTIONS="$OPTIONS -o APT::Architecture=$ARCH" +fi + +if [ ! -z "${_ingredients_ghreleases[0]}" ] ; then + for GHREPO in "${_ingredients_ghreleases[@]}" ; do + wget -q "https://github.com/${GHREPO}/releases/" -O /tmp/gh-release.html + DEB=$(cat /tmp/gh-release.html | grep ".deb" | grep x86_64 | head -n 1 | cut -d '"' -f 2) + if [ -z "$DEB" ] ; then + DEB=$(cat /tmp/gh-release.html | grep ".deb" | grep amd64 | head -n 1 | cut -d '"' -f 2) + fi + if [ -z "$DEB" ] ; then + DEB=$(cat /tmp/gh-release.html | grep ".deb" | grep x64 | head -n 1 | cut -d '"' -f 2) + fi + if [ -z "$DEB" ] ; then + DEB=$(cat /tmp/gh-release.html | grep ".deb" | grep linux64 | head -n 1 | cut -d '"' -f 2) + fi + rm /tmp/gh-release.html + wget -c "https://github.com/${DEB}" + done +fi + +if [ ! -z "${_ingredients_dist}" ] ; then + rm status 2>/dev/null || true + generate_status + + # Some packages depend on packages which we do not want to bundle, + # in addition to the global excludes defined in excludedeblist. + # Use + # ingredients: + # exclude: + # - packagename + if [ ! -z "${_ingredients_exclude[0]}" ] ; then + for PACKAGE in "${_ingredients_exclude[@]}" ; do + printf "Package: $PACKAGE\nStatus: install ok installed\nArchitecture: all\nVersion: 9:999.999.999\n\n" >> status + done + fi + + # Some packages depend on an exact version of a dependency to be installed. + # Use + # ingredients: + # pretend: + # - packagename version_to_be_pretended + if [ ! -z "${_ingredients_pretend[0]}" ] ; then + for PRETEND in "${_ingredients_pretend[@]}" ; do + P_PKG=$(echo "$PRETEND" | cut -d " " -f 1) + P_VER=$(echo "$PRETEND" | cut -d " " -f 2) + cat status | tr '\n' '@' | sed -e 's|@@|\n\n|g' | sed -e 's|Package: '"$P_PKG"'@Status: install ok installed@Architecture: all@Version: 9:999.999.999|Package: '"$P_PKG"'@Status: install ok installed@Architecture: all@Version: '"$P_VER"'|g' | sed -e 's|@|\n|g' > status.temp + mv status.temp status + done + fi + + if [ -e sources.list ] ; then + rm sources.list + fi + for SOURCE in "${_ingredients_sources[@]}" ; do + echo "${SOURCE}" >> sources.list + done + for PPA in "${_ingredients_ppas[@]}" ; do + echo "deb http://ppa.launchpad.net/${PPA}/ubuntu ${_ingredients_dist} main" >> sources.list + done + for DEBFILE in "${_ingredients_debs[@]}" ; do + cp ${DEBFILE} . + done + # Use libcurl-slim to reduce AppImage size, thanks darealshinji + # Not really compiled on xenial but CentOS 6, https://github.com/AppImage/AppImages/issues/187 + echo "deb http://ppa.launchpad.net/djcj/libcurl-slim/ubuntu xenial main" >> sources.list + # Use gnutls-patched to have libgnutls look in various distributions' places for certificates, + # https://github.com/darealshinji/vlc-AppImage/issues/1#issuecomment-321041496 + # echo "deb http://ppa.launchpad.net/djcj/gnutls-patched/ubuntu ${_ingredients_dist} main" >> sources.list + ### echo "deb http://ppa.launchpad.net/djcj/gnutls-patched/ubuntu trusty main" >> sources.list # https://github.com/AppImage/pkg2appimage/issues/345 +fi + +if [ ! -z "${_ingredients_script[0]}" ] ; then + # Execute extra steps defined in recipe + shell_execute $YAMLFILE _ingredients_script +fi + +if [ ! -z "${_ingredients_dist}" ] ; then + # Some projects provide raw .deb files without a repository + # hence we create our own local repository as part of + # the AppImage creation process in order to "install" + # the package using apt-get as normal + if [ ! -z "${_ingredients_debs[0]}" ] ; then + for DEB in "${_ingredients_debs[@]}" ; do + if [ ! -f $(basename "$DEB") ] ; then + wget -c $DEB + fi + done + fi + scanpackages | gzip -9c > Packages.gz + echo "deb file:$(readlink -e $PWD) ./" >> sources.list + + INSTALL=$LOWERAPP + if [ ! -z "${_ingredients_package}" ] ; then + INSTALL="${_ingredients_package}" + fi + if [ ! -z "${_ingredients_packages}" ] ; then + INSTALL="" + fi + + # If packages are specifically listed, only install these, not a package with the name of the app + if [ ! -z "${_ingredients_packages[0]}" ] ; then + INSTALL=${_ingredients_packages[@]} + fi + + apt-get -o Acquire::AllowInsecureRepositories=true -o Acquire::Languages="none" -o Acquire::AllowDowngradeToInsecureRepositories=true $OPTIONS update || true + URLS=$(apt-get --allow-unauthenticated -o Apt::Get::AllowUnauthenticated=true $OPTIONS -y install --print-uris $INSTALL | cut -d "'" -f 2 | grep -e "^http") || true + if which aria2c &>/dev/null; then + dltool=aria2c + else + dltool=wget + fi + + $dltool -c -i- <<<"$URLS" +fi + +if [ ! -z "${_ingredients_post_script[0]}" ] ; then + # Execute extra steps defined in recipe + shell_execute $YAMLFILE _ingredients_post_script +fi + +mkdir -p ./$APP.AppDir/ +cd ./$APP.AppDir/ + +mkdir -p usr/bin usr/lib +find ../*.deb -exec dpkg-deb -X {} . \; || true + +unset LD_PRELOAD + +# Try to copy icons to standard locations where appimaged can pick them up +mkdir -p usr/share/icons/hicolor/{22x22,24x24,32x32,48x48,64x64,128x128,256x256,512x512}/apps/ +find . -path *icons* -path *22* -name "*$LOWERAPP*" -exec cp {} usr/share/icons/hicolor/22x22/apps/ \; || true +find . -path *icons* -path *24* -name "*$LOWERAPP*" -exec cp {} usr/share/icons/hicolor/24x24/apps/ \; || true +find . -path *icons* -path *32* -name "*$LOWERAPP*" -exec cp {} usr/share/icons/hicolor/32x32/apps/ \; || true +find . -path *icons* -path *48* -name "*$LOWERAPP*" -exec cp {} usr/share/icons/hicolor/48x48/apps/ \; || true +find . -path *icons* -path *64* -name "*$LOWERAPP*" -exec cp {} usr/share/icons/hicolor/64x64/apps/ \; || true +find . -path *icons* -path *128* -name "*$LOWERAPP*" -exec cp {} usr/share/icons/hicolor/128x128/apps/ \; || true +find . -path *icons* -path *256* -name "*$LOWERAPP*" -exec cp {} usr/share/icons/hicolor/256x256/apps/ \; || true +find . -path *icons* -path *512* -name "*$LOWERAPP*" -exec cp {} usr/share/icons/hicolor/512x512/apps/ \; || true + +get_icon + +if [ -z "${_union}" ] ; then + get_apprun +else +cat > AppRun <<\EOF +#!/bin/sh +HERE="$(dirname "$(readlink -f "${0}")")" +export UNION_PRELOAD="${HERE}" +export LD_PRELOAD="${HERE}/libunionpreload.so" +export PATH="${HERE}"/usr/bin/:"${HERE}"/usr/sbin/:"${HERE}"/usr/games/:"${HERE}"/bin/:"${HERE}"/sbin/:"${PATH}" +export LD_LIBRARY_PATH="${HERE}"/usr/lib/:"${HERE}"/usr/lib/i386-linux-gnu/:"${HERE}"/usr/lib/x86_64-linux-gnu/:"${HERE}"/usr/lib32/:"${HERE}"/usr/lib64/:"${HERE}"/lib/:"${HERE}"/lib/i386-linux-gnu/:"${HERE}"/lib/x86_64-linux-gnu/:"${HERE}"/lib32/:"${HERE}"/lib64/:"${LD_LIBRARY_PATH}" +export PYTHONPATH="${HERE}"/usr/share/pyshared/:"${PYTHONPATH}" +export PYTHONHOME="${HERE}"/usr/ +export XDG_DATA_DIRS="${HERE}"/usr/share/:"${XDG_DATA_DIRS}" +export PERLLIB="${HERE}"/usr/share/perl5/:"${HERE}"/usr/lib/perl5/:"${PERLLIB}" +export GSETTINGS_SCHEMA_DIR="${HERE}"/usr/share/glib-2.0/schemas/:"${GSETTINGS_SCHEMA_DIR}" +export QT_PLUGIN_PATH="${HERE}"/usr/lib/qt4/plugins/:"${HERE}"/usr/lib/i386-linux-gnu/qt4/plugins/:"${HERE}"/usr/lib/x86_64-linux-gnu/qt4/plugins/:"${HERE}"/usr/lib32/qt4/plugins/:"${HERE}"/usr/lib64/qt4/plugins/:"${HERE}"/usr/lib/qt5/plugins/:"${HERE}"/usr/lib/i386-linux-gnu/qt5/plugins/:"${HERE}"/usr/lib/x86_64-linux-gnu/qt5/plugins/:"${HERE}"/usr/lib32/qt5/plugins/:"${HERE}"/usr/lib64/qt5/plugins/:"${QT_PLUGIN_PATH}" +EXEC=$(grep -e '^Exec=.*' "${HERE}"/*.desktop | head -n 1 | cut -d "=" -f 2- | sed -e 's|%.||g') +exec ${EXEC} "$@" +EOF +chmod a+x AppRun +fi + +get_desktop + +# Prevent Qt from loading plugins from the system +unset QTPATH +QTPATH=$(find usr/lib -type d -name qt4 -or -name qt5 | sed -e 's|usr/|../|g') +if [ ! -z $QTPATH ] ; then +cat > usr/bin/qt.conf <<EOF +[Paths] +Prefix = $QTPATH +EOF +fi + +# http://www.mono-project.com/docs/advanced/assemblies-and-the-gac/ +# At runtime, Mono looks in three places for assemblies necessary +# to run a program. It first searches the location of the executing assembly. +# For this to work without setting $MONO_PATH, we need to move the +# main *.exe to usr/lib/mono/exe, because we move all "assemblies" (sic) +# there in this script + +if [ -e usr/lib/mono ] ; then + # Force all so files referenced in config files into LD_LIBRARY_PATH + find . -name "*.dll.config" -exec cat {} > temp \; + # Remove all absolute paths + sed -i -E 's|target=\"\/(.*\/)([a-z0-9].*?)>|target=\"\2>|g' temp + SONAMES=$(cat temp | cut -d '"' -f 4 | grep ".so" || true) + if [ "" != "$SONAMES" ] ; then + for SONAME in $SONAMES; do + find . -name "$SONAME" -exec mv {} usr/lib \; + done + fi + rm temp + PATH_OF_THE_EXE="usr/lib/mono/exe" + mkdir -p "$PATH_OF_THE_EXE" + # Force all dll files into PATH_OF_THE_EXE (or MONO_PATH which we would have to set) + find . -name "*.dll" -and -not -name "mscorlib.dll" -exec mv {} "$PATH_OF_THE_EXE" \; + # Edit all config files in place to remove absolute paths + find . -name "*.dll.config" -exec sed -i -E 's|target=\"\/(.*\/)([a-z0-9].*?)>|target=\"\2>|g' {} \; + # Force all config files into the PATH_OF_THE_EXE (or MONO_PATH which we would have to set) + find . -name "*.dll.config" -exec mv {} "$PATH_OF_THE_EXE" \; + # Remove gac, we are not using it since it is convoluted + rm -rf usr/lib/mono/gac/ +fi + +if [ -d "./usr/lib/x86_64-linux-gnu/gstreamer-1.0/" ] ; then + mv ./usr/lib/x86_64-linux-gnu/gstreamer-1.0/* ./usr/lib/x86_64-linux-gnu/ + rm -r ./usr/lib/x86_64-linux-gnu/gstreamer-1.0 +fi + +if [ -d "./usr/lib/x86_64-linux-gnu/pulseaudio/" ] ; then + mv ./usr/lib/x86_64-linux-gnu/pulseaudio/* ./usr/lib/x86_64-linux-gnu/ + rm -r ./usr/lib/x86_64-linux-gnu/pulseaudio +fi + +# Execute extra steps defined in recipe +if [ ! -z "${_script}" ] ; then + shell_execute $YAMLFILE _script +fi + +DESKTOP=$(find . -name '*.desktop' | sort | head -n 1) + +# desktop-file-validate complains about missing trailing semicolons for some +# keys although the format definition says that they are optional +fix_desktop "$DESKTOP" + +# Some non-distribution provided applications have an absolute +# path in the Exec= line which we remove for relocateability +if [ -z "$DESKTOP" ] ; then + echo "desktop file not found, aborting" + exit 1 +else + desktop-file-validate "$DESKTOP" || exit 1 + ORIG=$(grep -o "^Exec=.*$" "${DESKTOP}" | head -n 1| cut -d " " -f 1) + REPL=$(basename $(grep -o "^Exec=.*$" "${DESKTOP}" | head -n 1 | cut -d " " -f 1 | sed -e 's|Exec=||g')) + sed -i -e 's|'"${ORIG}"'|Exec='"${REPL}"'|g' "${DESKTOP}" +fi + +# Compile GLib schemas if the subdirectory is present in the AppImage +# AppRun has to export GSETTINGS_SCHEMA_DIR for this to work +if [ -d usr/share/glib-2.0/schemas/ ] ; then + ( cd usr/share/glib-2.0/schemas/ ; glib-compile-schemas . ) +fi + +if [ -f ../VERSION ] ; then + VERSION=$(cat ../VERSION) +else + get_version || true +fi + +# patch_usr +# Patching only the executable files seems not to be enough for some apps +if [ ! -z "${_binpatch}" ] ; then + find usr/ -type f -exec sed -i -e 's|/usr|././|g' {} \; + find usr/ -type f -exec sed -i -e 's@././/bin/env@/usr/bin/env@g' {} \; +fi + +# Don't suffer from NIH; use LD_PRELOAD to override calls to /usr paths +if [ ! -z "${_union}" ] ; then + mkdir -p usr/src/ + wget -q "https://raw.githubusercontent.com/mikix/deb2snap/master/src/preload.c" -O - | \ + sed -e 's|SNAPPY|UNION|g' | sed -e 's|SNAPP|UNION|g' | sed -e 's|SNAP|UNION|g' | \ + sed -e 's|snappy|union|g' > usr/src/libunionpreload.c + gcc -shared -fPIC usr/src/libunionpreload.c -o libunionpreload.so -ldl -DUNION_LIBNAME=\"libunionpreload.so\" + strip libunionpreload.so +fi + +delete_blacklisted + +if [ "$ENABLE_DI" = "yes" ] ; then + get_desktopintegration $LOWERAPP +fi + +# Fix desktop files that have file endings for icons +sed -i -e 's|\.png||g' *.desktop || true +sed -i -e 's|\.svg||g' *.desktop || true +sed -i -e 's|\.svgz||g' *.desktop || true +sed -i -e 's|\.xpm||g' *.desktop || true + +# Setting PYTHONHOME instead +# Fix Python imports, +# https://github.com/AppImage/AppImages/issues/172 +# SITECUSTOMIZEFILES=$(find . -name "sitecustomize.py") +# for SITECUSTOMIZEFILE in $SITECUSTOMIZEFILES ; do +# rm $SITECUSTOMIZEFILE # Remove symlinks, replace by files +# cat > $SITECUSTOMIZEFILE <<\EOF +# import sys,os +# if sys.version_info[0] < 3: +# prefix = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(sys.path[0])))) +# sys.path = [ prefix+s for s in sys.path if not s.startswith(prefix) ] +# EOF +# done + +# Execute extra steps defined in recipe +if [ ! -z "${_post_script[0]}" ] ; then + shell_execute $YAMLFILE _post_script +fi + +# Go out of AppImage +cd .. + +generate_type2_appimage +ls -lh ../out/*.AppImage