mirror of
https://github.com/InstanceBrick/nightcap.git
synced 2026-10-18 01:01:36 +02:00
Rename the desktop entry, metainfo, icons, Flatpak manifest, and SDL app identity so Nightcap installs side by side with upstream. Build a signed Flatpak on v* tags, publish the OSTree repo and landing page to GitHub Pages, and attach the bundle to the release. Release creation tolerates the AppImage and Flatpak workflows racing on the same tag.
1033 lines
36 KiB
Bash
Executable File
1033 lines
36 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Modified by vii from komaruworld/mocktail. See README "About this fork".
|
|
# Copyright 2026 Mocktail Project Authors
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
set -Eeuo pipefail
|
|
umask 022
|
|
|
|
SCRIPT_DIR="$(cd -P -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd -P -- "${SCRIPT_DIR}/.." && pwd)"
|
|
BUILD_DIR="${PROJECT_ROOT}/build"
|
|
OUTPUT=""
|
|
OUTPUT_EXPLICIT=false
|
|
MODE=standalone
|
|
TARGET_LIBC=glibc
|
|
ARTIFACT_NAME=""
|
|
TARGET_INTERPRETER=""
|
|
PACKAGING_HOST_LIBC=unknown
|
|
DRY_RUN=false
|
|
APPIMAGE_OUTPUT=""
|
|
ANDROID_BUILD_TOOLS_ROOT="${MOCKTAIL_ANDROID_BUILD_TOOLS_ROOT:-}"
|
|
ANDROID_APKANALYZER_ROOT="${MOCKTAIL_ANDROID_APKANALYZER_ROOT:-}"
|
|
STAGING=""
|
|
APPDIR=""
|
|
PACKAGING_LIBRARY_PATH=""
|
|
ANDROID_TOOLS_ABI=""
|
|
APPIMAGE_FORMAT="${MOCKTAIL_APPIMAGE_FORMAT:-classic}"
|
|
|
|
readonly -a PROJECT_ARTIFACTS=(
|
|
mocktail
|
|
mocktail_updater
|
|
mocktail_failure_dialog
|
|
mocktail_webview_helper
|
|
libmocktail_audio_sdl.so
|
|
libc.so
|
|
libdl.so
|
|
libm.so
|
|
libz.so
|
|
libandroid.so
|
|
liblog.so
|
|
libmediandk.so
|
|
libOpenSLES.so
|
|
libOpenMAXAL.so
|
|
libEGL.so
|
|
libGLESv2.so
|
|
libvulkan.so
|
|
)
|
|
readonly FREEBSD_SOCKET_HELPER="mocktail_freebsd_socket_helper"
|
|
|
|
readonly -a RUNTIME_SCRIPTS=(
|
|
collect_support_bundle.sh
|
|
)
|
|
|
|
readonly STANDALONE_SUPPORT_PACKAGER="${PROJECT_ROOT}/scripts/package_standalone_support.sh"
|
|
readonly STANDALONE_WEBKIT_PACKAGER="${PROJECT_ROOT}/scripts/package_standalone_webkit.sh"
|
|
|
|
declare -A COPIED_SOURCES=()
|
|
declare -A HOST_REQUIREMENTS=()
|
|
declare -a DEPENDENCY_QUEUE=()
|
|
|
|
Usage() {
|
|
cat <<'EOF'
|
|
Usage: scripts/package_portable.sh [OPTIONS]
|
|
|
|
Create a relocatable x86-64 Linux bundle with a single ./run.sh launcher.
|
|
|
|
Options:
|
|
--build-dir DIR Release build directory (default: build).
|
|
--output DIR Bundle output directory (default includes libc ABI).
|
|
--libc ABI Target libc ABI: glibc (default) or musl.
|
|
--mode MODE standalone (default) or thin; aliases: full, static,
|
|
dynamic, and legacy minimal.
|
|
--dry-run Audit artifacts and dependency policy without writing.
|
|
--appimage FILE Also build FILE. Set MOCKTAIL_APPIMAGE_FORMAT to
|
|
classic (default) or anylinux. AnyLinux uses a native
|
|
/usr install instead of producing a portable directory.
|
|
-h, --help Show this help.
|
|
|
|
standalone bundles the application userspace and support tools. thin omits
|
|
copied dependency libraries and bootstraps host packages before launch.
|
|
The physical GPU kernel driver and vendor ICD remain host-provided.
|
|
|
|
Every Mocktail ELF build artifact must match --libc. Classic glibc bundles use
|
|
compact native aapt plus the Java apksigner library. A musl standalone bundle uses the
|
|
Java apkanalyzer backend instead of hiding a glibc runtime dependency.
|
|
No Android SDK development tree is published. Public release artifacts are:
|
|
Mocktail-x86_64-<libc>-<standalone|thin>.AppImage.
|
|
EOF
|
|
}
|
|
|
|
Log() {
|
|
printf '[portable] %s\n' "$*" >&2
|
|
}
|
|
|
|
Die() {
|
|
Log "error: $*"
|
|
exit 1
|
|
}
|
|
|
|
Cleanup() {
|
|
[[ -z "${STAGING}" ]] || rm -rf -- "${STAGING}"
|
|
[[ -z "${APPDIR}" ]] || rm -rf -- "${APPDIR}"
|
|
}
|
|
|
|
ParseArguments() {
|
|
while (( $# > 0 )); do
|
|
case "$1" in
|
|
--build-dir)
|
|
(( $# >= 2 )) || Die "--build-dir requires a directory"
|
|
BUILD_DIR="$2"
|
|
shift 2
|
|
;;
|
|
--output)
|
|
(( $# >= 2 )) || Die "--output requires a directory"
|
|
OUTPUT="$2"
|
|
OUTPUT_EXPLICIT=true
|
|
shift 2
|
|
;;
|
|
--libc)
|
|
(( $# >= 2 )) || Die "--libc requires glibc or musl"
|
|
TARGET_LIBC="$2"
|
|
shift 2
|
|
;;
|
|
--mode)
|
|
(( $# >= 2 )) || Die "--mode requires standalone or thin"
|
|
MODE="$2"
|
|
shift 2
|
|
;;
|
|
--dry-run)
|
|
DRY_RUN=true
|
|
shift
|
|
;;
|
|
--appimage)
|
|
(( $# >= 2 )) || Die "--appimage requires an output file"
|
|
APPIMAGE_OUTPUT="$2"
|
|
shift 2
|
|
;;
|
|
-h|--help)
|
|
Usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
Die "unknown option: $1"
|
|
;;
|
|
esac
|
|
done
|
|
case "${MODE}" in
|
|
standalone|full|static) MODE=standalone ;;
|
|
dynamic|minimal) MODE=thin ;;
|
|
esac
|
|
[[ "${MODE}" == standalone || "${MODE}" == thin ]] ||
|
|
Die "--mode must be standalone or thin"
|
|
[[ "${TARGET_LIBC}" == glibc || "${TARGET_LIBC}" == musl ]] ||
|
|
Die "--libc must be glibc or musl"
|
|
[[ "${APPIMAGE_FORMAT}" == classic ||
|
|
"${APPIMAGE_FORMAT}" == anylinux ]] ||
|
|
Die "MOCKTAIL_APPIMAGE_FORMAT must be classic or anylinux"
|
|
if [[ "${APPIMAGE_FORMAT}" == anylinux && "${TARGET_LIBC}" != glibc ]]; then
|
|
Die "AnyLinux packaging requires a glibc build"
|
|
fi
|
|
|
|
if [[ "${TARGET_LIBC}" == musl && "${MODE}" == standalone ]]; then
|
|
ANDROID_TOOLS_ABI=java
|
|
else
|
|
ANDROID_TOOLS_ABI=glibc
|
|
fi
|
|
|
|
ARTIFACT_NAME="mocktail-linux-x86_64-${TARGET_LIBC}-${MODE}"
|
|
if [[ "${OUTPUT_EXPLICIT}" == false ]]; then
|
|
OUTPUT="${PROJECT_ROOT}/dist/${ARTIFACT_NAME}"
|
|
fi
|
|
}
|
|
|
|
RequireCommand() {
|
|
command -v "$1" >/dev/null 2>&1 || Die "missing required command: $1"
|
|
}
|
|
|
|
CanonicalDirectory() {
|
|
local path="$1"
|
|
[[ -d "${path}" ]] || Die "directory does not exist: ${path}"
|
|
cd -P -- "${path}" && pwd
|
|
}
|
|
|
|
ReadElfInterpreter() {
|
|
LC_ALL=C readelf -l "$1" 2>/dev/null |
|
|
sed -n 's/.*Requesting program interpreter: \([^]]*\).*/\1/p'
|
|
}
|
|
|
|
DetectElfLibc() {
|
|
local path="$1"
|
|
local interpreter needed interpreter_libc="" needed_libc=""
|
|
interpreter="$(ReadElfInterpreter "${path}")"
|
|
case "${interpreter}" in
|
|
*ld-musl-*.so.1) interpreter_libc=musl ;;
|
|
*ld-linux*.so.*) interpreter_libc=glibc ;;
|
|
esac
|
|
|
|
needed="$(LC_ALL=C readelf -d "${path}" 2>/dev/null |
|
|
sed -n 's/.*Shared library: \[\([^]]*\)\].*/\1/p')"
|
|
if grep -Eq '^(libc\.so\.6|ld-linux[^[:space:]]*\.so(\.[0-9]+)*)$' \
|
|
<<<"${needed}"; then
|
|
needed_libc=glibc
|
|
fi
|
|
if grep -Eq '^(libc\.so|ld-musl-[^[:space:]]*\.so\.1)$' \
|
|
<<<"${needed}"; then
|
|
if [[ -n "${needed_libc}" && "${needed_libc}" != musl ]]; then
|
|
printf mixed
|
|
return 0
|
|
fi
|
|
needed_libc=musl
|
|
fi
|
|
if [[ -n "${interpreter_libc}" && -n "${needed_libc}" &&
|
|
"${interpreter_libc}" != "${needed_libc}" ]]; then
|
|
printf mixed
|
|
elif [[ -n "${interpreter_libc}" ]]; then
|
|
printf '%s' "${interpreter_libc}"
|
|
elif [[ -n "${needed_libc}" ]]; then
|
|
printf '%s' "${needed_libc}"
|
|
else
|
|
printf unknown
|
|
fi
|
|
}
|
|
|
|
DetectHostLibc() {
|
|
local version shell_interpreter
|
|
version="$(LC_ALL=C getconf GNU_LIBC_VERSION 2>/dev/null || true)"
|
|
if [[ "${version}" == glibc\ * ]]; then
|
|
printf glibc
|
|
return 0
|
|
fi
|
|
shell_interpreter="$(ReadElfInterpreter /proc/$$/exe)"
|
|
case "${shell_interpreter}" in
|
|
*ld-musl-*.so.1) printf musl ;;
|
|
*ld-linux*.so.*) printf glibc ;;
|
|
*) printf unknown ;;
|
|
esac
|
|
}
|
|
|
|
ValidateElfLibc() {
|
|
local path="$1"
|
|
local label="$2"
|
|
local detected interpreter
|
|
detected="$(DetectElfLibc "${path}")"
|
|
interpreter="$(ReadElfInterpreter "${path}")"
|
|
[[ "${detected}" == "${TARGET_LIBC}" ]] ||
|
|
Die "${label} libc ABI mismatch: expected ${TARGET_LIBC}, detected ${detected} (interpreter=${interpreter:-none})"
|
|
}
|
|
|
|
ValidateArtifacts() {
|
|
local artifact path architecture interpreter
|
|
for artifact in "${PROJECT_ARTIFACTS[@]}"; do
|
|
path="${BUILD_DIR}/${artifact}"
|
|
[[ -f "${path}" && ! -L "${path}" ]] ||
|
|
Die "missing regular build artifact: ${path}"
|
|
architecture="$(LC_ALL=C readelf -h "${path}" 2>/dev/null |
|
|
sed -n 's/^[[:space:]]*Machine:[[:space:]]*//p')"
|
|
[[ "${architecture}" == *"X86-64"* ]] ||
|
|
Die "build artifact is not x86-64 ELF: ${path}"
|
|
ValidateElfLibc "${path}" "build artifact ${artifact}"
|
|
done
|
|
|
|
TARGET_INTERPRETER="$(ReadElfInterpreter "${BUILD_DIR}/mocktail")"
|
|
[[ -n "${TARGET_INTERPRETER}" ]] ||
|
|
Die "mocktail must be a dynamically linked ${TARGET_LIBC} executable"
|
|
|
|
local freebsd_helper="${BUILD_DIR}/${FREEBSD_SOCKET_HELPER}"
|
|
[[ -f "${freebsd_helper}" && ! -L "${freebsd_helper}" &&
|
|
-x "${freebsd_helper}" ]] ||
|
|
Die "missing executable FreeBSD socket helper: ${freebsd_helper}"
|
|
LC_ALL=C readelf -h "${freebsd_helper}" 2>/dev/null |
|
|
grep -Eq 'OS/ABI:[[:space:]]+UNIX - FreeBSD' ||
|
|
Die "socket helper is not branded for FreeBSD"
|
|
LC_ALL=C readelf -h "${freebsd_helper}" 2>/dev/null |
|
|
grep -Eq 'Machine:[[:space:]]+.*X86-64' ||
|
|
Die "socket helper is not an x86-64 ELF"
|
|
[[ -z "$(ReadElfInterpreter "${freebsd_helper}")" ]] ||
|
|
Die "FreeBSD socket helper must be statically linked"
|
|
LC_ALL=C readelf -n "${freebsd_helper}" 2>/dev/null |
|
|
grep -Fq FreeBSD || Die "socket helper has no FreeBSD ABI note"
|
|
}
|
|
|
|
ResolveAndroidBuildTools() {
|
|
local apksigner_path analyzer_path discovered_root
|
|
if [[ -n "${ANDROID_BUILD_TOOLS_ROOT}" ]]; then
|
|
ANDROID_BUILD_TOOLS_ROOT="$(CanonicalDirectory \
|
|
"${ANDROID_BUILD_TOOLS_ROOT}")"
|
|
else
|
|
apksigner_path="$(command -v apksigner)" ||
|
|
Die "apksigner is required to package the standalone updater"
|
|
apksigner_path="$(readlink -f -- "${apksigner_path}")"
|
|
discovered_root="$(dirname -- "${apksigner_path}")"
|
|
ANDROID_BUILD_TOOLS_ROOT="$(CanonicalDirectory "${discovered_root}")"
|
|
fi
|
|
|
|
local required
|
|
for required in apksigner lib/apksigner.jar NOTICE.txt source.properties; do
|
|
[[ -f "${ANDROID_BUILD_TOOLS_ROOT}/${required}" &&
|
|
! -L "${ANDROID_BUILD_TOOLS_ROOT}/${required}" ]] ||
|
|
Die "Android build-tools artifact is unavailable: ${required}"
|
|
done
|
|
if [[ "${ANDROID_TOOLS_ABI}" == glibc ]]; then
|
|
for required in aapt lib64/libc++.so; do
|
|
[[ -f "${ANDROID_BUILD_TOOLS_ROOT}/${required}" &&
|
|
! -L "${ANDROID_BUILD_TOOLS_ROOT}/${required}" ]] ||
|
|
Die "Android native metadata artifact is unavailable: ${required}"
|
|
done
|
|
return 0
|
|
fi
|
|
if [[ -n "${ANDROID_APKANALYZER_ROOT}" ]]; then
|
|
ANDROID_APKANALYZER_ROOT="$(CanonicalDirectory \
|
|
"${ANDROID_APKANALYZER_ROOT}")"
|
|
else
|
|
analyzer_path="$(command -v apkanalyzer)" ||
|
|
Die "apkanalyzer is required to package portable APK metadata support"
|
|
analyzer_path="$(readlink -f -- "${analyzer_path}")"
|
|
ANDROID_APKANALYZER_ROOT="$(CanonicalDirectory \
|
|
"$(dirname -- "$(dirname -- "${analyzer_path}")")")"
|
|
fi
|
|
[[ -f "${ANDROID_APKANALYZER_ROOT}/lib/apkanalyzer-classpath.jar" &&
|
|
! -L "${ANDROID_APKANALYZER_ROOT}/lib/apkanalyzer-classpath.jar" ]] ||
|
|
Die "Android apkanalyzer classpath is unavailable"
|
|
[[ -f "${ANDROID_APKANALYZER_ROOT}/NOTICE.txt" &&
|
|
-f "${ANDROID_APKANALYZER_ROOT}/source.properties" ]] ||
|
|
Die "Android apkanalyzer license or source metadata is unavailable"
|
|
}
|
|
|
|
ListElfDependencies() {
|
|
local output status=0
|
|
output="$(LC_ALL=C LD_LIBRARY_PATH="${PACKAGING_LIBRARY_PATH}${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}" \
|
|
ldd "$1" 2>&1)" || status=$?
|
|
if grep -Eq '=>[[:space:]]*not found|^Error loading shared library .*: No such file' \
|
|
<<<"${output}"; then
|
|
printf '%s\n' "${output}" >&2
|
|
return 1
|
|
fi
|
|
if (( status != 0 )) && ! grep -q '^Error relocating ' <<<"${output}"; then
|
|
printf '%s\n' "${output}" >&2
|
|
return 1
|
|
fi
|
|
awk '
|
|
/=>[[:space:]]*\// {
|
|
for (field = 1; field <= NF; ++field) {
|
|
if ($field == "=>") {
|
|
print $(field + 1)
|
|
break
|
|
}
|
|
}
|
|
next
|
|
}
|
|
/^[[:space:]]*\// { print $1 }
|
|
' <<<"${output}"
|
|
}
|
|
|
|
ListElfNeededSonames() {
|
|
LC_ALL=C readelf -d "$1" 2>/dev/null |
|
|
sed -n 's/.*Shared library: \[\([^]]*\)\].*/\1/p'
|
|
}
|
|
|
|
HostDependencyReason() {
|
|
local soname="$1"
|
|
case "${soname}" in
|
|
ld-musl-*.so.1)
|
|
printf musl-runtime
|
|
;;
|
|
libc.so)
|
|
if [[ "${TARGET_LIBC}" == musl ]]; then
|
|
printf musl-runtime
|
|
else
|
|
return 1
|
|
fi
|
|
;;
|
|
ld-linux*.so*|libc.so.*|libm.so.*|libdl.so.*|libpthread.so.*|librt.so.*|\
|
|
libresolv.so.*|libutil.so.*|libanl.so.*|libnss_*.so.*)
|
|
printf glibc
|
|
;;
|
|
libGL.so.*|libGLX.so.*|libGLdispatch.so.*|libOpenGL.so.*|libEGL.so.*|\
|
|
libGLESv1_CM.so.*|libGLESv2.so.*|libgbm.so.*|libdrm.so.*|libdrm_*.so.*)
|
|
printf gpu-driver-stack
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
RecordHostRequirement() {
|
|
local soname="$1"
|
|
local reason="$2"
|
|
HOST_REQUIREMENTS["${soname}"]="${reason}"
|
|
}
|
|
|
|
IsProjectArtifact() {
|
|
local candidate="$1"
|
|
local artifact
|
|
for artifact in "${PROJECT_ARTIFACTS[@]}"; do
|
|
[[ "${candidate}" != "${artifact}" ]] || return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
QueueDependencies() {
|
|
local source="$1"
|
|
local dependencies dependency soname canonical reason destination
|
|
local existing_hash new_hash
|
|
dependencies="$(ListElfDependencies "${source}")" ||
|
|
Die "cannot resolve dependency closure for ${source}"
|
|
while IFS= read -r dependency; do
|
|
[[ -n "${dependency}" ]] || continue
|
|
soname="${dependency##*/}"
|
|
if reason="$(HostDependencyReason "${soname}")"; then
|
|
RecordHostRequirement "${soname}" "${reason}"
|
|
continue
|
|
fi
|
|
if IsProjectArtifact "${soname}"; then
|
|
continue
|
|
fi
|
|
canonical="$(readlink -f -- "${dependency}")"
|
|
[[ -f "${canonical}" ]] || Die "unresolved dependency: ${dependency}"
|
|
if [[ "${MODE}" == thin ]]; then
|
|
RecordHostRequirement "${soname}" thin-mode
|
|
continue
|
|
fi
|
|
destination="${STAGING}/mocktail/lib/${soname}"
|
|
if [[ -n "${COPIED_SOURCES[${soname}]+present}" ]]; then
|
|
existing_hash="$(sha256sum "${COPIED_SOURCES[${soname}]}" |
|
|
awk '{print $1}')"
|
|
new_hash="$(sha256sum "${canonical}" | awk '{print $1}')"
|
|
[[ "${existing_hash}" == "${new_hash}" ]] ||
|
|
Die "dependency basename collision for ${soname}"
|
|
continue
|
|
fi
|
|
COPIED_SOURCES["${soname}"]="${canonical}"
|
|
DEPENDENCY_QUEUE+=("${canonical}")
|
|
if [[ "${DRY_RUN}" == false ]]; then
|
|
install -m 0755 -- "${canonical}" "${destination}"
|
|
fi
|
|
done <<<"${dependencies}"
|
|
}
|
|
|
|
AuditWebViewHostStack() {
|
|
local dependencies dependency soname reason
|
|
dependencies="$(ListElfDependencies \
|
|
"${BUILD_DIR}/mocktail_webview_helper")" ||
|
|
Die "cannot resolve host WebKit dependency closure"
|
|
while IFS= read -r dependency; do
|
|
[[ -n "${dependency}" ]] || continue
|
|
soname="${dependency##*/}"
|
|
reason="$(HostDependencyReason "${soname}" 2>/dev/null || printf webview-stack)"
|
|
RecordHostRequirement "${soname}" "${reason}"
|
|
done <<<"${dependencies}"
|
|
}
|
|
|
|
AuditThinDependencies() {
|
|
local artifact soname reason
|
|
for artifact in "${PROJECT_ARTIFACTS[@]}"; do
|
|
while IFS= read -r soname; do
|
|
[[ -n "${soname}" ]] || continue
|
|
if IsProjectArtifact "${soname}"; then
|
|
continue
|
|
fi
|
|
reason="$(HostDependencyReason "${soname}" 2>/dev/null ||
|
|
printf thin-mode)"
|
|
RecordHostRequirement "${soname}" "${reason}"
|
|
done < <(ListElfNeededSonames "${BUILD_DIR}/${artifact}")
|
|
done
|
|
}
|
|
|
|
CopyRuntimeTree() {
|
|
local runtime_root="${STAGING}/mocktail"
|
|
mkdir -p -- "${runtime_root}/bin" "${runtime_root}/lib" \
|
|
"${runtime_root}/scripts" "${runtime_root}/metadata" \
|
|
"${runtime_root}/runtime"
|
|
chmod 0755 -- "${STAGING}"
|
|
|
|
local artifact mode
|
|
for artifact in "${PROJECT_ARTIFACTS[@]}"; do
|
|
mode=0755
|
|
install -m "${mode}" -- "${BUILD_DIR}/${artifact}" \
|
|
"${runtime_root}/bin/${artifact}"
|
|
done
|
|
install -m 0755 -- "${BUILD_DIR}/${FREEBSD_SOCKET_HELPER}" \
|
|
"${runtime_root}/bin/${FREEBSD_SOCKET_HELPER}"
|
|
|
|
install -m 0755 -- "${PROJECT_ROOT}/packaging/run.sh" "${STAGING}/run.sh"
|
|
install -m 0755 -- "${PROJECT_ROOT}/packaging/mocktail-launcher.sh" \
|
|
"${runtime_root}/scripts/portable_launcher.sh"
|
|
install -m 0755 -- "${PROJECT_ROOT}/scripts/install_thin_dependencies.sh" \
|
|
"${runtime_root}/scripts/install_thin_dependencies.sh"
|
|
install -m 0644 -- "${PROJECT_ROOT}/config/roblox_compatibility.json" \
|
|
"${PROJECT_ROOT}/config/roblox_bootstrap_sources.json" \
|
|
"${PROJECT_ROOT}/config/roblox_host_abi_reference.json" \
|
|
"${PROJECT_ROOT}/config/roblox_signing_certificates.json" \
|
|
"${runtime_root}/metadata/"
|
|
|
|
local script
|
|
for script in "${RUNTIME_SCRIPTS[@]}"; do
|
|
install -m 0755 -- "${PROJECT_ROOT}/scripts/${script}" \
|
|
"${runtime_root}/scripts/${script}"
|
|
done
|
|
}
|
|
|
|
CopyApkAnalyzerClasspath() {
|
|
local source_root="${ANDROID_APKANALYZER_ROOT}/lib"
|
|
local destination_root="$1"
|
|
local manifest_jar="${source_root}/apkanalyzer-classpath.jar"
|
|
local class_path relative source destination
|
|
class_path="$(unzip -p "${manifest_jar}" META-INF/MANIFEST.MF |
|
|
tr -d '\r' | awk '
|
|
/^Class-Path: / {
|
|
collecting = 1
|
|
value = substr($0, 13)
|
|
next
|
|
}
|
|
collecting && /^ / {
|
|
value = value substr($0, 2)
|
|
next
|
|
}
|
|
collecting {
|
|
print value
|
|
emitted = 1
|
|
exit
|
|
}
|
|
END {
|
|
if (collecting && !emitted) print value
|
|
}
|
|
')"
|
|
[[ -n "${class_path}" ]] || Die "Android apkanalyzer classpath is empty"
|
|
install -d -m 0755 -- "${destination_root}"
|
|
install -m 0644 -- "${manifest_jar}" \
|
|
"${destination_root}/apkanalyzer-classpath.jar"
|
|
for relative in ${class_path}; do
|
|
[[ "${relative}" != /* && "${relative}" != .. &&
|
|
"${relative}" != ../* && "${relative}" != */../* &&
|
|
"${relative}" != */.. ]] ||
|
|
Die "unsafe Android analyzer classpath entry: ${relative}"
|
|
source="${source_root}/${relative}"
|
|
[[ -f "${source}" && ! -L "${source}" ]] ||
|
|
Die "Android analyzer classpath entry is unavailable: ${relative}"
|
|
destination="${destination_root}/${relative}"
|
|
install -d -m 0755 -- "$(dirname -- "${destination}")"
|
|
install -m 0644 -- "${source}" "${destination}"
|
|
done
|
|
}
|
|
|
|
PackageAndroidRuntime() {
|
|
local android_root="${STAGING}/mocktail/runtime/android-tools"
|
|
install -d -m 0755 -- "${android_root}/bin" "${android_root}/lib" \
|
|
"${android_root}/licenses"
|
|
install -m 0755 -- "${ANDROID_BUILD_TOOLS_ROOT}/apksigner" \
|
|
"${android_root}/bin/apksigner"
|
|
install -m 0644 -- "${ANDROID_BUILD_TOOLS_ROOT}/lib/apksigner.jar" \
|
|
"${android_root}/lib/apksigner.jar"
|
|
ln -s -- ../lib "${android_root}/bin/lib"
|
|
install -m 0644 -- "${ANDROID_BUILD_TOOLS_ROOT}/NOTICE.txt" \
|
|
"${android_root}/licenses/build-tools.txt"
|
|
|
|
if [[ "${ANDROID_TOOLS_ABI}" == glibc ]]; then
|
|
install -d -m 0755 -- "${android_root}/bin/lib64"
|
|
install -m 0755 -- "${ANDROID_BUILD_TOOLS_ROOT}/aapt" \
|
|
"${android_root}/bin/aapt"
|
|
install -m 0755 -- "${ANDROID_BUILD_TOOLS_ROOT}/lib64/libc++.so" \
|
|
"${android_root}/bin/lib64/libc++.so"
|
|
return 0
|
|
fi
|
|
|
|
install -d -m 0755 -- \
|
|
"${android_root}/sdk/cmdline-tools/latest/lib" \
|
|
"${android_root}/sdk/build-tools/37.0.0"
|
|
install -m 0755 -- "${PROJECT_ROOT}/packaging/aapt-apkanalyzer.sh" \
|
|
"${android_root}/bin/aapt"
|
|
install -m 0755 -- "${PROJECT_ROOT}/packaging/apkanalyzer.sh" \
|
|
"${android_root}/bin/apkanalyzer"
|
|
CopyApkAnalyzerClasspath \
|
|
"${android_root}/sdk/cmdline-tools/latest/lib"
|
|
install -m 0755 -- "${PROJECT_ROOT}/packaging/aapt-apkanalyzer.sh" \
|
|
"${android_root}/sdk/build-tools/37.0.0/aapt"
|
|
install -m 0644 -- "${ANDROID_BUILD_TOOLS_ROOT}/source.properties" \
|
|
"${android_root}/sdk/build-tools/37.0.0/source.properties"
|
|
install -m 0644 -- "${ANDROID_APKANALYZER_ROOT}/source.properties" \
|
|
"${android_root}/sdk/cmdline-tools/latest/source.properties"
|
|
install -m 0644 -- "${ANDROID_APKANALYZER_ROOT}/NOTICE.txt" \
|
|
"${android_root}/licenses/cmdline-tools.txt"
|
|
}
|
|
|
|
PackageStandaloneRuntime() {
|
|
[[ "${MODE}" == standalone ]] || return 0
|
|
rmdir -- "${STAGING}/mocktail/runtime"
|
|
"${STANDALONE_SUPPORT_PACKAGER}" \
|
|
--runtime-root "${STAGING}/mocktail" --libc "${TARGET_LIBC}"
|
|
"${STANDALONE_WEBKIT_PACKAGER}" \
|
|
--runtime-root "${STAGING}/mocktail" --libc "${TARGET_LIBC}"
|
|
PACKAGING_LIBRARY_PATH="$(find "${STAGING}/mocktail" -type f \
|
|
\( -perm -0100 -o -name '*.so' -o -name '*.so.*' \) -print0 |
|
|
while IFS= read -r -d '' path; do
|
|
IsElfFile "${path}" || continue
|
|
dirname -- "${path}"
|
|
done | LC_ALL=C sort -u | paste -sd: -)"
|
|
}
|
|
|
|
BuildStandaloneNamespace() {
|
|
[[ "${MODE}" == standalone ]] || return 0
|
|
local environment_file="${STAGING}/mocktail/webkit.env"
|
|
local webkit6_namespace namespace_root
|
|
webkit6_namespace="$(sed -n \
|
|
's/^MOCKTAIL_WEBKITGTK6_NAMESPACE_DIR=//p' "${environment_file}")"
|
|
[[ "${webkit6_namespace}" =~ ^/usr/(lib|lib64|libexec|lib/x86_64-linux-gnu)/webkitgtk-6\.0$ ]] ||
|
|
Die "unsafe WebKitGTK 6 namespace directory: ${webkit6_namespace}"
|
|
|
|
namespace_root="${STAGING}/mocktail/namespace"
|
|
mkdir -p -- "${namespace_root}/usr/bin" \
|
|
"${namespace_root}${webkit6_namespace}/injected-bundle"
|
|
local process_name
|
|
for process_name in WebKitGPUProcess WebKitNetworkProcess WebKitWebProcess; do
|
|
ln -- "${STAGING}/mocktail/libexec/webkitgtk-6.0/${process_name}" \
|
|
"${namespace_root}${webkit6_namespace}/${process_name}"
|
|
done
|
|
ln -- "${STAGING}/mocktail/lib/webkitgtk-6.0/injected-bundle/libwebkitgtkinjectedbundle.so" \
|
|
"${namespace_root}${webkit6_namespace}/injected-bundle/libwebkitgtkinjectedbundle.so"
|
|
ln -- "$(readlink -f -- "${STAGING}/mocktail/runtime/bin/bwrap")" \
|
|
"${namespace_root}/usr/bin/bwrap"
|
|
ln -- "$(readlink -f -- "${STAGING}/mocktail/runtime/bin/xdg-dbus-proxy")" \
|
|
"${namespace_root}/usr/bin/xdg-dbus-proxy"
|
|
}
|
|
|
|
IsElfFile() {
|
|
LC_ALL=C readelf -h "$1" 2>/dev/null |
|
|
grep -Eq '^[[:space:]]*Type:[[:space:]]*(DYN|EXEC)'
|
|
}
|
|
|
|
QueuePackagedRuntimeDependencies() {
|
|
local source
|
|
while IFS= read -r -d '' source; do
|
|
IsElfFile "${source}" || continue
|
|
case "${source#${STAGING}/mocktail/}" in
|
|
bin/${FREEBSD_SOCKET_HELPER}) continue ;;
|
|
runtime/android-tools/*) continue ;;
|
|
esac
|
|
QueueDependencies "${source}"
|
|
done < <(find "${STAGING}/mocktail/bin" \
|
|
"${STAGING}/mocktail/runtime" "${STAGING}/mocktail/libexec" \
|
|
"${STAGING}/mocktail/lib/plugins" \
|
|
"${STAGING}/mocktail/lib/webkitgtk-6.0" \
|
|
-type f \( -perm -0100 -o -name '*.so' -o -name '*.so.*' \) \
|
|
-print0 2>/dev/null)
|
|
}
|
|
|
|
CollectDependencies() {
|
|
local artifact source queue_index=0
|
|
if [[ "${MODE}" == thin ]]; then
|
|
AuditThinDependencies
|
|
return 0
|
|
fi
|
|
if [[ "${DRY_RUN}" == true ]]; then
|
|
for artifact in "${PROJECT_ARTIFACTS[@]}"; do
|
|
QueueDependencies "${BUILD_DIR}/${artifact}"
|
|
done
|
|
else
|
|
QueuePackagedRuntimeDependencies
|
|
fi
|
|
while (( queue_index < ${#DEPENDENCY_QUEUE[@]} )); do
|
|
source="${DEPENDENCY_QUEUE[queue_index]}"
|
|
((queue_index += 1))
|
|
QueueDependencies "${source}"
|
|
done
|
|
}
|
|
|
|
WriteDependencyManifest() {
|
|
{
|
|
printf 'Mocktail portable dependency contract\n'
|
|
printf 'mode=%s\n' "${MODE}"
|
|
printf 'architecture=x86_64\n'
|
|
printf 'libc=%s\n' "${TARGET_LIBC}"
|
|
printf 'interpreter=%s\n' "${TARGET_INTERPRETER}"
|
|
printf 'android_tools_libc=%s\n\n' "${ANDROID_TOOLS_ABI}"
|
|
printf 'Bundled userspace libraries:\n'
|
|
if (( ${#COPIED_SOURCES[@]} == 0 )); then
|
|
if [[ "${MODE}" == thin ]]; then
|
|
printf ' (none; thin mode)\n'
|
|
else
|
|
printf ' (none required by the standalone closure)\n'
|
|
fi
|
|
else
|
|
local soname
|
|
while IFS= read -r soname; do
|
|
printf ' %s <- %s\n' "${soname}" "${COPIED_SOURCES[${soname}]}"
|
|
done < <(printf '%s\n' "${!COPIED_SOURCES[@]}" | sort)
|
|
fi
|
|
printf '\nHost-provided libraries:\n'
|
|
if (( ${#HOST_REQUIREMENTS[@]} == 0 )); then
|
|
printf ' (none)\n'
|
|
else
|
|
local soname
|
|
while IFS= read -r soname; do
|
|
printf ' %s (%s)\n' "${soname}" "${HOST_REQUIREMENTS[${soname}]}"
|
|
done < <(printf '%s\n' "${!HOST_REQUIREMENTS[@]}" | sort)
|
|
fi
|
|
printf '\nThe Linux kernel and x86-64 %s ABI remain host-provided.\n' \
|
|
"${TARGET_LIBC}"
|
|
printf 'Only the physical GPU driver, Vulkan ICD, and GL/display driver stack are host-provided.\n'
|
|
} >"${STAGING}/mocktail/metadata/DEPENDENCIES.txt"
|
|
}
|
|
|
|
WriteAbiManifest() {
|
|
{
|
|
printf 'schema=2\n'
|
|
printf 'architecture=x86_64\n'
|
|
printf 'libc=%s\n' "${TARGET_LIBC}"
|
|
printf 'mode=%s\n' "${MODE}"
|
|
printf 'interpreter=%s\n' "${TARGET_INTERPRETER}"
|
|
printf 'android_tools_libc=%s\n' "${ANDROID_TOOLS_ABI}"
|
|
} >"${STAGING}/mocktail/metadata/ABI.txt"
|
|
}
|
|
|
|
PatchRuntimePaths() {
|
|
local artifact
|
|
if [[ "${MODE}" == thin ]]; then
|
|
patchelf --set-rpath '$ORIGIN' "${STAGING}/mocktail/bin/mocktail"
|
|
patchelf --remove-rpath \
|
|
"${STAGING}/mocktail/bin/mocktail_webview_helper"
|
|
for artifact in "${PROJECT_ARTIFACTS[@]}"; do
|
|
case "${artifact}" in
|
|
mocktail|mocktail_webview_helper) continue ;;
|
|
esac
|
|
patchelf --set-rpath '$ORIGIN' \
|
|
"${STAGING}/mocktail/bin/${artifact}"
|
|
done
|
|
return 0
|
|
fi
|
|
|
|
local elf directory relative_to_lib central_path existing_path new_path
|
|
while IFS= read -r -d '' elf; do
|
|
IsElfFile "${elf}" || continue
|
|
LC_ALL=C readelf -d "${elf}" 2>/dev/null |
|
|
grep -q 'Dynamic section' || continue
|
|
case "${elf#${STAGING}/mocktail/}" in
|
|
runtime/android-tools/*) continue ;;
|
|
esac
|
|
directory="$(dirname -- "${elf}")"
|
|
relative_to_lib="$(realpath --relative-to="${directory}" -- \
|
|
"${STAGING}/mocktail/lib")"
|
|
if [[ "${relative_to_lib}" == . ]]; then
|
|
central_path='$ORIGIN'
|
|
else
|
|
central_path="\$ORIGIN/${relative_to_lib}"
|
|
fi
|
|
existing_path="$(patchelf --print-rpath "${elf}" 2>/dev/null || true)"
|
|
new_path="\$ORIGIN:${central_path}"
|
|
if [[ -n "${existing_path}" ]]; then
|
|
new_path+=":${existing_path}"
|
|
fi
|
|
patchelf --set-rpath "${new_path}" "${elf}"
|
|
done < <(find "${STAGING}/mocktail" -type f \
|
|
\( -perm -0100 -o -name '*.so' -o -name '*.so.*' \) -print0)
|
|
patchelf --set-rpath '$ORIGIN:$ORIGIN/../lib' \
|
|
"${STAGING}/mocktail/bin/mocktail" \
|
|
"${STAGING}/mocktail/bin/mocktail_failure_dialog" \
|
|
"${STAGING}/mocktail/bin/mocktail_webview_helper"
|
|
}
|
|
|
|
WriteBundleChecksums() {
|
|
touch "${STAGING}/mocktail/metadata/.mocktail-portable-bundle"
|
|
(
|
|
cd -- "${STAGING}"
|
|
find . -type f \
|
|
! -path './mocktail/metadata/SHA256SUMS.txt' -print0 |
|
|
LC_ALL=C sort -z |
|
|
while IFS= read -r -d '' path; do
|
|
sha256sum -- "${path}"
|
|
done
|
|
) >"${STAGING}/mocktail/metadata/SHA256SUMS.txt"
|
|
}
|
|
|
|
VerifyBundle() {
|
|
local elf dynamic_path dependencies packaged_interpreter packaged_libc
|
|
local public_entries runtime_entries
|
|
local expected_dynamic_path='$ORIGIN'
|
|
if [[ "${MODE}" == standalone ]]; then
|
|
expected_dynamic_path='$ORIGIN:$ORIGIN/../lib'
|
|
fi
|
|
public_entries="$(find "${STAGING}" -mindepth 1 -maxdepth 1 \
|
|
-printf '%f\n' | LC_ALL=C sort)"
|
|
[[ "${public_entries}" == $'mocktail\nrun.sh' ]] ||
|
|
Die "portable root must contain only run.sh and mocktail/"
|
|
[[ -x "${STAGING}/run.sh" && ! -L "${STAGING}/run.sh" &&
|
|
-d "${STAGING}/mocktail" && ! -L "${STAGING}/mocktail" ]] ||
|
|
Die "portable public layout contains an invalid launcher or runtime root"
|
|
runtime_entries="$(find "${STAGING}/mocktail" -mindepth 1 -maxdepth 1 \
|
|
-printf '%f\n' | LC_ALL=C sort)"
|
|
if [[ "${MODE}" == standalone ]]; then
|
|
[[ "${runtime_entries}" == \
|
|
$'bin\nlib\nlibexec\nmetadata\nnamespace\nruntime\nscripts\nshare\nwebkit.env' ]] ||
|
|
Die "standalone runtime root has an unexpected public entry: ${runtime_entries//$'\n'/,}"
|
|
else
|
|
[[ "${runtime_entries}" == \
|
|
$'bin\nlib\nmetadata\nruntime\nscripts' ]] ||
|
|
Die "thin runtime root has an unexpected public entry: ${runtime_entries//$'\n'/,}"
|
|
fi
|
|
while IFS= read -r -d '' elf; do
|
|
if LC_ALL=C readelf -d "${elf}" 2>/dev/null |
|
|
grep -F -- "${BUILD_DIR}" >/dev/null; then
|
|
Die "absolute build path remains in packaged ELF: ${elf}"
|
|
fi
|
|
done < <(find "${STAGING}/mocktail" -type f \
|
|
\( -perm -0100 -o -name '*.so' -o -name '*.so.*' \) -print0)
|
|
|
|
dynamic_path="$(LC_ALL=C readelf -d "${STAGING}/mocktail/bin/mocktail" |
|
|
sed -n 's/.*\(RPATH\|RUNPATH\).*\[\(.*\)\].*/\2/p')"
|
|
[[ "${dynamic_path}" == "${expected_dynamic_path}" ]] ||
|
|
Die "unexpected packaged mocktail RUNPATH: ${dynamic_path}"
|
|
|
|
packaged_interpreter="$(ReadElfInterpreter \
|
|
"${STAGING}/mocktail/bin/mocktail")"
|
|
[[ "${packaged_interpreter}" == "${TARGET_INTERPRETER}" ]] ||
|
|
Die "packaged mocktail interpreter changed unexpectedly"
|
|
packaged_libc="$(DetectElfLibc "${STAGING}/mocktail/bin/mocktail")"
|
|
[[ "${packaged_libc}" == "${TARGET_LIBC}" ]] ||
|
|
Die "packaged mocktail libc ABI changed unexpectedly"
|
|
grep -Fxq 'schema=2' "${STAGING}/mocktail/metadata/ABI.txt" ||
|
|
Die "portable ABI manifest has an invalid schema"
|
|
grep -Fxq 'architecture=x86_64' \
|
|
"${STAGING}/mocktail/metadata/ABI.txt" ||
|
|
Die "portable ABI manifest has an invalid architecture"
|
|
grep -Fxq "libc=${TARGET_LIBC}" \
|
|
"${STAGING}/mocktail/metadata/ABI.txt" ||
|
|
Die "portable ABI manifest has an invalid libc"
|
|
grep -Fxq "mode=${MODE}" "${STAGING}/mocktail/metadata/ABI.txt" ||
|
|
Die "portable ABI manifest has an invalid packaging mode"
|
|
grep -Fxq "interpreter=${TARGET_INTERPRETER}" \
|
|
"${STAGING}/mocktail/metadata/ABI.txt" ||
|
|
Die "portable ABI manifest has an invalid interpreter"
|
|
grep -Fxq "android_tools_libc=${ANDROID_TOOLS_ABI}" \
|
|
"${STAGING}/mocktail/metadata/ABI.txt" ||
|
|
Die "portable ABI manifest has an invalid Android tools ABI"
|
|
grep -Fxq "mode=${MODE}" \
|
|
"${STAGING}/mocktail/metadata/DEPENDENCIES.txt" ||
|
|
Die "portable dependency manifest has an invalid packaging mode"
|
|
|
|
if [[ "${MODE}" == thin ]] &&
|
|
find "${STAGING}/mocktail/lib" -mindepth 1 -maxdepth 1 \
|
|
-print -quit |
|
|
grep -q .; then
|
|
Die "thin bundle unexpectedly contains userspace dependency libraries"
|
|
fi
|
|
|
|
local artifact
|
|
for artifact in "${PROJECT_ARTIFACTS[@]}"; do
|
|
if [[ "${MODE}" == standalone ]]; then
|
|
dependencies="$(ListElfDependencies \
|
|
"${STAGING}/mocktail/bin/${artifact}")" ||
|
|
Die "packaged artifact has unresolved dependencies: ${artifact}"
|
|
else
|
|
ListElfNeededSonames \
|
|
"${STAGING}/mocktail/bin/${artifact}" >/dev/null ||
|
|
Die "cannot inspect packaged artifact dependencies: ${artifact}"
|
|
fi
|
|
done
|
|
if [[ "${MODE}" == standalone ]]; then
|
|
local support_elf
|
|
for support_elf in \
|
|
runtime/bin/bash runtime/jre/bin/java \
|
|
runtime/bin/bwrap runtime/bin/xdg-dbus-proxy \
|
|
libexec/webkitgtk-6.0/WebKitWebProcess \
|
|
libexec/gstreamer-1.0/gst-plugin-scanner; do
|
|
ListElfDependencies "${STAGING}/mocktail/${support_elf}" >/dev/null ||
|
|
Die "standalone support ELF has unresolved dependencies: ${support_elf}"
|
|
done
|
|
fi
|
|
local android_runtime="${STAGING}/mocktail/runtime/android-tools/bin"
|
|
local verification_path="${android_runtime}:${PATH}"
|
|
local verification_java_home="${JAVA_HOME:-}"
|
|
if [[ "${MODE}" == standalone ]]; then
|
|
verification_path="${STAGING}/mocktail/runtime/bin:${verification_path}"
|
|
verification_java_home="${STAGING}/mocktail/runtime/jre"
|
|
fi
|
|
if [[ "${ANDROID_TOOLS_ABI}" == java ]]; then
|
|
JAVA_HOME="${verification_java_home}" PATH="${verification_path}" \
|
|
"${android_runtime}/apkanalyzer" --help >/dev/null 2>&1
|
|
else
|
|
"${android_runtime}/aapt" version >/dev/null
|
|
fi
|
|
JAVA_HOME="${verification_java_home}" PATH="${verification_path}" \
|
|
"${android_runtime}/apksigner" \
|
|
version >/dev/null
|
|
(cd -- "${STAGING}" &&
|
|
sha256sum --quiet -c mocktail/metadata/SHA256SUMS.txt) ||
|
|
Die "packaged file checksum verification failed"
|
|
if [[ "${PACKAGING_HOST_LIBC}" == "${TARGET_LIBC}" ]]; then
|
|
MOCKTAIL_SKIP_HOST_CHECK=1 MOCKTAIL_SKIP_NAMESPACE_CHECK=1 \
|
|
"${STAGING}/run.sh" --help >/dev/null
|
|
else
|
|
Log "skipping cross-libc launcher execution during thin packaging"
|
|
fi
|
|
}
|
|
|
|
PublishBundle() {
|
|
if [[ -e "${OUTPUT}" ]]; then
|
|
[[ -d "${OUTPUT}" &&
|
|
( -f "${OUTPUT}/mocktail/metadata/.mocktail-portable-bundle" ||
|
|
-f "${OUTPUT}/.mocktail-portable-bundle" ) ]] ||
|
|
Die "refusing to replace unmarked output path: ${OUTPUT}"
|
|
rm -rf -- "${OUTPUT}"
|
|
fi
|
|
mv -- "${STAGING}" "${OUTPUT}"
|
|
STAGING=""
|
|
Log "bundle ready: ${OUTPUT}/run.sh"
|
|
}
|
|
|
|
BuildAppImage() {
|
|
local icon_path icon_relative_path
|
|
[[ -n "${APPIMAGE_OUTPUT}" ]] || return 0
|
|
APPDIR="$(mktemp -d "${OUTPUT%/*}/.Mocktail.AppDir.XXXXXX")"
|
|
mkdir -p "${APPDIR}/usr/share/mocktail-bundle" \
|
|
"${APPDIR}/usr/share/applications" \
|
|
"${APPDIR}/usr/share/icons/hicolor/scalable/apps"
|
|
cp -a -- "${OUTPUT}/." "${APPDIR}/usr/share/mocktail-bundle/"
|
|
ln -s "share/mocktail-bundle/run.sh" "${APPDIR}/usr/mocktail"
|
|
install -m 0755 -- "${PROJECT_ROOT}/packaging/AppRun" "${APPDIR}/AppRun"
|
|
install -m 0644 -- \
|
|
"${PROJECT_ROOT}/packaging/io.github.CoderDayton.nightcap.desktop" \
|
|
"${APPDIR}/io.github.CoderDayton.nightcap.desktop"
|
|
install -m 0644 -- \
|
|
"${PROJECT_ROOT}/packaging/io.github.CoderDayton.nightcap.desktop" \
|
|
"${APPDIR}/usr/share/applications/io.github.CoderDayton.nightcap.desktop"
|
|
install -m 0644 -- \
|
|
"${PROJECT_ROOT}/packaging/io.github.CoderDayton.nightcap.svg" \
|
|
"${APPDIR}/io.github.CoderDayton.nightcap.svg"
|
|
install -m 0644 -- \
|
|
"${PROJECT_ROOT}/packaging/io.github.CoderDayton.nightcap.svg" \
|
|
"${APPDIR}/usr/share/icons/hicolor/scalable/apps/io.github.CoderDayton.nightcap.svg"
|
|
for icon_path in \
|
|
"${PROJECT_ROOT}"/packaging/icons/hicolor/*x*/apps/io.github.CoderDayton.nightcap.png; do
|
|
[[ -f "${icon_path}" ]] || Die "hicolor icon set is incomplete"
|
|
icon_relative_path="${icon_path#"${PROJECT_ROOT}/packaging/icons/hicolor/"}"
|
|
install -D -m 0644 -- "${icon_path}" \
|
|
"${APPDIR}/usr/share/icons/hicolor/${icon_relative_path}"
|
|
done
|
|
ln -s io.github.CoderDayton.nightcap.svg "${APPDIR}/.DirIcon"
|
|
mkdir -p -- "$(dirname -- "${APPIMAGE_OUTPUT}")"
|
|
ARCH=x86_64 appimagetool "${APPDIR}" "${APPIMAGE_OUTPUT}"
|
|
rm -rf -- "${APPDIR}"
|
|
APPDIR=""
|
|
Log "AppImage ready: ${APPIMAGE_OUTPUT}"
|
|
}
|
|
|
|
DryRun() {
|
|
STAGING="$(mktemp -d "${TMPDIR:-/tmp}/mocktail-portable-dry-run.XXXXXX")"
|
|
CollectDependencies
|
|
printf 'mode=%s\n' "${MODE}"
|
|
printf 'libc=%s\n' "${TARGET_LIBC}"
|
|
printf 'artifact_name=%s\n' "${ARTIFACT_NAME}"
|
|
printf 'interpreter=%s\n' "${TARGET_INTERPRETER}"
|
|
printf 'android_tools_libc=%s\n' "${ANDROID_TOOLS_ABI}"
|
|
printf 'project_artifacts=%d\n' "${#PROJECT_ARTIFACTS[@]}"
|
|
printf 'bundled_dependency_count=%d\n' "${#COPIED_SOURCES[@]}"
|
|
printf 'host_dependency_count=%d\n' "${#HOST_REQUIREMENTS[@]}"
|
|
if (( ${#HOST_REQUIREMENTS[@]} > 0 )); then
|
|
local soname
|
|
while IFS= read -r soname; do
|
|
printf 'host=%s reason=%s\n' "${soname}" "${HOST_REQUIREMENTS[${soname}]}"
|
|
done < <(printf '%s\n' "${!HOST_REQUIREMENTS[@]}" | sort)
|
|
fi
|
|
}
|
|
|
|
Main() {
|
|
ParseArguments "$@"
|
|
if [[ -n "${APPIMAGE_OUTPUT}" && "${APPIMAGE_FORMAT}" == anylinux ]]; then
|
|
[[ "${MODE}" == standalone ]] ||
|
|
Die "AnyLinux is self-contained; use classic for a thin bundle"
|
|
[[ "${DRY_RUN}" == false ]] ||
|
|
Die "AnyLinux packaging requires a real native install, not a portable dry-run"
|
|
local -a anylinux_arguments=(
|
|
--build-dir "${BUILD_DIR}" --appimage "${APPIMAGE_OUTPUT}"
|
|
)
|
|
if [[ "${MOCKTAIL_ANYLINUX_SYSTEM_INSTALL:-0}" == 1 ]]; then
|
|
anylinux_arguments+=(--system-install)
|
|
fi
|
|
exec "${PROJECT_ROOT}/scripts/package_anylinux.sh" "${anylinux_arguments[@]}"
|
|
fi
|
|
RequireCommand readelf
|
|
RequireCommand patchelf
|
|
RequireCommand sha256sum
|
|
RequireCommand paste
|
|
RequireCommand unzip
|
|
if [[ -n "${APPIMAGE_OUTPUT}" ]]; then
|
|
RequireCommand appimagetool
|
|
fi
|
|
BUILD_DIR="$(CanonicalDirectory "${BUILD_DIR}")"
|
|
ValidateArtifacts
|
|
PACKAGING_HOST_LIBC="$(DetectHostLibc)"
|
|
[[ "${PACKAGING_HOST_LIBC}" != unknown ]] ||
|
|
Die "cannot determine packaging host libc"
|
|
if [[ "${MODE}" == standalone ]]; then
|
|
RequireCommand ldd
|
|
[[ "${PACKAGING_HOST_LIBC}" == "${TARGET_LIBC}" ]] ||
|
|
Die "standalone mode requires a matching ${TARGET_LIBC} host; detected ${PACKAGING_HOST_LIBC}"
|
|
fi
|
|
ResolveAndroidBuildTools
|
|
trap Cleanup EXIT
|
|
|
|
if [[ "${DRY_RUN}" == true ]]; then
|
|
DryRun
|
|
return 0
|
|
fi
|
|
|
|
[[ ! -L "${OUTPUT}" ]] || Die "refusing symlink output path: ${OUTPUT}"
|
|
OUTPUT="$(readlink -m -- "${OUTPUT}")"
|
|
[[ "${OUTPUT}" != / && "${OUTPUT}" != "${PROJECT_ROOT}" ]] ||
|
|
Die "unsafe output path: ${OUTPUT}"
|
|
mkdir -p -- "$(dirname -- "${OUTPUT}")"
|
|
STAGING="$(mktemp -d "${OUTPUT}.tmp.XXXXXX")"
|
|
CopyRuntimeTree
|
|
PackageStandaloneRuntime
|
|
PackageAndroidRuntime
|
|
CollectDependencies
|
|
WriteDependencyManifest
|
|
PatchRuntimePaths
|
|
BuildStandaloneNamespace
|
|
WriteAbiManifest
|
|
WriteBundleChecksums
|
|
VerifyBundle
|
|
PublishBundle
|
|
BuildAppImage
|
|
}
|
|
|
|
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
|
Main "$@"
|
|
fi
|