Browse Source
Generate AppImage on GitHub Actions
Generate AppImage on GitHub Actions
Fixes #695. Supersedes #172. See https://appimage.org/.pull/2/head
12 changed files with 542 additions and 13 deletions
-
2CMakeLists.txt
-
42cmake-proxies/cmake-modules/Package.cmake
-
2help/CMakeLists.txt
-
1help/audacity.appdata.xml
-
49linux/AppRun.sh
-
95linux/check_dependencies.sh
-
113linux/create_appimage.sh
-
205linux/ldd_recursive.pl
-
27linux/package_appimage.cmake
-
14scripts/ci/dependencies.sh
-
3src/CMakeLists.txt
-
2src/audacity.desktop.in
@ -0,0 +1,49 @@ |
|||
#!/usr/bin/env bash |
|||
# The AppImage runtime sets some special environment variables. We provide |
|||
# default values here in case the user tries to run this script outside an |
|||
# AppImage where the variables would otherwise be undefined. |
|||
if [[ ! "${APPIMAGE}" || ! "${APPDIR}" ]]; then |
|||
export APPIMAGE="$(readlink -f "${0}")" |
|||
export APPDIR="$(dirname "${APPIMAGE}")" |
|||
fi |
|||
|
|||
export LD_LIBRARY_PATH="${APPDIR}/lib:${LD_LIBRARY_PATH}" |
|||
|
|||
function help() |
|||
{ |
|||
# Normal audacity help |
|||
"${APPDIR}/bin/audacity" --help |
|||
# Special options handled by this script |
|||
cat >&2 <<EOF |
|||
--readme display README |
|||
--license display LICENSE |
|||
--man[ual|page] display audacity(1) manual page |
|||
--check-dependencies check library dependency fulfillment (developer tool) |
|||
|
|||
EOF |
|||
# Blank line then special options handled by the AppImage runtime |
|||
"${APPIMAGE}" --appimage-help |
|||
} |
|||
|
|||
# Intercept command line arguments |
|||
case "$1" in |
|||
-h|--help ) |
|||
help |
|||
;; |
|||
--readme ) |
|||
exec less "${APPDIR}/share/doc/audacity/README.txt" |
|||
;; |
|||
--license ) |
|||
exec less "${APPDIR}/share/doc/audacity/LICENSE.txt" |
|||
;; |
|||
--man|--manual|--manpage ) |
|||
exec man "${APPDIR}/share/man/man1/audacity.1" |
|||
;; |
|||
--check-depends|--check-dependencies ) |
|||
exec bash "${APPDIR}/bin/check_dependencies" |
|||
;; |
|||
* ) |
|||
# Other arguments go to Audacity |
|||
exec "${APPDIR}/bin/audacity" "$@" |
|||
;; |
|||
esac |
@ -0,0 +1,95 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
version="@CPACK_PACKAGE_NAME@ v@CPACK_PACKAGE_VERSION@ @CPACK_AUDACITY_ARCH_LABEL@" |
|||
|
|||
export LC_ALL=C # Using `sort` a lot. Order depends on locale so override it. |
|||
|
|||
# PROBLEM: We want to check dependencies provided by the system, but `ldd` |
|||
# looks in the current directory first so will show other package libraries. |
|||
# SOLUTION: Copy library to temporary folder and test it there. |
|||
function check_file() |
|||
{ |
|||
counter=$(($(cat "$2/.counter")+1)) |
|||
echo ${counter} > "$2/.counter" |
|||
printf >&2 "Done ${counter} of $3.\r" |
|||
cp "$1" "$2" |
|||
file="$(basename "$1")" |
|||
LANG=C LD_LIBRARY_PATH="" "${APPDIR}/bin/ldd_recursive" -uniq "$2/${file}" 2>/dev/null |
|||
rm "$2/${file}" # delete library before we test the next one |
|||
} |
|||
export -f check_file # make function available in Bash subprocesses |
|||
|
|||
function prep_result() |
|||
{ |
|||
sed -n "s|$2||p" "$1" | sort -f | tee "$3" | wc -l |
|||
} |
|||
|
|||
if ! which less >/dev/null; then |
|||
function less() { cat "$@"; } # use `cat` if `less` is unavailable |
|||
fi |
|||
|
|||
tmp="$(mktemp -d)" |
|||
trap "rm -rf '${tmp}'" EXIT |
|||
|
|||
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 >&2 "AppImage contains ${num_exes} executables and ${num_libs} libraries." |
|||
|
|||
echo >&2 "Checking dependencies for executables and libraries..." |
|||
include_libs="${tmp}/libs.txt" |
|||
num_includes="$((${num_libs}+${num_exes}))" |
|||
|
|||
# Check dependencies against system. See 'check_file' function. |
|||
echo 0 > "${tmp}/.counter" |
|||
cat "${tmp}/exes.txt" "${include_libs}" | xargs -n1 -I '%%%' bash -c \ |
|||
'check_file "${0}" "${1}" "${2}"' "%%%" "${tmp}" "${num_includes}" \; \ |
|||
| sort | uniq > "${tmp}/deps.txt" |
|||
echo >&2 "Processing results." |
|||
|
|||
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" |
|||
|
|||
num_package=$(prep_result "${tmp}/deps.txt" ' => package$' "${tmp}/package.txt") |
|||
num_system=$(prep_result "${tmp}/deps.txt" ' => system$' "${tmp}/system.txt") |
|||
num_both=$(prep_result "${tmp}/deps.txt" ' => both$' "${tmp}/both.txt") |
|||
num_neither=$(prep_result "${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) |
|||
|
|||
less <<EOF |
|||
# Package: ${version} |
|||
# System: $(uname -srmo) |
|||
$(cat /etc/*release*) |
|||
|
|||
# In package only: ${num_package} |
|||
$(cat "${tmp}/package.txt") |
|||
|
|||
# System only: ${num_system} |
|||
$(cat "${tmp}/system.txt") |
|||
|
|||
# Provided by both: ${num_both} |
|||
$(cat "${tmp}/both.txt") |
|||
|
|||
# Provided by neither: ${num_neither} |
|||
$(cat "${tmp}/neither.txt") |
|||
|
|||
# Extra: ${num_extra} (In package but unlinked. Possibly needed by plugins.) |
|||
$(cat "${tmp}/extra.txt") |
|||
EOF |
@ -0,0 +1,113 @@ |
|||
#!/usr/bin/env bash |
|||
|
|||
((${BASH_VERSION%%.*} >= 4)) || { echo >&2 "$0: Error: Please upgrade Bash."; exit 1; } |
|||
|
|||
set -euxo pipefail |
|||
|
|||
readonly appdir="$1" # input path (Audacity install directory) |
|||
readonly appimage="$2" # output path to use for created AppImage |
|||
|
|||
#============================================================================ |
|||
# Helper functions |
|||
#============================================================================ |
|||
|
|||
function download_github_release() |
|||
{ |
|||
local -r repo_slug="$1" release_tag="$2" file="$3" |
|||
wget -q --show-progress "https://github.com/${repo_slug}/releases/download/${release_tag}/${file}" |
|||
chmod +x "${file}" |
|||
} |
|||
|
|||
function extract_appimage() |
|||
{ |
|||
# Extract AppImage so we can run it without having to install FUSE |
|||
local -r image="$1" binary_name="$2" |
|||
local -r dir="${image%.AppImage}.AppDir" |
|||
"./${image}" --appimage-extract >/dev/null # dest folder "squashfs-root" |
|||
mv squashfs-root "${dir}" # rename folder to avoid collisions |
|||
ln -s "${dir}/AppRun" "${binary_name}" # symlink for convenience |
|||
rm -f "${image}" |
|||
} |
|||
|
|||
function download_appimage_release() |
|||
{ |
|||
local -r github_repo_slug="$1" binary_name="$2" tag="$3" |
|||
local -r image="${binary_name}-x86_64.AppImage" |
|||
download_github_release "${github_repo_slug}" "${tag}" "${image}" |
|||
extract_appimage "${image}" "${binary_name}" |
|||
} |
|||
|
|||
function download_linuxdeploy_component() |
|||
{ |
|||
local -r component="$1" tag="$2" |
|||
download_appimage_release "linuxdeploy/$1" "$1" "$2" |
|||
} |
|||
|
|||
function create_path() |
|||
{ |
|||
local -r path="$1" |
|||
if [[ -d "${path}" ]]; then |
|||
return 1 # already exists |
|||
fi |
|||
mkdir -p "${path}" |
|||
} |
|||
|
|||
#============================================================================ |
|||
# Fetch AppImage packaging tools |
|||
#============================================================================ |
|||
|
|||
if create_path "appimagetool"; then |
|||
( |
|||
cd "appimagetool" |
|||
download_appimage_release AppImage/AppImageKit appimagetool continuous |
|||
) |
|||
fi |
|||
export PATH="${PWD%/}/appimagetool:${PATH}" |
|||
appimagetool --version |
|||
|
|||
if create_path "linuxdeploy"; then |
|||
( |
|||
cd "linuxdeploy" |
|||
download_linuxdeploy_component linuxdeploy continuous |
|||
) |
|||
fi |
|||
export PATH="${PWD%/}/linuxdeploy:${PATH}" |
|||
linuxdeploy --list-plugins |
|||
|
|||
#============================================================================ |
|||
# Create symlinks |
|||
#============================================================================ |
|||
|
|||
ln -sf --no-dereference . "${appdir}/usr" |
|||
ln -sf share/applications/audacity.desktop "${appdir}/audacity.desktop" |
|||
ln -sf share/icons/hicolor/scalable/apps/audacity.svg "${appdir}/audacity.svg" |
|||
ln -sf share/icons/hicolor/scalable/apps/audacity.svg "${appdir}/.DirIcon" |
|||
|
|||
#============================================================================ |
|||
# Bundle dependencies |
|||
#============================================================================ |
|||
|
|||
# HACK: Some wxWidget libraries depend on themselves. Add |
|||
# them to LD_LIBRARY_PATH so that linuxdeploy can find them. |
|||
export LD_LIBRARY_PATH="${appdir}/usr/lib/audacity:${LD_LIBRARY_PATH-}" |
|||
|
|||
linuxdeploy --appdir "${appdir}" # add all shared library dependencies |
|||
|
|||
#============================================================================ |
|||
# Build AppImage |
|||
#============================================================================ |
|||
|
|||
appimagetool_args=( |
|||
# none |
|||
) |
|||
|
|||
if [[ "${AUDACITY_UPDATE_INFO-}" ]]; then |
|||
# Enable updates. See https://github.com/AppImage/AppImageSpec/blob/master/draft.md#update-information |
|||
appimagetool_args+=( --updateinformation="${AUDACITY_UPDATE_INFO}" ) |
|||
else |
|||
echo >&2 "$0: Automatic updates disabled" |
|||
fi |
|||
|
|||
# Create AppImage |
|||
cd "$(dirname "${appimage}")" # otherwise zsync created in wrong directory |
|||
appimagetool "${appimagetool_args[@]}" "${appdir}" "${appimage}" |
@ -0,0 +1,205 @@ |
|||
#!/usr/bin/env perl |
|||
|
|||
############################################################################### |
|||
# |
|||
# Written by Igor Ljubuncic (igor.ljubuncic@intel.com) |
|||
# Yuval Nissan (yuval.nissan@intel.com) |
|||
# |
|||
# Version 1.0 on Mar 29, 2011 |
|||
# |
|||
# This program performs recursive ldd checks for binaries and libraries |
|||
# It recurses through entire ldd tree for every listed binary and library |
|||
# It completes when no matches found in the current branch |
|||
# Same limitations to standard ldd apply |
|||
# ldd cannot check libraries with no permissions |
|||
# |
|||
############################################################################### |
|||
|
|||
# /* |
|||
# |
|||
# This file is provided under a dual BSD/GPLv2 license. When using or |
|||
# redistributing this file, you may do so under either license. |
|||
# |
|||
# GPL LICENSE SUMMARY |
|||
# |
|||
# Copyright(c) 2011 Intel Corporation. All rights reserved. |
|||
# |
|||
# This program is free software; you can redistribute it and/or modify |
|||
# it under the terms of version 2 of the GNU General Public License as |
|||
# published by the Free Software Foundation. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, but |
|||
# WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
# General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU General Public License |
|||
# along with this program; if not, write to the Free Software |
|||
# Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
|||
# The full GNU General Public License is included in this distribution |
|||
# in the file called LICENSE.GPL. |
|||
# |
|||
# Contact Information: |
|||
# Igor Ljubuncic, igor.ljubuncic@intel.com |
|||
# P.O.B 1659, MATAM, 31015 Haifa, Israel |
|||
# |
|||
# BSD LICENSE |
|||
# |
|||
# Copyright(c) 2011 Intel Corporation. All rights reserved. |
|||
# All rights reserved. |
|||
# |
|||
# Redistribution and use in source and binary forms, with or without |
|||
# modification, are permitted provided that the following conditions |
|||
# are met: |
|||
# |
|||
# * Redistributions of source code must retain the above copyright |
|||
# notice, this list of conditions and the following disclaimer. |
|||
# * Redistributions in binary form must reproduce the above copyright |
|||
# notice, this list of conditions and the following disclaimer in |
|||
# the documentation and/or other materials provided with the |
|||
# distribution. |
|||
# * Neither the name of Intel Corporation nor the names of its |
|||
# contributors may be used to endorse or promote products derived |
|||
# from this software without specific prior written permission. |
|||
# |
|||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
# |
|||
# */ |
|||
|
|||
|
|||
use strict; |
|||
use Data::Dumper; |
|||
use Getopt::Long; |
|||
|
|||
# global variables |
|||
# ---------------- |
|||
|
|||
my $result; |
|||
my @inputs = (); |
|||
my $helpoption=0; |
|||
my $debug=0; |
|||
my $verbose=0; |
|||
my $sep; |
|||
my $print_vars; |
|||
my $uniq; |
|||
my %uniq; |
|||
|
|||
############################################# |
|||
############################################# |
|||
### ### |
|||
### FUNCTION MAIN ### |
|||
### ### |
|||
############################################# |
|||
############################################# |
|||
|
|||
|
|||
GetOptions( |
|||
'h|help+' => \$helpoption, |
|||
'debug+' => \$verbose, |
|||
't=s' => \$sep, |
|||
'l' => \$print_vars, |
|||
'uniq' => \$uniq |
|||
); |
|||
|
|||
if ($verbose==1) { |
|||
# Print messages for debug purposes |
|||
$debug = 1; |
|||
} |
|||
|
|||
if (($helpoption==1) || ($ARGV[0] eq "")) { |
|||
print "\nRecursive ldd v1.00\n"; |
|||
print "Written by Igor Ljubuncic (igor.ljubuncic\@intel.com)\n"; |
|||
print "Written by Yuval Nissan (yuval.nissan\@intel.com)\n\n"; |
|||
print "Usage mode:\n\n"; |
|||
print "-d\t\tverbose output\n"; |
|||
print "-h|help\t\tprint help\n"; |
|||
print "-t\t\tdelimiter\n"; |
|||
print "-l\t\tprint env. variables\n"; |
|||
print "-uniq\t\tprint unique values only\n\n"; |
|||
exit 0; |
|||
} |
|||
|
|||
$sep ||= "\t"; |
|||
|
|||
push @inputs, @ARGV; |
|||
|
|||
if($print_vars) { |
|||
print "ldd output can be affected by:\n"; |
|||
print "\$LD_LIBRARY_PATH = '".$ENV{LD_LIBRARY_PATH}."'\n"; |
|||
print "\$LD_PRELOAD = '".$ENV{LD_PRELOAD}."'\n\n"; |
|||
} |
|||
|
|||
&recurseLibs($inputs[0], 0); |
|||
delete $uniq{$inputs[0]}; |
|||
print join("\n", keys(%uniq))."\n" if $uniq; |
|||
|
|||
exit 0; |
|||
|
|||
############################## |
|||
############################## |
|||
## ## |
|||
## FUNCTIONS ## |
|||
## ## |
|||
############################## |
|||
############################## |
|||
|
|||
sub recurseLibs |
|||
{ |
|||
my $filename=shift; |
|||
my $depth = shift; |
|||
print "Working on file: $filename\n" if $debug; |
|||
print "$sep"x$depth if not $uniq; |
|||
++$depth; |
|||
return if $uniq{$filename} and $uniq; |
|||
$uniq{$filename} = 1; |
|||
print "$filename\n" if not $uniq; |
|||
chomp(my @libraries = `/usr/bin/ldd $filename`); |
|||
print "Libraries:\n@libraries\n" if $debug; |
|||
|
|||
foreach my $line (@libraries) { |
|||
next if not $line; |
|||
$line =~ s/^\s+//g; |
|||
$line =~ s/\s+$//g; |
|||
# If static or else |
|||
if (($line =~ /statically linked/) or ($line =~ /not a dynamic executable/)) { |
|||
return; |
|||
} |
|||
elsif($line =~ /not found/) { |
|||
print "$sep"x$depth if not $uniq; |
|||
print "$line\n" if not $uniq; |
|||
$uniq{$line} = 1; |
|||
next; |
|||
} |
|||
|
|||
|
|||
# Split and recurse on libraries (third value is the lib path): |
|||
my @newlibs = split(/\s+/,$line); |
|||
print Dumper(\@newlibs) if $debug; |
|||
|
|||
# Skip if no mapped or directly linked |
|||
# Sane output comes with four elements |
|||
if (scalar(@newlibs) < 4) { |
|||
print "$sep"x$depth if not $uniq; |
|||
print $newlibs[0]."\n" if not $uniq; |
|||
$uniq{$newlibs[0]} = 1; |
|||
next; |
|||
} |
|||
|
|||
print "\nI'm gonna enter recursion with $newlibs[2].\n\n" if $debug; |
|||
&recurseLibs($newlibs[2], $depth); |
|||
|
|||
} |
|||
return; |
|||
} |
|||
|
|||
__END__ |
@ -0,0 +1,27 @@ |
|||
if(CPACK_EXTERNAL_ENABLE_STAGING) |
|||
set(appdir "${CPACK_TEMPORARY_DIRECTORY}") |
|||
else() |
|||
set(appdir "${CPACK_INSTALL_PREFIX}") |
|||
endif() |
|||
set(appimage "${CPACK_PACKAGE_DIRECTORY}/${CPACK_PACKAGE_FILE_NAME}.AppImage") |
|||
|
|||
set(CPACK_EXTERNAL_BUILT_PACKAGES "${appimage}") |
|||
|
|||
if(DEFINED CPACK_AUDACITY_APPIMAGE_UPDATE_INFO) |
|||
set(ENV{AUDACITY_UPDATE_INFO} "${CPACK_AUDACITY_APPIMAGE_UPDATE_INFO}") |
|||
list(APPEND CPACK_EXTERNAL_BUILT_PACKAGES "${appimage}.zsync") |
|||
endif() |
|||
|
|||
configure_file("${CPACK_AUDACITY_SOURCE_DIR}/linux/AppRun.sh" "${appdir}/AppRun" ESCAPE_QUOTES @ONLY) |
|||
configure_file("${CPACK_AUDACITY_SOURCE_DIR}/linux/check_dependencies.sh" "${appdir}/bin/check_dependencies" ESCAPE_QUOTES @ONLY) |
|||
configure_file("${CPACK_AUDACITY_SOURCE_DIR}/linux/ldd_recursive.pl" "${appdir}/bin/ldd_recursive" COPYONLY) |
|||
|
|||
execute_process( |
|||
COMMAND "linux/create_appimage.sh" "${appdir}" "${appimage}" |
|||
WORKING_DIRECTORY "${CPACK_AUDACITY_SOURCE_DIR}" |
|||
RESULT_VARIABLE exit_status |
|||
) |
|||
|
|||
if(NOT "${exit_status}" EQUAL "0") |
|||
message(FATAL_ERROR "Could not create AppImage. See output above for details.") |
|||
endif() |
Write
Preview
Loading…
Cancel
Save
Reference in new issue