mirror of
https://github.com/patchzyy/wiicompiled
synced 2026-09-10 17:16:47 -04:00
2bcfdca199
* 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>
591 lines
28 KiB
Bash
Executable File
591 lines
28 KiB
Bash
Executable File
#!/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"
|