diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 72920c1..d24c3c9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,7 +2,7 @@ name: Release on: push: - branches: [ master ] + branches: [ master, release ] workflow_dispatch: permissions: diff --git a/data/apprun.sh b/data/apprun.sh index a37001b..78c7f21 100644 --- a/data/apprun.sh +++ b/data/apprun.sh @@ -39,7 +39,8 @@ if [ -z "$MAIN_BIN" ] ; then MAIN_BIN=$(find "${ROOT}/usr/bin" -name "${MAIN}" | head -n 1) fi -LD_LINUX=$(find "${ROOT}" -name 'ld-*.so.*' | head -n 1) +# Sort the result, since find returns the files in filesystem order, which is not the same on all systems. +LD_LINUX=$(find "${ROOT}" -name 'ld-*.so.*' | sort | head -n 1) # Set paths diff --git a/src/appimagebuilder.cpp b/src/appimagebuilder.cpp index 4812bde..2776da7 100644 --- a/src/appimagebuilder.cpp +++ b/src/appimagebuilder.cpp @@ -17,6 +17,8 @@ * */ +#include + #include #include #include @@ -37,6 +39,42 @@ using namespace Qt::Literals::StringLiterals; namespace AppImageBuilder { +namespace { + +// Returns the paths where the bundled AppImage runtime is looked for, in order of preference. +// Inside an AppImage the executable is started through the bundled dynamic loader, and applicationDirPath() then returns the directory of the loader instead of the executable. +// The loader can be in either lib64 or usr/lib64, so also look relative to the executable path from argv[0], which the dynamic loader sets to the path of the executable. +QStringList RuntimeFileCandidates(const QString &arch) { + + const QString runtime_filename = "runtime-"_L1 + arch; + + QStringList runtime_dirs; + const QStringList arguments = QCoreApplication::arguments(); + if (!arguments.isEmpty() && arguments.first().contains(u'/')) { + const QString executable_dir = QFileInfo(arguments.first()).absolutePath(); + runtime_dirs << executable_dir + "/../share/AppImageKit/runtime"_L1 + << executable_dir + << executable_dir + "/../lib64"_L1 + << executable_dir + "/../../lib64"_L1; + } + const QString application_dir = QCoreApplication::applicationDirPath(); + runtime_dirs << application_dir + "/../share/AppImageKit/runtime"_L1 + << application_dir; + + QStringList runtime_files; + for (const QString &runtime_dir : std::as_const(runtime_dirs)) { + const QString runtime_file = QDir::cleanPath(runtime_dir + u'/' + runtime_filename); + if (!runtime_files.contains(runtime_file)) { + runtime_files << runtime_file; + } + } + + return runtime_files; + +} + +} // namespace + bool Build(const QString &app_dir_path, const Options &options, QString &output_path, QString &error_message) { if (!QFileInfo::exists(app_dir_path)) { @@ -177,12 +215,20 @@ bool Build(const QString &app_dir_path, const Options &options, QString &output_ QString runtime_file = options.runtime_file; if (runtime_file.isEmpty()) { - QString runtime_dir = QDir::cleanPath(QCoreApplication::applicationDirPath() + "/../share/AppImageKit/runtime/"_L1); - if (!QDir(runtime_dir).exists()) runtime_dir = QCoreApplication::applicationDirPath(); - runtime_file = runtime_dir + "/runtime-"_L1 + arch; + const QStringList runtime_file_candidates = RuntimeFileCandidates(arch); + for (const QString &runtime_file_candidate : runtime_file_candidates) { + if (QFileInfo::exists(runtime_file_candidate)) { + runtime_file = runtime_file_candidate; + break; + } + } + if (runtime_file.isEmpty()) { + error_message = u"Cannot find runtime-%1, looked in:\n%2\nIt should have been bundled, but you can get it from https://github.com/AppImage/type2-runtime/releases/tag/continuous and pass it with --runtime-file"_s.arg(arch, runtime_file_candidates.join(u'\n')); + return false; + } } - if (!QFileInfo::exists(runtime_file)) { - error_message = u"Cannot find %1. It should have been bundled, but you can get it from https://github.com/AppImage/type2-runtime/releases/tag/continuous"_s.arg(runtime_file); + else if (!QFileInfo::exists(runtime_file)) { + error_message = u"Runtime file %1 does not exist"_s.arg(runtime_file); return false; } diff --git a/src/appimagebuilder.h b/src/appimagebuilder.h index 0536a11..34d1163 100644 --- a/src/appimagebuilder.h +++ b/src/appimagebuilder.h @@ -30,7 +30,7 @@ struct Options { // Output path. If empty, defaults to "--Linux-.AppImage" in the current directory (or inside `destination` if it names an existing directory). QString destination; - // Path to the AppImage runtime binary to embed. If empty, looked up as "runtime-" next to the executable, or under ../share/AppImageKit/runtime/ relative to it. + // Path to the AppImage runtime binary to embed. If empty, looked up as "runtime-" under ../share/AppImageKit/runtime/ relative to the executable, next to it, or in lib64 or usr/lib64 of the AppImage. QString runtime_file; // mksquashfs -comp value. diff --git a/src/appimagebuilder_main.cpp b/src/appimagebuilder_main.cpp index 0a6769d..050053e 100644 --- a/src/appimagebuilder_main.cpp +++ b/src/appimagebuilder_main.cpp @@ -52,6 +52,9 @@ int main(int argc, char *argv[]) { const QCommandLineOption version_option(u"version"_s, u"Version string to stamp into X-AppImage-Version and use in the output filename; if not given, detected by running the AppDir's main executable (from Exec= in its .desktop file) with --version"_s, u"version"_s); parser.addOption(version_option); + const QCommandLineOption runtime_file_option(u"runtime-file"_s, u"Path to the AppImage runtime to embed, instead of the runtime bundled with appimagebuilder"_s, u"file"_s); + parser.addOption(runtime_file_option); + parser.process(app); const QStringList positional = parser.positionalArguments(); @@ -79,6 +82,7 @@ int main(int argc, char *argv[]) { AppImageBuilder::Options options; options.version = parser.value(version_option); + options.runtime_file = parser.value(runtime_file_option); QString output_path; if (!AppImageBuilder::Build(app_dir_info.canonicalFilePath(), options, output_path, error_message)) { qCritical().noquote() << "ERROR:" << error_message;