mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-09-11 01:23:15 -04:00
Linux appimage prebuild aurora (#121)
* Linux Appimage: statically prebuild Aurora (and all its dependencies) adds symlinks to the compiler locations in a static path that way rebuilds do not think that the compiler path has changed between appimage install commands * Update package.yml * Update Launcher/build-appimage.sh Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
@@ -42,10 +42,16 @@ jobs:
|
||||
|
||||
# appimagetool ships as an AppImage itself and needs to self-mount via FUSE to run directly;
|
||||
# GitHub's ubuntu-latest runners don't have libfuse2 preinstalled.
|
||||
- name: Install libfuse2 (required by appimagetool)
|
||||
- name: Install dependencies (required by appimagetool and SDL3 build)
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libfuse2
|
||||
sudo apt-get install -y libfuse2 build-essential git make \
|
||||
pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \
|
||||
libaudio-dev libfribidi-dev libjack-dev libsndio-dev libx11-dev libxext-dev \
|
||||
libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev \
|
||||
libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \
|
||||
libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libthai-dev libusb-1.0-0-dev \
|
||||
libpipewire-0.3-dev libwayland-dev libdecor-0-dev liburing-dev
|
||||
|
||||
- name: Build AppImage
|
||||
run: bash Launcher/build-appimage.sh
|
||||
|
||||
Executable
+590
@@ -0,0 +1,590 @@
|
||||
#!/usr/bin/env bash
|
||||
# Builds the redistributable precompiled aurora + third-party package for native Linux: aurora
|
||||
# (~43% of local build CPU time per Prepare-NativePrebuilt.ps1) and vendored Crypto++ are identical
|
||||
# for every user under the pinned toolchain prepare-portable-tools.sh bundles, so this configures
|
||||
# runtime/ against that toolchain, builds just that closure, and harvests the archives plus a
|
||||
# generated CMake description into an output package - the Linux counterpart to
|
||||
# Launcher/Prepare-NativePrebuilt.ps1, consumed by the same platform-agnostic
|
||||
# runtime/cmake/NativePrebuilt.cmake either script's package works with unmodified.
|
||||
#
|
||||
# Not a byte-for-byte port of the Windows script: Linux needs no offline pinned dependency cache.
|
||||
# Every FetchContent dependency in aurora-main/extern/CMakeLists.txt is a fixed-version URL already
|
||||
# (verified directly), and aurora-main/CMakeLists.txt's own _default_linkage is "static" on any
|
||||
# non-Windows platform. Windows pins Launcher/artifacts/dependencies because its installer ships
|
||||
# pre-fetched sources to end users for a fully offline build; this harvest only ever runs on a
|
||||
# maintainer's own machine (network access needed once, here, not shipped to anyone), so a plain
|
||||
# networked configure is exactly as reproducible - what actually makes the harvested archives safe
|
||||
# to link into any consumer's own build is the pinned compiler below, not an offline source cache.
|
||||
set -euo pipefail
|
||||
|
||||
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
workspace=$(cd "$script_dir/.." && pwd)
|
||||
|
||||
arch=""
|
||||
output_dir=""
|
||||
stage_dir="$workspace/build/native-prebuilt-stage"
|
||||
keep_stage=0
|
||||
reuse_stage=0
|
||||
parallel=0
|
||||
print_fingerprint_only=0
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: Prepare-NativePrebuilt.sh --arch {x86_64|aarch64} [options]
|
||||
|
||||
--arch ARCH Target architecture; selects the matching bundled toolchain (required)
|
||||
--output-dir DIR Where the package is written (default: Launcher/artifacts/native-prebuilt-ARCH)
|
||||
--stage-dir DIR Staging build directory (default: build/native-prebuilt-stage)
|
||||
--keep-stage Do not delete the staging build directory afterward
|
||||
--reuse-stage Reuse an existing staging build directory (maintainer iteration aid: a
|
||||
re-harvest does not recompile aurora from scratch)
|
||||
--parallel N Ninja build parallelism (default: nproc)
|
||||
--print-fingerprint-only Print the four provenance inputs (compiler_sha256, flag_fingerprint,
|
||||
aurora_fingerprint, third_party_fingerprint) as "key=value" lines and
|
||||
exit, without configuring/building/harvesting anything - lets a caller
|
||||
(build-appimage.sh) decide whether an existing package is still current
|
||||
without paying for a full aurora rebuild just to find out.
|
||||
EOF
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--arch) arch=$2; shift 2 ;;
|
||||
--output-dir) output_dir=$2; shift 2 ;;
|
||||
--stage-dir) stage_dir=$2; shift 2 ;;
|
||||
--keep-stage) keep_stage=1; shift ;;
|
||||
--reuse-stage) reuse_stage=1; shift ;;
|
||||
--parallel) parallel=$2; shift 2 ;;
|
||||
--print-fingerprint-only) print_fingerprint_only=1; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) echo "Prepare-NativePrebuilt.sh: unknown argument: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$arch" in
|
||||
x86_64|aarch64) ;;
|
||||
*) echo "Prepare-NativePrebuilt.sh: --arch must be x86_64 or aarch64" >&2; usage; exit 1 ;;
|
||||
esac
|
||||
|
||||
fail() { echo "Prepare-NativePrebuilt.sh: error: $*" >&2; exit 1; }
|
||||
assert_file() { [[ -f "$1" ]] || fail "$2 is missing: $1"; }
|
||||
assert_dir() { [[ -d "$1" ]] || fail "$2 is missing: $1"; }
|
||||
sha256_of() { sha256sum "$1" | awk '{print $1}'; }
|
||||
normalize() { readlink -f "$1"; }
|
||||
|
||||
[[ -n "$output_dir" ]] || output_dir="$script_dir/artifacts/native-prebuilt-$arch"
|
||||
[[ "$stage_dir" = /* ]] || stage_dir="$workspace/$stage_dir"
|
||||
|
||||
toolchain_dir="$script_dir/artifacts/portable-tools/toolchain-$arch"
|
||||
cc="$toolchain_dir/bin/clang"
|
||||
cxx="$toolchain_dir/bin/clang++"
|
||||
cmake_bin="$toolchain_dir/bin/cmake"
|
||||
ninja_bin="$toolchain_dir/bin/ninja"
|
||||
runtime_source="$workspace/runtime"
|
||||
aurora_source="$workspace/aurora-main"
|
||||
|
||||
assert_file "$cmake_bin" "Portable CMake (run prepare-portable-tools.sh --arch $arch first)"
|
||||
assert_file "$ninja_bin" "Portable Ninja"
|
||||
assert_file "$cc" "Portable C compiler"
|
||||
assert_file "$cxx" "Portable C++ compiler"
|
||||
assert_dir "$aurora_source" "aurora-main source tree"
|
||||
clang_binary=$(normalize "$toolchain_dir/bin/clang-22")
|
||||
assert_file "$clang_binary" "Portable clang driver binary"
|
||||
|
||||
(( parallel > 0 )) || parallel=$(nproc)
|
||||
|
||||
fingerprint_tree() {
|
||||
# $1 = root dir, remaining args = top-level subdirectory names to exclude
|
||||
local root=$1; shift
|
||||
root=$(normalize "$root")
|
||||
[[ -d "$root" ]] || return 0
|
||||
local find_args=("$root")
|
||||
local ex
|
||||
for ex in "$@"; do find_args+=(-path "$root/$ex" -prune -o); done
|
||||
find_args+=(-type f -print)
|
||||
find "${find_args[@]}" | LC_ALL=C sort | while IFS= read -r abs; do
|
||||
printf '%s %s\n' "${abs#$root/}" "$(sha256_of "$abs")"
|
||||
done | sha256sum | awk '{print $1}'
|
||||
}
|
||||
|
||||
# The fixed (path-independent) half of the configure command line - identical for every
|
||||
# harvest, which is what lets one package be valid regardless of where it was built.
|
||||
# -DBUILD_SHARED_LIBS=OFF: aurora-main/extern/CMakeLists.txt derives its own _USE_SHARED from
|
||||
# whether BUILD_SHARED_LIBS is *defined at all* (not its value), so leaving it unset would default
|
||||
# zlib/libpng to shared. -DAURORA_SDL3_PROVIDER=vendor: without it, "auto" resolves to "system" on
|
||||
# any machine that happens to have an SDL3 dev package installed. -DCMAKE_DISABLE_FIND_PACKAGE_*:
|
||||
# absl/PNG/Freetype each call find_package() unconditionally, with no provider flag to gate them
|
||||
# (unlike SDL3/Dawn) - verified directly that this machine's system libpng-dev/freetype-dev get
|
||||
# linked in shared instead of the pinned vendored source otherwise. Disabling find_package for
|
||||
# these three forces the same FetchContent-vendored, statically-built result regardless of what a
|
||||
# given maintainer's machine happens to have installed. ZLIB is deliberately NOT disabled the same
|
||||
# way: libpng's own vendored CMakeLists.txt calls find_package(ZLIB REQUIRED) internally even when
|
||||
# zlib came from aurora's own FetchContent (extern/CMakeLists.txt writes a redirect config for
|
||||
# exactly this), and CMAKE_DISABLE_FIND_PACKAGE_ZLIB errors out on any REQUIRED call site outright
|
||||
# (verified directly) - it cannot be scoped to only aurora's own initial, non-required check.
|
||||
# Dawn stays a prebuilt package regardless (Linux x86_64/aarch64 always auto-resolve to "package" -
|
||||
# see AuroraDawnProvider.cmake).
|
||||
fixed_configure_flags=(
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DBUILD_SHARED_LIBS=OFF
|
||||
-DAURORA_SDL3_PROVIDER=vendor
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_absl=ON
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_PNG=ON
|
||||
-DCMAKE_DISABLE_FIND_PACKAGE_Freetype=ON
|
||||
# Freetype's own vendored CMakeLists.txt separately probes for system BZip2 (optional
|
||||
# bzip2-compressed-font support aurora-main never asked for) regardless of the Freetype
|
||||
# find_package disable above, since that only stops aurora's own outer find_package(Freetype)
|
||||
# from picking up a system Freetype - it does not reach into the FetchContent-built copy's own
|
||||
# internal find_package(BZip2) call. FT_DISABLE_BZIP2 is Freetype's own documented flag for
|
||||
# exactly this (verified in its CMakeLists.txt), unlike the ZLIB/libpng situation where no such
|
||||
# source-level flag exists.
|
||||
-DFT_DISABLE_BZIP2=ON
|
||||
-DCMAKE_POLICY_DEFAULT_CMP0168=NEW
|
||||
)
|
||||
flag_fingerprint=$(printf '%s\n' "${fixed_configure_flags[@]}" | sha256sum | awk '{print $1}')
|
||||
|
||||
# extern/ is excluded because the payload ships that tree separately (aurora-main/extern is bundled
|
||||
# whole by build-appimage.sh); build/ is a plain developer build directory.
|
||||
aurora_fingerprint=$(fingerprint_tree "$aurora_source" extern build)
|
||||
[[ -n "$aurora_fingerprint" ]] || fail "The aurora source tree could not be fingerprinted: $aurora_source"
|
||||
|
||||
# The harvested Crypto++ archive is consumed against this tree's headers, so it is fingerprinted
|
||||
# for the same reason as aurora above. No exclusions: unlike aurora's extern/, nothing under
|
||||
# runtime/third_party is shipped separately.
|
||||
third_party_fingerprint=$(fingerprint_tree "$runtime_source/third_party")
|
||||
[[ -n "$third_party_fingerprint" ]] || fail "The vendored third-party tree could not be fingerprinted: $runtime_source/third_party"
|
||||
|
||||
compiler_sha256=$(sha256_of "$clang_binary")
|
||||
|
||||
if [[ "$print_fingerprint_only" -eq 1 ]]; then
|
||||
printf 'compiler_sha256=%s\n' "$compiler_sha256"
|
||||
printf 'flag_fingerprint=%s\n' "$flag_fingerprint"
|
||||
printf 'aurora_fingerprint=%s\n' "$aurora_fingerprint"
|
||||
printf 'third_party_fingerprint=%s\n' "$third_party_fingerprint"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# The package must never contain a stale mixture of two builds.
|
||||
rm -rf "$output_dir"
|
||||
mkdir -p "$output_dir/lib" "$output_dir/bin" "$output_dir/include"
|
||||
|
||||
# The staging build has to be configured without the package present, otherwise the runtime would
|
||||
# consume the very package this script is producing.
|
||||
if [[ "$reuse_stage" -eq 0 ]]; then rm -rf "$stage_dir"; fi
|
||||
export_dir="$stage_dir/export"
|
||||
mkdir -p "$export_dir"
|
||||
|
||||
# fixed_configure_flags/flag_fingerprint were already computed above (needed before the
|
||||
# --print-fingerprint-only early exit).
|
||||
echo "Prepare-NativePrebuilt.sh: configuring the aurora/third-party staging build..."
|
||||
"$cmake_bin" -S "$runtime_source" -B "$stage_dir" -G Ninja \
|
||||
"${fixed_configure_flags[@]}" \
|
||||
-DCMAKE_C_COMPILER="$cc" -DCMAKE_CXX_COMPILER="$cxx" \
|
||||
-DCMAKE_MAKE_PROGRAM="$ninja_bin" \
|
||||
-DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld" \
|
||||
-DMKW_NATIVE_PREBUILT_EXPORT_DIR="$export_dir"
|
||||
|
||||
# mkw_np_probe links exactly what mkw_runtime_common and the public products link, so building it
|
||||
# compiles the whole redistributable closure and nothing else - and a successful link proves the
|
||||
# harvested archives are complete.
|
||||
echo "Prepare-NativePrebuilt.sh: building the aurora/third-party closure..."
|
||||
"$cmake_bin" --build "$stage_dir" --target mkw_np_probe --parallel "$parallel"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Read the description CMake wrote, plus the resolved command lines Ninja holds.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
meta_path="$export_dir/meta.txt"
|
||||
assert_file "$meta_path" "Native prebuilt export metadata"
|
||||
get_meta() { awk -F= -v k="$1" '$0 ~ "^" k "=" { sub(/^[^=]*=/, ""); print; exit }' "$meta_path"; }
|
||||
|
||||
build_ninja="$stage_dir/build.ninja"
|
||||
assert_file "$build_ninja" "Generated build.ninja"
|
||||
|
||||
extract_ninja_vars() {
|
||||
# $1 = ninja file, $2 = exact rule name. Prints "KEY\tVALUE" for every " KEY = VALUE" line
|
||||
# following the first "build ...: RULE ..." statement that uses that rule.
|
||||
awk -v rule="$2" '
|
||||
BEGIN { in_stmt = 0 }
|
||||
/^build / {
|
||||
if (in_stmt) exit
|
||||
colon = index($0, ": ")
|
||||
if (colon == 0) next
|
||||
rest = substr($0, colon + 2)
|
||||
sp = index(rest, " ")
|
||||
stmt_rule = (sp > 0) ? substr(rest, 1, sp - 1) : rest
|
||||
if (stmt_rule == rule) in_stmt = 1
|
||||
next
|
||||
}
|
||||
in_stmt {
|
||||
if (substr($0, 1, 2) != " ") exit
|
||||
line = substr($0, 3)
|
||||
eq = index(line, " = ")
|
||||
if (eq == 0) exit
|
||||
print substr(line, 1, eq - 1) "\t" substr(line, eq + 3)
|
||||
}
|
||||
' "$1"
|
||||
}
|
||||
|
||||
declare -A probe_compile=() control_compile=() probe_link=() control_link=()
|
||||
load_vars() {
|
||||
local -n dest=$1
|
||||
local k v
|
||||
while IFS=$'\t' read -r k v; do dest["$k"]=$v; done < <(extract_ninja_vars "$build_ninja" "$2")
|
||||
}
|
||||
load_vars probe_compile "CXX_COMPILER__mkw_np_probe_unscanned_Release"
|
||||
load_vars control_compile "CXX_COMPILER__mkw_np_probe_control_unscanned_Release"
|
||||
load_vars probe_link "CXX_EXECUTABLE_LINKER__mkw_np_probe_Release"
|
||||
load_vars control_link "CXX_EXECUTABLE_LINKER__mkw_np_probe_control_Release"
|
||||
[[ -n "${probe_compile[INCLUDES]+x}" ]] || fail "No Ninja statement used rule CXX_COMPILER__mkw_np_probe_unscanned_Release."
|
||||
[[ -n "${probe_link[LINK_LIBRARIES]+x}" ]] || fail "No Ninja statement used rule CXX_EXECUTABLE_LINKER__mkw_np_probe_Release."
|
||||
|
||||
split_list() {
|
||||
# $1 = string value, $2 = name of the array to fill (nameref)
|
||||
local -n out=$2
|
||||
out=()
|
||||
[[ -n "$1" ]] || return 0
|
||||
read -ra out <<< "$1"
|
||||
}
|
||||
|
||||
subtract_multiset() {
|
||||
# $1 = "all" array name, $2 = "remove" array name, $3 = result array name (all namerefs)
|
||||
local -n all_ref=$1 remove_ref=$2 result_ref=$3
|
||||
local -A pending=()
|
||||
local item
|
||||
for item in "${remove_ref[@]}"; do
|
||||
pending["$item"]=$(( ${pending["$item"]:-0} + 1 ))
|
||||
done
|
||||
result_ref=()
|
||||
for item in "${all_ref[@]}"; do
|
||||
if [[ "${pending["$item"]:-0}" -gt 0 ]]; then
|
||||
pending["$item"]=$(( pending["$item"] - 1 ))
|
||||
continue
|
||||
fi
|
||||
result_ref+=("$item")
|
||||
done
|
||||
}
|
||||
|
||||
split_list "${probe_compile[INCLUDES]:-}" _probe_includes
|
||||
split_list "${control_compile[INCLUDES]:-}" _control_includes
|
||||
subtract_multiset _probe_includes _control_includes include_arguments
|
||||
|
||||
split_list "${probe_compile[DEFINES]:-}" _probe_defines
|
||||
split_list "${control_compile[DEFINES]:-}" _control_defines
|
||||
subtract_multiset _probe_defines _control_defines define_arguments
|
||||
|
||||
split_list "${probe_compile[FLAGS]:-}" _probe_flags
|
||||
split_list "${control_compile[FLAGS]:-}" _control_flags
|
||||
subtract_multiset _probe_flags _control_flags compile_options_raw
|
||||
|
||||
split_list "${probe_link[LINK_LIBRARIES]:-}" _probe_link
|
||||
split_list "${control_link[LINK_LIBRARIES]:-}" _control_link
|
||||
subtract_multiset _probe_link _control_link link_items
|
||||
|
||||
[[ ${#include_arguments[@]} -gt 0 ]] || fail "The aurora compile interface is empty; the probe did not link aurora."
|
||||
[[ ${#link_items[@]} -gt 0 ]] || fail "The aurora link interface is empty; the probe did not link aurora."
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Harvest the built libraries.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
binary_root=$(normalize "$stage_dir")
|
||||
aurora_root=$(normalize "$(get_meta aurora_dir)")
|
||||
runtime_root=$(normalize "$(get_meta runtime_dir)")
|
||||
|
||||
generated_include_index=0
|
||||
copy_harvested() {
|
||||
# $1 = source path, $2 = subdirectory under the package. Echoes "subdir/name".
|
||||
local source=$1 subdir=$2
|
||||
local name destination
|
||||
name=$(basename "$source")
|
||||
destination="$output_dir/$subdir/$name"
|
||||
[[ ! -e "$destination" ]] || fail "Two harvested files collide on the name $name: $source"
|
||||
cp "$source" "$destination"
|
||||
printf '%s/%s' "$subdir" "$name"
|
||||
}
|
||||
declare -A generated_include_tokens=()
|
||||
convert_to_token() {
|
||||
# $1 = absolute path, $2 = 'include' or another kind. Sets $TOKEN_RESULT - NOT echoed/command-
|
||||
# substituted: this needs to mutate generated_include_tokens/generated_include_index, and a
|
||||
# $(...) call runs in a subshell, silently discarding that mutation once it returns (verified
|
||||
# directly: every build-tree include directory collided into the same "generated01" until this
|
||||
# was written this way instead).
|
||||
local path kind=$2
|
||||
path=$(normalize "$1")
|
||||
case "$path" in
|
||||
"$aurora_root"/*) TOKEN_RESULT="@AURORA@/${path#$aurora_root/}"; return ;;
|
||||
"$runtime_root"/*) TOKEN_RESULT="@RUNTIME@/${path#$runtime_root/}"; return ;;
|
||||
"$binary_root"/*)
|
||||
[[ "$kind" == include ]] || fail "Unhandled build-tree artifact: $path"
|
||||
if [[ -n "${generated_include_tokens[$path]:-}" ]]; then
|
||||
TOKEN_RESULT=${generated_include_tokens[$path]}
|
||||
return
|
||||
fi
|
||||
generated_include_index=$((generated_include_index + 1))
|
||||
local name destination
|
||||
name=$(printf 'generated%02d' "$generated_include_index")
|
||||
destination="$output_dir/include/$name"
|
||||
mkdir -p "$destination"
|
||||
cp -a "$path/." "$destination/"
|
||||
TOKEN_RESULT="@PKG@/include/$name"
|
||||
generated_include_tokens[$path]=$TOKEN_RESULT
|
||||
return ;;
|
||||
*) fail "Path escapes every shippable root ($kind): $path" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
declare -A linker_file_to_reference=()
|
||||
shared_import_targets=() shared_import_runtime=() shared_import_implib=()
|
||||
targets_txt="$export_dir/targets.txt"
|
||||
assert_file "$targets_txt" "Native prebuilt export targets list"
|
||||
while IFS='|' read -r name type file linkerfile; do
|
||||
[[ -n "$name" ]] || continue
|
||||
[[ -f "$linkerfile" ]] || continue # not part of the closure mkw_np_probe pulled in
|
||||
linkerfile=$(normalize "$linkerfile")
|
||||
relative_linker=$(copy_harvested "$linkerfile" lib)
|
||||
if [[ "$type" == "SHARED_LIBRARY" ]]; then
|
||||
file_abs=$(normalize "$file")
|
||||
# Unlike Windows (a .dll + a separate .dll.a import library), a Linux shared object's
|
||||
# TARGET_FILE and TARGET_LINKER_FILE are the same path - one file, harvested once.
|
||||
if [[ "$file_abs" == "$linkerfile" ]]; then
|
||||
relative_runtime=$relative_linker
|
||||
else
|
||||
[[ -f "$file_abs" ]] || fail "Shared library $name has no runtime file: $file_abs"
|
||||
relative_runtime=$(copy_harvested "$file_abs" bin)
|
||||
fi
|
||||
shared_import_targets+=("mkw_np::$name")
|
||||
shared_import_runtime+=("$relative_runtime")
|
||||
shared_import_implib+=("$relative_linker")
|
||||
linker_file_to_reference["$linkerfile"]="mkw_np::$name"
|
||||
else
|
||||
linker_file_to_reference["$linkerfile"]="@PKG@/$relative_linker"
|
||||
fi
|
||||
done < "$targets_txt"
|
||||
# Unlike Windows (SDL/zlib/libpng ship as DLLs by default), everything here was forced static above
|
||||
# and Dawn's own Linux package (verified directly) ships libwebgpu_dawn.a, also static - so zero
|
||||
# shared imports is the expected, normal outcome, not a failure.
|
||||
echo "Prepare-NativePrebuilt.sh: harvested ${#linker_file_to_reference[@]} archive(s) (${#shared_import_targets[@]} shared)"
|
||||
|
||||
dawn_linker_file="" dawn_runtime_file=""
|
||||
dawn_txt="$export_dir/dawn.txt"
|
||||
if [[ -s "$dawn_txt" ]]; then
|
||||
IFS='|' read -r _ dawn_runtime_file dawn_linker_file < "$dawn_txt"
|
||||
dawn_runtime_file=$(normalize "$dawn_runtime_file")
|
||||
dawn_linker_file=$(normalize "$dawn_linker_file")
|
||||
fi
|
||||
[[ -n "$dawn_linker_file" ]] && linker_file_to_reference["$dawn_linker_file"]="dawn::webgpu_dawn"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Rewrite the compile/link interface into workspace-relative tokens.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
package_include_dirs=()
|
||||
include_count=${#include_arguments[@]}
|
||||
i=0
|
||||
while (( i < include_count )); do
|
||||
entry=${include_arguments[i]}
|
||||
case "$entry" in
|
||||
-I*) convert_to_token "${entry#-I}" include; package_include_dirs+=("$TOKEN_RESULT") ;;
|
||||
-isystem)
|
||||
i=$((i + 1))
|
||||
(( i < include_count )) || fail "Trailing -isystem with no argument"
|
||||
convert_to_token "${include_arguments[i]}" include; package_include_dirs+=("$TOKEN_RESULT") ;;
|
||||
-isystem*) convert_to_token "${entry#-isystem}" include; package_include_dirs+=("$TOKEN_RESULT") ;;
|
||||
*) fail "Unexpected include argument: $entry" ;;
|
||||
esac
|
||||
i=$((i + 1))
|
||||
done
|
||||
|
||||
package_definitions=()
|
||||
for entry in "${define_arguments[@]}"; do
|
||||
case "$entry" in
|
||||
# Ninja records a quoted define's value with its quotes backslash-escaped (verified
|
||||
# directly: IMGUI_USER_CONFIG=\"aurora/imgui_config.h\" in build.ninja) regardless of
|
||||
# platform - format_cmake_block below re-escapes for CMake's own string syntax, so the
|
||||
# literal backslash has to come out here first or the result is double-escaped and the
|
||||
# define expands to literal backslash-quote characters instead of a quoted string.
|
||||
-D*) package_definitions+=("${entry#-D}") ;;
|
||||
*) fail "Unexpected define argument: $entry" ;;
|
||||
esac
|
||||
done
|
||||
package_definitions=("${package_definitions[@]//\\\"/\"}")
|
||||
|
||||
# fmt's `-include cstdlib` (and anything like it) is recorded from a C++ compile line, so re-scope
|
||||
# it to C++. The runtime also assembles generated .S blobs through the same targets, and a forced
|
||||
# C++ header include would break them.
|
||||
package_compile_options=()
|
||||
for entry in "${compile_options_raw[@]}"; do
|
||||
package_compile_options+=("\$<\$<COMPILE_LANGUAGE:CXX>:$entry>")
|
||||
done
|
||||
|
||||
package_link_items=()
|
||||
for item in "${link_items[@]}"; do
|
||||
case "$item" in
|
||||
-*) package_link_items+=("$item"); continue ;;
|
||||
esac
|
||||
if [[ "$item" = /* ]]; then item_abs=$(normalize "$item"); else item_abs=$(normalize "$stage_dir/$item"); fi
|
||||
ref=${linker_file_to_reference["$item_abs"]:-}
|
||||
if [[ -n "$ref" ]]; then
|
||||
package_link_items+=("$ref")
|
||||
continue
|
||||
fi
|
||||
case "$item_abs" in
|
||||
*/libz.so*)
|
||||
# The one unavoidable system reference: libpng's own vendored CMakeLists.txt calls
|
||||
# find_package(ZLIB REQUIRED) internally even when zlib came from aurora's own
|
||||
# FetchContent (extern/CMakeLists.txt writes a redirect config for exactly this case),
|
||||
# and CMAKE_DISABLE_FIND_PACKAGE_ZLIB errors out on any REQUIRED call site outright
|
||||
# (verified directly), so it cannot be forced to vendor/static the way absl/PNG/Freetype
|
||||
# are above. zlib is as close to a universal baseline as a Linux shared library gets
|
||||
# (glibc-adjacent - practically every distro has it already), so this is recorded as a
|
||||
# portable `-lz` link flag instead of the harvesting machine's absolute path.
|
||||
package_link_items+=("-lz")
|
||||
continue ;;
|
||||
esac
|
||||
fail "Link item is neither a system library nor a harvested archive: $item"
|
||||
done
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Emit the consumer-facing CMake description.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
format_cmake_block() {
|
||||
# $1 = CMake variable name, remaining args = list items
|
||||
local name=$1; shift
|
||||
if [[ $# -eq 0 ]]; then
|
||||
printf 'set(%s "")\n' "$name"
|
||||
return
|
||||
fi
|
||||
printf 'set(%s\n' "$name"
|
||||
local item escaped
|
||||
for item in "$@"; do
|
||||
escaped=${item//\\/\\\\}
|
||||
escaped=${escaped//\"/\\\"}
|
||||
printf ' "%s"\n' "$escaped"
|
||||
done
|
||||
printf ')\n'
|
||||
}
|
||||
|
||||
dawn_config_dir_meta=$(get_meta dawn_config_dir)
|
||||
dawn_config_token=""
|
||||
if [[ -n "$dawn_config_dir_meta" ]]; then
|
||||
dawn_config_dir_abs=$(normalize "$dawn_config_dir_meta")
|
||||
case "$dawn_config_dir_abs" in
|
||||
"$binary_root"/*)
|
||||
# Dawn's find_package() config directory (lib/cmake/Dawn under its own fetched package
|
||||
# root) cannot go through the generic single-directory copy other build-tree artifacts
|
||||
# use: DawnConfig.cmake/DawnTargets.cmake derive _IMPORT_PREFIX/PACKAGE_PREFIX_DIR from
|
||||
# their OWN file location by walking up three parent directories (verified directly), so
|
||||
# the whole package root - include/, lib/, lib/cmake/Dawn/ together - must be copied as
|
||||
# one self-contained unit for that relative navigation to still resolve once moved.
|
||||
dawn_package_root=$(normalize "$dawn_config_dir_abs/../../..")
|
||||
dawn_config_relative=${dawn_config_dir_abs#$dawn_package_root/}
|
||||
mkdir -p "$output_dir/include/dawn_package"
|
||||
cp -a "$dawn_package_root/." "$output_dir/include/dawn_package/"
|
||||
dawn_config_token="@PKG@/include/dawn_package/$dawn_config_relative"
|
||||
;;
|
||||
*) convert_to_token "$dawn_config_dir_abs" include; dawn_config_token=$TOKEN_RESULT ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
generated_cmake="$output_dir/native_prebuilt.cmake"
|
||||
{
|
||||
echo "# Generated by Launcher/Prepare-NativePrebuilt.sh - do not edit."
|
||||
echo "#"
|
||||
echo "# Describes the precompiled aurora + third-party archives that replace a"
|
||||
echo "# from-source aurora-main build. Every path is a token resolved against the"
|
||||
echo "# consuming workspace, because the source trees still ship next to this"
|
||||
echo "# package and the runtime compiles against their headers."
|
||||
echo ""
|
||||
echo 'if(NOT MKW_NP_PACKAGE_DIR OR NOT MKW_NP_AURORA_DIR OR NOT MKW_NP_RUNTIME_DIR OR NOT MKW_NP_DEPS_DIR)'
|
||||
echo ' message(FATAL_ERROR "native_prebuilt.cmake must be included by runtime/cmake/NativePrebuilt.cmake")'
|
||||
echo 'endif()'
|
||||
echo ""
|
||||
format_cmake_block MKW_NP_INCLUDE_DIRECTORIES "${package_include_dirs[@]}"
|
||||
format_cmake_block MKW_NP_COMPILE_DEFINITIONS "${package_definitions[@]}"
|
||||
format_cmake_block MKW_NP_COMPILE_OPTIONS "${package_compile_options[@]}"
|
||||
format_cmake_block MKW_NP_LINK_LIBRARIES "${package_link_items[@]}"
|
||||
format_cmake_block MKW_NP_AURORA_TARGETS "aurora::gx" "aurora::pad" "aurora::si" "aurora::vi" "aurora::mtx"
|
||||
echo ""
|
||||
printf 'set(MKW_NP_DAWN_CONFIG_DIR "%s")\n' "$dawn_config_token"
|
||||
printf 'set(MKW_NP_DAWN_VERSION "%s")\n' "$(get_meta aurora_dawn_version)"
|
||||
echo ""
|
||||
echo "# Shared third-party libraries keep imported SHARED targets so that"
|
||||
echo '# $<TARGET_RUNTIME_DLLS> still copies them next to the game executable.'
|
||||
for idx in "${!shared_import_targets[@]}"; do
|
||||
printf 'add_library(%s SHARED IMPORTED GLOBAL)\n' "${shared_import_targets[$idx]}"
|
||||
printf 'set_target_properties(%s PROPERTIES\n' "${shared_import_targets[$idx]}"
|
||||
printf ' IMPORTED_LOCATION "${MKW_NP_PACKAGE_DIR}/%s"\n' "${shared_import_runtime[$idx]}"
|
||||
printf ' IMPORTED_IMPLIB "${MKW_NP_PACKAGE_DIR}/%s")\n' "${shared_import_implib[$idx]}"
|
||||
done
|
||||
} > "$generated_cmake"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Provenance: the pinned toolchain is what makes a precompiled archive interchangeable with
|
||||
# locally compiled objects (bit-for-bit the compiler and flag set the user's machine will use);
|
||||
# LocalBuild.ps1's Windows equivalent re-checks both before consuming a package, and a Linux
|
||||
# consumer-side check (once local-build.sh gains --native-prebuilt-dir) should do the same.
|
||||
# compiler_sha256/flag_fingerprint/aurora_fingerprint/third_party_fingerprint were already computed
|
||||
# above (needed before the --print-fingerprint-only early exit).
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
dawn_runtime_sha256=""
|
||||
[[ -n "$dawn_runtime_file" && -f "$dawn_runtime_file" ]] && dawn_runtime_sha256=$(sha256_of "$dawn_runtime_file")
|
||||
[[ -n "$dawn_runtime_sha256" ]] || fail "The Dawn runtime could not be hashed: $dawn_runtime_file"
|
||||
|
||||
compiler_version=$(get_meta cxx_compiler_version)
|
||||
sdl3_target=$(get_meta aurora_sdl3_target)
|
||||
dawn_version=$(get_meta aurora_dawn_version)
|
||||
harvested_count=${#linker_file_to_reference[@]}
|
||||
|
||||
python3 - "$output_dir" "$compiler_sha256" "$compiler_version" "$flag_fingerprint" \
|
||||
"$dawn_version" "$dawn_runtime_sha256" "$aurora_fingerprint" "$third_party_fingerprint" \
|
||||
"$sdl3_target" "$harvested_count" <<'PY'
|
||||
import hashlib, json, os, sys, datetime
|
||||
|
||||
(output_dir, compiler_sha256, compiler_version, flag_fingerprint, dawn_version,
|
||||
dawn_runtime_sha256, aurora_fingerprint, third_party_fingerprint, sdl3_target,
|
||||
harvested_count) = sys.argv[1:]
|
||||
|
||||
contents = []
|
||||
for root, dirs, files in os.walk(output_dir):
|
||||
dirs.sort()
|
||||
for name in sorted(files):
|
||||
if name == "provenance.json":
|
||||
continue
|
||||
path = os.path.join(root, name)
|
||||
rel = os.path.relpath(path, output_dir).replace(os.sep, "/")
|
||||
with open(path, "rb") as fh:
|
||||
digest = hashlib.sha256(fh.read()).hexdigest()
|
||||
contents.append({"Path": rel, "Bytes": os.path.getsize(path), "Sha256": digest})
|
||||
contents.sort(key=lambda c: c["Path"])
|
||||
|
||||
provenance = {
|
||||
"SchemaVersion": 1,
|
||||
"BuiltUtc": datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z",
|
||||
"CompilerSha256": compiler_sha256,
|
||||
"CompilerVersion": compiler_version,
|
||||
"FlagFingerprint": flag_fingerprint,
|
||||
"DawnVersion": dawn_version,
|
||||
"DawnRuntimeSha256": dawn_runtime_sha256,
|
||||
"AuroraSourceFingerprint": aurora_fingerprint,
|
||||
"ThirdPartySourceFingerprint": third_party_fingerprint,
|
||||
"Sdl3Target": sdl3_target,
|
||||
"HarvestedLibraryCount": int(harvested_count),
|
||||
"Contents": contents,
|
||||
}
|
||||
with open(os.path.join(output_dir, "provenance.json"), "w", encoding="utf-8") as fh:
|
||||
json.dump(provenance, fh, indent=2)
|
||||
fh.write("\n")
|
||||
|
||||
total_bytes = sum(c["Bytes"] for c in contents)
|
||||
print(f" files: {len(contents)}; size: {total_bytes / (1024 * 1024):.1f} MiB")
|
||||
PY
|
||||
|
||||
if [[ "$keep_stage" -eq 0 ]]; then rm -rf "$stage_dir"; fi
|
||||
|
||||
echo ""
|
||||
echo "Native prebuilt package: $output_dir"
|
||||
echo " archives: $(find "$output_dir/lib" -maxdepth 1 -type f | wc -l); shared imports: ${#shared_import_targets[@]}"
|
||||
echo " flag fingerprint: $flag_fingerprint"
|
||||
@@ -13,7 +13,8 @@ internal static class BuildRunner
|
||||
string workspace, string profile, string outputDir, string? baseOutputDir,
|
||||
string? retroDir, string? retroWfcOfflineDir, bool skipRetroWfcPayload,
|
||||
bool forceCleanBuild, string? translatorBin, string? ccBin, string? cxxBin, string? fuseLd,
|
||||
string? cmakeBin, string? ninjaBin, IInstallReporter reporter, CancellationToken cancellationToken)
|
||||
string? cmakeBin, string? ninjaBin, string? nativePrebuiltDir, IInstallReporter reporter,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var script = Path.Combine(workspace, "Launcher", "local-build.sh");
|
||||
if (!File.Exists(script)) throw new FileNotFoundException("local-build.sh is missing", script);
|
||||
@@ -70,6 +71,12 @@ internal static class BuildRunner
|
||||
{
|
||||
startInfo.ArgumentList.Add("--ninja"); startInfo.ArgumentList.Add(ninjaBin);
|
||||
}
|
||||
// Forwarded by AppRun so the AppImage's bundled precompiled aurora/third-party package (see
|
||||
// Prepare-NativePrebuilt.sh) is used instead of local-build.sh compiling aurora-main itself.
|
||||
if (!string.IsNullOrEmpty(nativePrebuiltDir))
|
||||
{
|
||||
startInfo.ArgumentList.Add("--native-prebuilt-dir"); startInfo.ArgumentList.Add(nativePrebuiltDir);
|
||||
}
|
||||
|
||||
using var process = new Process { StartInfo = startInfo };
|
||||
var window = new BuildProgressWindow(reporter, InstallStages.Build, start: 6, end: 96);
|
||||
|
||||
@@ -155,6 +155,7 @@ internal static class Program
|
||||
flags.GetValueOrDefault("fuse-ld"),
|
||||
flags.GetValueOrDefault("cmake"),
|
||||
flags.GetValueOrDefault("ninja"),
|
||||
flags.GetValueOrDefault("native-prebuilt-dir"),
|
||||
reporter, token);
|
||||
|
||||
reporter.Progress(InstallStages.Shortcuts, "Creating shortcuts", 98);
|
||||
@@ -323,7 +324,7 @@ internal static class Program
|
||||
{--download-retro-wfc-payload | --skip-retro-wfc-payload}]
|
||||
[--force-clean-build] [--translator-bin PATH] [--disc-tool-bin PATH]
|
||||
[--cc PATH] [--cxx PATH] [--fuse-ld NAME_OR_PATH] [--cmake PATH] [--ninja PATH]
|
||||
[--progress-json] [--workspace DIR]
|
||||
[--native-prebuilt-dir DIR] [--progress-json] [--workspace DIR]
|
||||
uninstall
|
||||
launch-base
|
||||
launch-retro
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
# A pruned native clang/lld/cmake/ninja toolchain (see prepare-portable-tools.sh) is bundled the
|
||||
# same way - AppRun passes --cc/--cxx/--fuse-ld/--cmake/--ninja so local-build.sh never has to find
|
||||
# a system compiler, CMake, or Ninja. It still shells out to system pkg-config and Vulkan headers,
|
||||
# matching Launcher/local-build.sh's own remaining prerequisites.
|
||||
# matching Launcher/local-build.sh's own remaining prerequisites. A precompiled aurora +
|
||||
# third-party package (see Prepare-NativePrebuilt.sh) is bundled the same way too - AppRun passes
|
||||
# --native-prebuilt-dir so local-build.sh never compiles aurora-main from source at all.
|
||||
#
|
||||
# An AppImage mounts read-only, but local-build.sh writes generated/, native-build/, Assets/, etc.
|
||||
# into the workspace it's given. So AppRun (written below) copies the bundled workspace snapshot
|
||||
@@ -17,7 +19,17 @@
|
||||
# - generated/native-build/Assets/PulsarPacks live only in that writable cache and are never
|
||||
# touched by the sync, so local-build.sh's own incremental caching survives across runs and across
|
||||
# AppImage updates. translator/ isn't part of this snapshot at all: it's published as its own
|
||||
# self-contained binary (usr/bin/translator-cli) below and never needs a writable copy.
|
||||
# self-contained binary (usr/bin/translator-cli) below and never needs a writable copy. Neither
|
||||
# native-prebuilt/ nor the toolchain are copied into the cache either - both are large
|
||||
# (~90 MiB / ~500 MiB) and local-build.sh only ever reads from them - but AppRun does point
|
||||
# $CACHE/toolchain and $CACHE/native-prebuilt symlinks at the current mount on every single launch
|
||||
# (see AppRun's own comment): an AppImage's FUSE mount is at a fresh random /tmp/.mount_XXXXXX
|
||||
# every run, and CMake bakes whatever compiler/tool path it's given directly into each
|
||||
# build.ninja rule's command line, so referencing $HERE straight would change that command line -
|
||||
# and Ninja reruns any rule whose command line changed - forcing a full rebuild on every single
|
||||
# launch even though the compiler itself never actually changed. The symlink keeps the path
|
||||
# string CMake/Ninja see identical across runs while what it resolves to tracks the current mount
|
||||
# underneath.
|
||||
set -euo pipefail
|
||||
|
||||
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
@@ -114,6 +126,42 @@ bash "$script_dir/prepare-portable-tools.sh" --arch "$appimagetool_arch"
|
||||
mkdir -p "$appdir/usr/toolchain"
|
||||
cp -a "$workspace/Launcher/artifacts/portable-tools/toolchain-$appimagetool_arch"/. "$appdir/usr/toolchain/"
|
||||
|
||||
# Precompiled aurora + third-party package (see Prepare-NativePrebuilt.sh) so a user's own
|
||||
# local-build.sh never has to compile aurora itself (~43% of local build CPU time). Re-harvesting
|
||||
# recompiles the whole aurora/Crypto++ closure with the toolchain above, so this is skipped unless
|
||||
# --print-fingerprint-only (a fast, build-free check) says the existing package no longer matches
|
||||
# the current compiler/flags/aurora/third_party sources.
|
||||
native_prebuilt_dir="$workspace/Launcher/artifacts/native-prebuilt-$appimagetool_arch"
|
||||
echo "Checking whether the precompiled aurora + third-party package ($appimagetool_arch) is current..."
|
||||
current_fingerprint=$(bash "$script_dir/Prepare-NativePrebuilt.sh" --arch "$appimagetool_arch" --print-fingerprint-only)
|
||||
package_current=0
|
||||
if [[ -f "$native_prebuilt_dir/provenance.json" ]]; then
|
||||
package_current=$(CURRENT_FINGERPRINT="$current_fingerprint" python3 - "$native_prebuilt_dir/provenance.json" <<'PY'
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
provenance = json.load(open(sys.argv[1], encoding="utf-8"))
|
||||
current = dict(line.split("=", 1) for line in os.environ["CURRENT_FINGERPRINT"].splitlines() if line)
|
||||
fields = {
|
||||
"compiler_sha256": "CompilerSha256",
|
||||
"flag_fingerprint": "FlagFingerprint",
|
||||
"aurora_fingerprint": "AuroraSourceFingerprint",
|
||||
"third_party_fingerprint": "ThirdPartySourceFingerprint",
|
||||
}
|
||||
print(1 if all(provenance.get(v) == current.get(k) for k, v in fields.items()) else 0)
|
||||
PY
|
||||
)
|
||||
fi
|
||||
if [[ "$package_current" == "1" ]]; then
|
||||
echo "Native prebuilt package is current; reusing $native_prebuilt_dir"
|
||||
else
|
||||
echo "Native prebuilt package is missing or stale; harvesting a fresh one (compiles aurora once, can take a while)..."
|
||||
bash "$script_dir/Prepare-NativePrebuilt.sh" --arch "$appimagetool_arch"
|
||||
fi
|
||||
mkdir -p "$appdir/native-prebuilt"
|
||||
cp -a "$native_prebuilt_dir/." "$appdir/native-prebuilt/"
|
||||
|
||||
echo "Staging the bundled workspace snapshot..."
|
||||
for dir in runtime aurora-main projects; do
|
||||
cp -r "$workspace/$dir" "$appdir/workspace/$dir"
|
||||
@@ -127,8 +175,21 @@ find "$appdir/workspace/aurora-main/extern" -mindepth 1 -maxdepth 1 -type d -exe
|
||||
rm -rf "$appdir/workspace/runtime/build"
|
||||
cp "$workspace/Launcher/local-build.sh" "$appdir/workspace/Launcher/local-build.sh"
|
||||
|
||||
# AppRun re-syncs runtime/aurora-main/projects/local-build.sh into the writable cache only when
|
||||
# this changes, so it must change whenever any of those bundled paths actually did - a bare commit
|
||||
# hash gets this wrong for an uncommitted change (verified directly: rebuilding after editing
|
||||
# local-build.sh with no commit produced the same hash as the stale cache, so AppRun kept serving
|
||||
# the old script and failed on a flag that didn't exist yet). `git status --porcelain` catches both
|
||||
# modified tracked files and new untracked ones; appending a fresh timestamp when it's non-empty
|
||||
# guarantees this never matches a previous build's stamp, forcing a resync every time the tree is
|
||||
# dirty. A clean tree (a real tagged release) keeps the stable commit-hash behavior, so identical
|
||||
# reruns of the same release AppImage don't resync needlessly.
|
||||
if git -C "$workspace" rev-parse HEAD >/dev/null 2>&1; then
|
||||
git -C "$workspace" rev-parse HEAD > "$appdir/workspace/.bundle-version"
|
||||
version=$(git -C "$workspace" rev-parse HEAD)
|
||||
if [[ -n "$(git -C "$workspace" status --porcelain 2>/dev/null)" ]]; then
|
||||
version="$version-dirty-$(date -u +%s)"
|
||||
fi
|
||||
echo "$version" > "$appdir/workspace/.bundle-version"
|
||||
else
|
||||
date -u +%s > "$appdir/workspace/.bundle-version"
|
||||
fi
|
||||
@@ -139,6 +200,7 @@ cat > "$appdir/AppRun" <<'APPRUN'
|
||||
set -euo pipefail
|
||||
HERE="$(dirname "$(readlink -f "$0")")"
|
||||
CACHE="${XDG_DATA_HOME:-$HOME/.local/share}/WiiCompiled/workspace"
|
||||
mkdir -p "$CACHE"
|
||||
if [ ! -f "$CACHE/.bundle-version" ] || \
|
||||
[ "$(cat "$HERE/workspace/.bundle-version")" != "$(cat "$CACHE/.bundle-version")" ]; then
|
||||
mkdir -p "$CACHE/Launcher"
|
||||
@@ -149,14 +211,31 @@ if [ ! -f "$CACHE/.bundle-version" ] || \
|
||||
cp "$HERE/workspace/Launcher/local-build.sh" "$CACHE/Launcher/local-build.sh"
|
||||
cp "$HERE/workspace/.bundle-version" "$CACHE/.bundle-version"
|
||||
fi
|
||||
# toolchain/ and native-prebuilt/ are NOT copied into the cache (they're large - ~500 MiB /
|
||||
# ~90 MiB - and local-build.sh only ever reads from them): $CACHE/toolchain and
|
||||
# $CACHE/native-prebuilt are symlinks re-pointed at the current mount on every single launch
|
||||
# (unconditionally, not gated on .bundle-version above, since the mount path itself - unlike the
|
||||
# bundled content - changes every run regardless). CMake bakes a compiler/tool path directly into
|
||||
# each build.ninja rule's command line and Ninja reruns any rule whose command line changed since
|
||||
# the last build (verified directly) - an AppImage's FUSE mount is at a fresh random
|
||||
# /tmp/.mount_XXXXXX every launch, so referencing $HERE straight would change that command line,
|
||||
# and therefore force a full rebuild, on every single run even though the compiler itself never
|
||||
# actually changed. A symlink keeps the *path string* CMake/Ninja see identical across runs while
|
||||
# what it resolves to tracks the current mount underneath (verified directly: CMake records
|
||||
# whatever path it's given as-is - including a symlink - without resolving it first).
|
||||
[ -L "$CACHE/toolchain" ] || rm -rf "$CACHE/toolchain"
|
||||
[ -L "$CACHE/native-prebuilt" ] || rm -rf "$CACHE/native-prebuilt"
|
||||
ln -sfn "$HERE/usr/toolchain" "$CACHE/toolchain"
|
||||
ln -sfn "$HERE/native-prebuilt" "$CACHE/native-prebuilt"
|
||||
exec "$HERE/usr/bin/wiicompiled-setup" --workspace "$CACHE" \
|
||||
--translator-bin "$HERE/usr/bin/translator-cli" \
|
||||
--disc-tool-bin "$HERE/usr/bin/nodtool" \
|
||||
--cc "$HERE/usr/toolchain/bin/clang" \
|
||||
--cxx "$HERE/usr/toolchain/bin/clang++" \
|
||||
--cc "$CACHE/toolchain/bin/clang" \
|
||||
--cxx "$CACHE/toolchain/bin/clang++" \
|
||||
--fuse-ld lld \
|
||||
--cmake "$HERE/usr/toolchain/bin/cmake" \
|
||||
--ninja "$HERE/usr/toolchain/bin/ninja" "$@"
|
||||
--cmake "$CACHE/toolchain/bin/cmake" \
|
||||
--ninja "$CACHE/toolchain/bin/ninja" \
|
||||
--native-prebuilt-dir "$CACHE/native-prebuilt" "$@"
|
||||
APPRUN
|
||||
chmod +x "$appdir/AppRun"
|
||||
|
||||
|
||||
+59
-6
@@ -2,11 +2,12 @@
|
||||
# Linux build automation: translate -> emit build shards -> configure -> compile -> publish.
|
||||
#
|
||||
# This is the native-Linux counterpart to Launcher/LocalBuild.ps1. It is a from-scratch parallel
|
||||
# implementation, not a port of NativeBuildFlags.ps1: that file's canonical flags and
|
||||
# prebuilt-package fingerprinting exist only for the Windows/mingw toolchain (a precompiled
|
||||
# aurora/third-party package, offline pinned dependencies) that this script does not build.
|
||||
# Linux always builds aurora from source, letting its own CMake auto-detect Vulkan + vendor
|
||||
# SDL3/Dawn via FetchContent - the same configuration already verified working by hand.
|
||||
# implementation, not a port of NativeBuildFlags.ps1: that file's canonical flags exist only for
|
||||
# the Windows/mingw toolchain's offline pinned dependencies, which this script does not use.
|
||||
# Without --native-prebuilt-dir, aurora is built from source, letting its own CMake auto-detect
|
||||
# Vulkan + vendor SDL3/Dawn via FetchContent - the same configuration already verified working by
|
||||
# hand. With it, aurora is not compiled at all - see Launcher/Prepare-NativePrebuilt.sh, which
|
||||
# harvests exactly that package (the Linux counterpart to Prepare-NativePrebuilt.ps1).
|
||||
set -euo pipefail
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -63,6 +64,7 @@ dotnet_override=""
|
||||
translator_dll_override=""
|
||||
translator_bin_override=""
|
||||
fuse_ld_override=""
|
||||
native_prebuilt_dir=""
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
@@ -83,6 +85,8 @@ Usage: local-build.sh --output-dir DIR [options]
|
||||
--dotnet PATH dotnet executable (default: on PATH)
|
||||
--translator-dll PATH Pre-built Translator.Cli.dll (skips building the translator; still needs --dotnet to run it)
|
||||
--translator-bin PATH Self-contained Translator.Cli executable (skips building AND needs no dotnet at all)
|
||||
--native-prebuilt-dir DIR Precompiled aurora/third-party package (see Prepare-NativePrebuilt.sh);
|
||||
skips compiling aurora-main from source entirely
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -105,6 +109,7 @@ while [[ $# -gt 0 ]]; do
|
||||
--dotnet) dotnet_override=$2; shift 2 ;;
|
||||
--translator-dll) translator_dll_override=$2; shift 2 ;;
|
||||
--translator-bin) translator_bin_override=$2; shift 2 ;;
|
||||
--native-prebuilt-dir) native_prebuilt_dir=$2; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) fail "unknown argument: $1" ;;
|
||||
esac
|
||||
@@ -160,6 +165,10 @@ require_command "$ninja_bin" ninja
|
||||
require_command "$cc_bin" cc
|
||||
require_command "$cxx_bin" cxx
|
||||
|
||||
if [[ -n "$native_prebuilt_dir" ]]; then
|
||||
assert_file "$native_prebuilt_dir/native_prebuilt.cmake" "Native prebuilt package"
|
||||
fi
|
||||
|
||||
project=$workspace/projects/mkwii/recomp.yml
|
||||
assets=$workspace/Assets
|
||||
generated=$workspace/generated
|
||||
@@ -339,15 +348,39 @@ translator "${shard_args[@]}"
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
keep_native_build=0
|
||||
stale_reason=""
|
||||
if [[ -f "$build/CMakeCache.txt" ]]; then
|
||||
expected_home=$workspace/runtime
|
||||
cache_home=$(grep '^CMAKE_HOME_DIRECTORY:INTERNAL=' "$build/CMakeCache.txt" | cut -d= -f2- || true)
|
||||
if [[ -n "$cache_home" && "$(cd "$cache_home" 2>/dev/null && pwd)" == "$expected_home" ]]; then
|
||||
keep_native_build=1
|
||||
# CMake auto-detects and caches auxiliary tool paths (CMAKE_AR/RANLIB/LINKER/ASM_COMPILER
|
||||
# and their per-language *_AR/*_RANLIB variants) only once, the first time a language's
|
||||
# compiler is checked - unlike CMAKE_C_COMPILER/CMAKE_CXX_COMPILER, which get overwritten by
|
||||
# the -D flags below on every configure, these are never refreshed on a plain reconfigure.
|
||||
# An AppImage's own AppRun works around this at the source by keeping --cc/--cxx/--cmake/
|
||||
# --ninja pointed at a stable symlink it re-targets at the current mount every launch
|
||||
# (see build-appimage.sh), so the *path string* CMake caches never actually changes run to
|
||||
# run - but this check stays as a general fallback for any tool path that goes stale some
|
||||
# other way (a moved/removed system toolchain, a relocated portable-tools directory, etc):
|
||||
# any :FILEPATH= cache entry whose recorded path no longer exists means this cache belongs
|
||||
# to a toolchain location that's gone - not just a workspace/path mismatch.
|
||||
while IFS= read -r tool_path; do
|
||||
[[ -n "$tool_path" ]] || continue
|
||||
# CMake's own sentinel for "this optional tool was legitimately never found" (e.g.
|
||||
# clang-scan-deps, not required here) - not a path at all, and not a sign of anything
|
||||
# stale. Without this exclusion, every configure wiped the cache unconditionally.
|
||||
[[ "$tool_path" != *-NOTFOUND ]] || continue
|
||||
if [[ ! -e "$tool_path" ]]; then
|
||||
keep_native_build=0
|
||||
stale_reason=" (cached tool path no longer exists: $tool_path)"
|
||||
break
|
||||
fi
|
||||
done < <(grep -o ':FILEPATH=.*' "$build/CMakeCache.txt" | sed 's/^:FILEPATH=//')
|
||||
fi
|
||||
fi
|
||||
if [[ -d "$build" && "$keep_native_build" -eq 0 ]]; then
|
||||
echo "MKWCBUILD: The native build cache does not belong to this workspace path; rebuilding from scratch"
|
||||
echo "MKWCBUILD: The native build cache does not belong to this workspace path or toolchain; rebuilding from scratch$stale_reason"
|
||||
rm -rf "$build"
|
||||
elif [[ "$keep_native_build" -eq 1 ]]; then
|
||||
echo "MKWCBUILD: Reusing the incremental native build directory"
|
||||
@@ -358,9 +391,29 @@ configure_args=(-S "$workspace/runtime" -B "$build" -G Ninja
|
||||
-DCMAKE_C_COMPILER="$cc_bin" -DCMAKE_CXX_COMPILER="$cxx_bin"
|
||||
-DCMAKE_MAKE_PROGRAM="$ninja_bin"
|
||||
-DMKW_TRANSLATED_COMPILE_JOBS="$translated_jobs")
|
||||
# CMAKE_C_COMPILER/CXX_COMPILER stay stable across AppImage runs on their own (they're exactly
|
||||
# what's passed via -D above, and AppRun points --cc/--cxx at a symlink it re-targets at the
|
||||
# current mount every launch - see build-appimage.sh). CMAKE_AR/RANLIB/LINKER/ASM_COMPILER do NOT
|
||||
# inherit that stability just because $cc_bin does: verified directly that even with a stable
|
||||
# --cc symlink, CMake's own auto-detection of these still resolved to the *real*, ephemeral mount
|
||||
# path underneath (clang's own driver locates its sibling llvm-ar/ld.lld tools by resolving its own
|
||||
# invoked path, symlinks included, rather than trusting the symlink CMake invoked it through) -
|
||||
# each subsequent run's differing command line then made Ninja rebuild every object from scratch
|
||||
# even though nothing had actually changed. Deriving these from $cc_bin (itself already stable)
|
||||
# and re-passing them explicitly every configure keeps them pinned to the same stable value too.
|
||||
if [[ "$cc_bin" == */* ]]; then
|
||||
toolchain_bin=$(dirname "$cc_bin")
|
||||
[[ -x "$toolchain_bin/llvm-ar" ]] && configure_args+=(-DCMAKE_AR="$toolchain_bin/llvm-ar" -DCMAKE_ASM_COMPILER_AR="$toolchain_bin/llvm-ar" -DCMAKE_C_COMPILER_AR="$toolchain_bin/llvm-ar" -DCMAKE_CXX_COMPILER_AR="$toolchain_bin/llvm-ar")
|
||||
[[ -x "$toolchain_bin/llvm-ranlib" ]] && configure_args+=(-DCMAKE_RANLIB="$toolchain_bin/llvm-ranlib" -DCMAKE_ASM_COMPILER_RANLIB="$toolchain_bin/llvm-ranlib" -DCMAKE_C_COMPILER_RANLIB="$toolchain_bin/llvm-ranlib" -DCMAKE_CXX_COMPILER_RANLIB="$toolchain_bin/llvm-ranlib")
|
||||
[[ -x "$toolchain_bin/ld.lld" ]] && configure_args+=(-DCMAKE_LINKER="$toolchain_bin/ld.lld")
|
||||
configure_args+=(-DCMAKE_ASM_COMPILER="$cc_bin")
|
||||
fi
|
||||
if [[ -n "$fuse_ld_override" ]]; then
|
||||
configure_args+=(-DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=$fuse_ld_override")
|
||||
fi
|
||||
if [[ -n "$native_prebuilt_dir" ]]; then
|
||||
configure_args+=(-DMKW_NATIVE_PREBUILT_DIR="$native_prebuilt_dir")
|
||||
fi
|
||||
|
||||
log_step configure-native "Configuring the native toolchain"
|
||||
"$cmake_bin" "${configure_args[@]}"
|
||||
|
||||
@@ -51,6 +51,16 @@ foreach(_dir IN LISTS _mkw_np_includes)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
# Dawn's own packaged config (DawnTargets.cmake) links dawn::webgpu_dawn against
|
||||
# Threads::Threads directly. A from-source aurora build resolves that as a side effect of
|
||||
# add_subdirectory(aurora-main) pulling in Dawn's own CMakeLists.txt; this mode never runs that
|
||||
# subdirectory at all, so nothing else would ever define it (verified directly: configuring without
|
||||
# this fails with "The link interface of target dawn::webgpu_dawn contains: Threads::Threads but
|
||||
# the target was not found"). find_package(Threads) is one of CMake's most basic finder modules and
|
||||
# is a no-op on Windows (its threading support is already part of the CRT), so this is safe on
|
||||
# every platform this package format targets, not just the one that first hit the failure.
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# Dawn is a prebuilt package on both sides; resolve the same install tree the
|
||||
# package was built against so its imported target (and therefore
|
||||
# webgpu_dawn.dll / dxcompiler.dll / dxil.dll) is available exactly as a
|
||||
|
||||
Reference in New Issue
Block a user