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:
theofficialgman
2026-09-03 04:20:24 -04:00
committed by GitHub
parent f2165a5aa6
commit 2bcfdca199
7 changed files with 763 additions and 17 deletions
+8 -2
View File
@@ -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
+590
View File
@@ -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);
+2 -1
View File
@@ -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
+86 -7
View File
@@ -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
View File
@@ -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[@]}"
+10
View File
@@ -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