mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-11-05 23:37:35 +00:00
Resolve relative paths once, retain successful namespace conversion, and use the resolved absolute path when conversion fails. Preserve the DLL directory cookie and Win32 errors across cleanup. Apply only to Linux-built Win64 ghc914-sh v2 target RTS packages. Add a real-module scope check and native caller boundary regression; Windows runtime qualification remains separate.
510 lines
32 KiB
Markdown
510 lines
32 KiB
Markdown
# Haskell builds with Nix
|
||
|
||
The compiler and all cross-compilers come from the haskell.nix input pinned in
|
||
`flake.lock`. The `ghc914-sh` compiler is the stable-haskell fork, not the stock
|
||
GHC 9.14 release from GHCup. Use the same source checkout and lock file for every
|
||
platform; Android ARMv7 does not require a separate branch or tag.
|
||
|
||
`compiler-selection.nix` and `compiler-cache-overlay.nix` match Hydra's compiler
|
||
build context, including auxiliary iserv projects, so the pinned compiler can use
|
||
its existing cache. Application dependencies keep the project's current packages.
|
||
|
||
On Linux x86_64, `native-rts-project.nix` patches the final compiler's RTS ELF
|
||
loader to honor section alignment even when its file offset is unaligned.
|
||
The stage2 RTS package patch preserves the configured source, stage1 compiler,
|
||
plan, and package IDs, but rebuilds the final compiler and its dependent slices.
|
||
Native, auxiliary, and cross compiler selection share this patched compiler.
|
||
The ELF fix does not patch target RTS sources or target iserv runtimes,
|
||
including musl.
|
||
|
||
The focused regression runs before the native x86_64 Linux CI build. To run it
|
||
from this checkout, use `nix build .#checks.x86_64-linux.ghc-elf-alignment -L`.
|
||
It loads the same 8192-byte C object with aligned and deliberately packed section
|
||
file offsets, requiring both to retain 64-byte memory alignment and their data.
|
||
It is a flake check, separate from the 21 existing Hydra artifact outputs.
|
||
|
||
On Intel Darwin, `darwin-info-table-project.nix` patches the GHC x86 backend in
|
||
both compiler stages. It marks info-table entry symbols as Mach-O alternate
|
||
entries, keeping each table and its code in the same linker subsection. The
|
||
observed Setup GC abort reads closure type zero because LLD inserts eight bytes
|
||
between a valid `FUN_STATIC` table and its entry. Relinking the actual cached
|
||
`directory` object reproduces that layout error; changing only alternate-entry
|
||
symbol flags preserves the table's original 24-byte distance and closure type 14.
|
||
The patched compiler rebuild and full Intel outputs still require CI validation.
|
||
|
||
Run `nix build .#checks.x86_64-darwin.ghc-macho-info-tables -L` to compile a Haskell
|
||
fixture with the selected compiler and check its table placement after a real
|
||
LLD link. CI runs this check after the Intel artifact builds. It checks object
|
||
metadata, table distances, and closure types without executing the fixture.
|
||
|
||
Intel Darwin projects also declare bitvec's Clang runtime dependency before
|
||
planning. GHC's object linker needs the selected C compiler's
|
||
`libclang_rt.osx.a` for `__builtin_cpu_supports`; its CPU model is otherwise
|
||
unresolved during Template Haskell. The SIMD implementation remains enabled.
|
||
The patch declares both ordinary and static C libraries for TH and rolled archives.
|
||
The patched local dependency keeps its tests and benchmarks disabled.
|
||
|
||
Cross projects add `ghc-internal` to the Cabal dependencies of the pinned
|
||
`composition-1.0.2.2` source. This preserves its deliberate avoidance of `base`
|
||
while making GHC 9.14's wired-in types available in isolated target package
|
||
databases. The patch runs before planning and source packaging. Native builds
|
||
and the Mac-to-iOS conversion projects keep the original package metadata.
|
||
|
||
Musl projects declare OpenSSL in Cabal's `extra-libraries-static` field for
|
||
`direct-sqlcipher` and `simplexmq`. GHC's fully static link path reads that field,
|
||
so an ordinary `extra-libraries` entry alone does not supply `libcrypto`. These
|
||
source metadata patches run before planning and require Cabal specification 3.8.
|
||
|
||
Linux-built musl `ghc914-sh` iserv projects use a package-specific
|
||
`executable-static: False` policy before Cabal v2 planning. The build-time
|
||
interpreter needs musl's dynamic C loader for Template Haskell dependencies;
|
||
a fully static musl executable has a `dlopen` stub. Its Haskell library ways
|
||
and profiling selection stay unchanged, and the final CLI remains fully static.
|
||
An i386-only preplanning source patch omits the private `__aio_close` symbol
|
||
registration: shared musl exposes `close`, which calls its hidden AIO hook
|
||
internally, rather than exporting that implementation detail.
|
||
The scope check uses the real Nix module fixed point in the Linux static CI
|
||
group, including `pkgs` supplied through `_module.args`. Repeat it with
|
||
`nix eval --impure --json --file scripts/nix/check-musl-iserv-scope.nix --apply 'f: f { lib = (builtins.getFlake (toString ./.)).inputs.nixpkgs.lib; }'`.
|
||
|
||
The i386 musl target RTS also handles duplicate hidden global function
|
||
signatures at the start of a sole executable COMDAT member. A known function
|
||
size must cover the whole member. GCC's `__x86.get_pc_thunk.*` PIC helpers use
|
||
zero symbol size (unknown extent); that case permits only the signature plus
|
||
local untyped labels and section metadata in the member. Additional typed or
|
||
nonlocal definitions remain errors. Coalescing is checked against the current
|
||
symbol owner during discovery and loading; it
|
||
does not persist weak-symbol status or suppress ordinary strong conflicts.
|
||
Multi-member groups remain outside this patch's scope. The source patch applies
|
||
to application and interpreter projects, including profiling. Native compilers
|
||
and other cross targets keep their existing RTS sources.
|
||
|
||
Run the native x86_64 Linux regression with
|
||
`nix build .#checks.x86_64-linux.ghc-elf-comdat-functions --no-link -L --max-jobs 1 --timeout 7200`.
|
||
It links the same configured RTS sources with and without the patch, verifies
|
||
the replacement objects in both linker maps, checks relocations in both load
|
||
orders, and rejects strong conflicts and stale archive-owner decisions. Each
|
||
probe has a 15-second bound. Module scope is checked separately by
|
||
`scripts/nix/check-musl-rts-scope.nix` using the same invocation as the iserv
|
||
scope check above.
|
||
|
||
Windows patches Basement's `INVALID_HANDLE_VALUE` expression to `INT_PTR`, so
|
||
hsc2hs receives the signed pointer-sized integer expected by the Haskell API.
|
||
The generated constant converts through Haskell `Int` with `Prelude.fromIntegral`;
|
||
Basement's `RebindableSyntax` has no `HasNegation IntPtr` instance.
|
||
Windows also plans the already selected `crypton-x509-system-1.6.8` as a local
|
||
patched source, so its Cabal metadata names `crypt32` before source packaging.
|
||
The pinned v2 builder otherwise replaces a package's patched Cabal file with its
|
||
original description override and copies that override into the slice index.
|
||
The source producer applies the existing haskell.nix patch once; its later
|
||
package patch list is empty to prevent a second application.
|
||
|
||
Linux-built Win64 `ghc914-sh` v2 projects patch only the RTS DLL search-path
|
||
caller, including normal and profiling interpreters. It resolves relative paths
|
||
once, retains successful namespace conversion for long paths, and uses the
|
||
resolved absolute path if conversion returns NULL. Invalid paths fail without
|
||
passing NULL to `AddDllDirectory`; its opaque cookie and Win32 errors survive
|
||
cleanup. Other filesystem helpers and native compilers keep their sources.
|
||
The native caller regression uses scripted APIs, not Wine or a target ABI test:
|
||
`python3 scripts/nix/test-windows-dll-search-path.py --rts-source /path/to/configured-ghc/rts`.
|
||
`scripts/nix/check-windows-rts-scope.nix` checks the real module fixed point using
|
||
the same pinned-lib invocation as the musl scope check above. Production Win64
|
||
RTS compilation and the canonical Wine TH gate remain runtime qualification.
|
||
|
||
Run these commands from the repository root:
|
||
|
||
```sh
|
||
make help
|
||
make nix-list # list this host's existing Hydra outputs
|
||
make nix-native # native library and CLI
|
||
make nix-android # ARM64 + ARMv7 libraries, on x86_64-linux
|
||
make nix-windows # Windows library and CLI, on x86_64-linux
|
||
make nix-linux-static # x86_64, i686, and ARM64 static Linux CLIs
|
||
make nix-build # all existing outputs for this build host
|
||
```
|
||
|
||
Each command builds this checkout, without cloning another branch. Output links
|
||
are placed under `result-nix/<build-system>/<package>`. ZIP filenames inside the
|
||
outputs retain the existing Hydra names. For example, the ARMv7 client library is
|
||
`result-nix/x86_64-linux/armv7a-android:lib:simplex-chat/pkg-armv7a-android-libsimplex.zip`.
|
||
Nix includes tracked working-tree changes; add new source files to Git before
|
||
building so they are included.
|
||
|
||
For a remote builder, select its build system explicitly. Nix must already have
|
||
that builder configured:
|
||
|
||
```sh
|
||
make nix-ios NIX_SYSTEM=aarch64-darwin
|
||
make nix-darwin-libs NIX_SYSTEM=x86_64-darwin
|
||
make nix-build NIX_SYSTEM=aarch64-linux
|
||
```
|
||
|
||
`nix-ios` produces the Haskell static-library ZIP. It does not run Xcode, sign an
|
||
iOS application, or upload to TestFlight. ARM64 device libraries and Intel
|
||
simulator libraries retain their separate outputs. The existing iOS preparation
|
||
scripts retag ARM64 archives with `mac2ios -s` when an Apple Silicon simulator is
|
||
needed.
|
||
|
||
The Cabal v2 builder stores ordinary package archives in its staged store.
|
||
`link-mobile-library.sh` relinks the selected package and its dependencies before
|
||
that store is pruned. iOS ZIPs contain both the plain `libHSsimplex-chat-*.a` and
|
||
the rolled `libHSsimplex-chat-*-ghc*.a` used by `update-pbxproj.sh`. Android and
|
||
Windows clients link the threaded RTS into `libsimplex.so` or `libsimplex.dll`;
|
||
Android's separate `libsupport.so` resolves RTS symbols from the client library.
|
||
Android client export flags use the ordered `chat-exports.txt` manifest. The
|
||
desktop library scripts check that manifest and `libsimplex.dll.def` against the
|
||
Haskell exports before building; update the manifest when adding an export.
|
||
Run `scripts/desktop/test-exports.sh` to check all three guards without compiling.
|
||
ARM64 Android libraries retain the 16 KiB linker alignment. These steps run in
|
||
the derivation's build/install phases because v2 component overrides do not run
|
||
the older `postBuild` and `postInstall` hooks.
|
||
|
||
Mobile builds keep the RTS's small-address-space setting. On Darwin, the final
|
||
link uses ABI-checked replacement RTS registrations, plus a replacement `text`
|
||
on ARM64. These boot libraries and all ARM64 mobile app C/C++ code target the
|
||
Apple A7 instruction baseline before `mac2ios` retags the archives. These mobile replacements use the cached ARM64
|
||
Darwin compiler; the Intel compiler receives the info-table patch described above. Android applies the RTS source patch
|
||
when building its boot libraries. The Android overlay also supplies GNU iconv
|
||
headers and a static PIC archive to `ghc-internal`, including the separate
|
||
iserv projects. The `ghc-internal` system dependency propagates GNU iconv to
|
||
interpreter consumers as well. Both Android interpreters register the GNU iconv
|
||
function addresses for runtime object loading. The ARM64 table uses the selected
|
||
`iconv.h` to distinguish GNU and Bionic, preserving plain lookup names alongside
|
||
GNU names when GNU supplies the definitions. Actual API26/API35 C controls check
|
||
these names against the target archive and the `ghc-internal` consumer; the
|
||
Bionic API35 object stays byte-identical. ARM32 source is patched before Cabal
|
||
planning, removing false missing-function stubs that shadow real static Bionic
|
||
startup routines. Android links libffi through the RTS package dependency.
|
||
Android preplanning patches remove the separate `pthread` library declarations
|
||
from crypton and direct-sqlcipher. Bionic supplies these functions in `libc`;
|
||
the NDK has no `libpthread`. Other targets retain their original declarations,
|
||
and compiler `-pthread` flags and the threaded RTS remain enabled. The local
|
||
crypton override retains its dependency-only scope; it does not add the package's
|
||
tests or benchmarks as planning goals. Repeat the
|
||
scope regression with `nix eval --json --file scripts/nix/check-android-pthread-scope.nix`.
|
||
The minimum Android API stays at 26.
|
||
For Android below API28, SplitMix's initializer uses Bionic's existing
|
||
`arc4random_buf` rather than the newer `getentropy` API. The dependency patch
|
||
preserves the OS entropy source; API28 and later keep the original initializer.
|
||
The ARM32 interpreter registers the real `arc4random_buf` address for loaded
|
||
objects on that route; the ARM64 interpreter already contains that registration.
|
||
The ARM64 interpreter registers `getentropy` and `getrandom` only from API28,
|
||
matching the NDK headers and versioned shared-library exports. API26 builds omit
|
||
those entries; API35 retains them.
|
||
The interpreter source patch applies to both Android ABIs; only ARM32 replaces
|
||
the emulator. The Android CI group runs the pure overlay-scope regression with
|
||
`nix eval --json --file scripts/nix/check-android-iserv-scope.nix`.
|
||
|
||
Android ARM32's QEMU wrapper preserves the guest's `PER_LINUX32` execution
|
||
domain when an ARM64 host kernel rejects that domain. Upper personality flags
|
||
still go through the host, and queries and setters retain the previous guest
|
||
value. This QEMU patch applies only to the Android ARM32 emulator;
|
||
the other emulator targets stay unchanged. Android's libc initializes
|
||
this state again at startup after an exec.
|
||
|
||
A standalone C regression checks startup, full personality values, the host's
|
||
`UNAME26` effect, and independent thread/fork state. Run it from this checkout
|
||
with configured Linux builders; Nix supplies the pinned Android API26 NDK and
|
||
patched QEMU. The NDK compiles the tiny payload on x86_64 Linux; QEMU then runs
|
||
on the selected Linux architecture. For example, on an Apple Silicon Mac whose
|
||
Linux builder supports both x86_64 and ARM64:
|
||
|
||
```sh
|
||
nix build --impure --file scripts/nix/check-android-personality.nix \
|
||
--argstr system aarch64-linux --max-jobs 1 --cores 1 --print-build-logs
|
||
```
|
||
|
||
Use `--argstr system x86_64-linux` for an x86_64 Linux builder. This optional
|
||
check does not add a Hydra output. It checks C startup and personality handling;
|
||
it does not qualify GHC/base under Rosetta, cross-TH, or Android device behavior.
|
||
The static C fixture supplies the ARM
|
||
[TLS alignment input](https://android.googlesource.com/platform/bionic/+/cc9b100%5E!/)
|
||
added to newer Android CRTs; its minimum API stays at 26. On 2026-10-15 this
|
||
regression passed on native ARM64 Linux, native x86_64 Linux, and x86_64 Linux
|
||
through Rosetta on an Apple Silicon Mac.
|
||
|
||
`NIX_ARGS` passes additional options to `nix build`, and `NIX_OUT_DIR` changes the
|
||
output-link directory. Each derivation has a two-hour timeout by default; set
|
||
`NIX_BUILD_TIMEOUT` in seconds to change it. For a wall-clock limit on a local
|
||
batch, use `timeout` from GNU coreutils to include environment setup in the limit:
|
||
|
||
```sh
|
||
timeout 6h nix develop -c make nix-build
|
||
make nix-native NIX_ARGS=--dry-run
|
||
```
|
||
|
||
`.github/workflows/nix.yml` runs these same outputs on Linux ARM64/x86_64 and
|
||
Darwin ARM64/x86_64, with a six-hour limit per build job. Its seven build jobs cover all 21
|
||
outputs once: x86_64 Linux runs `native`, `android`, `windows`, and `linux-static`
|
||
separately; ARM64 Linux runs `native`; both Darwin hosts run `all`. A failed
|
||
native Linux build therefore leaves the cross-build jobs running. PRs, pushes,
|
||
and default manual runs retain that complete matrix. A manual dispatch can
|
||
select the existing three `linux-static` outputs with
|
||
`gh workflow run nix.yml --ref BRANCH -f group=linux-static`, or the four Android
|
||
outputs with `gh workflow run nix.yml --ref BRANCH -f group=android`, or the two
|
||
Windows outputs with `gh workflow run nix.yml --ref BRANCH -f group=windows`. The registered
|
||
workflow can run on a branch containing this workflow file; it does not require
|
||
that file on the default branch. Runs for the same workflow/ref queue without
|
||
canceling an active run; manual groups have separate queues. Legacy PR build jobs also preserve running
|
||
jobs; their cancellation policy for other events and release tags is unchanged.
|
||
A two-minute planning job selects these
|
||
existing groups; it does not evaluate or build Nix outputs. Each build job
|
||
uploads its existing library ZIPs and CLIs under an artifact named for the build
|
||
host, target group, and source SHA. Each native CLI also runs
|
||
the encrypted-store and profile-password smoke test, which can be repeated with
|
||
`python3 scripts/nix/smoke-cli.py result-nix/BUILD_SYSTEM/exe:simplex-chat/bin/simplex-chat`.
|
||
|
||
The Windows job first prepares only the named direct input outputs of the
|
||
canonical `wai-app-static` component from the same Windows CLI project, using
|
||
the normal 7,200-second per-derivation limit. It then builds that exact component
|
||
with a 300-second limit; success is reused by the full Windows build. A failure
|
||
keeps the job red and skips the full outputs. CI replays only that failed
|
||
component in a separate diagnostic derivation, with `ISERV_ARGS=-v` and
|
||
`strace -f -tt -T -s 80` around compiler calls. It copies the exact selected Wine
|
||
scripts and appends `+seh,+loaddll` to their pinned `WINEDEBUG` value, with an
|
||
assertion that no other script bytes change. Only compile calls receive the
|
||
copied `-pgmi` path. Compiler queries, source/plan, original arguments, DLL
|
||
collection, Wine prefix and normal/profile selection stay unchanged.
|
||
The trace has a 15-minute step bound and a 300-second per-derivation limit;
|
||
strace is fetched from caches only. Logs and the selected derivation/input
|
||
metadata are uploaded even on failure. Repeat the canonical gate on Linux with
|
||
`python3 scripts/nix/windows-th-gate.py prepare` followed by
|
||
`python3 scripts/nix/windows-th-gate.py build`; use `trace` only after a failed
|
||
canonical attempt. These commands use the committed checkout and preserve the
|
||
canonical Nix core setting. A fresh Wine prefix and full syscall tracing can
|
||
consume most of the diagnostic budget; a timeout does not identify a deadlock.
|
||
The diagnostic changes its derivation and process
|
||
environment; it does not qualify a Windows artifact or change the failed result.
|
||
|
||
After a failed native x86_64 Linux library build, CI tries a separate derivation
|
||
with `-v2 -ddump-timings` and GDB on compile calls and uploads
|
||
`native-linux-ghc-phase-trace.log`, including when that trace fails. Compiler
|
||
queries and the original source, plan, dependency units and GHC arguments stay
|
||
unchanged. GDB follows the compiler's shell wrapper into the actual ELF program,
|
||
preserving its injected package DB and `-B` arguments. On a stopped fault it
|
||
records up to 80 frames per thread, registers, the PC symbol and disassembly,
|
||
shared libraries and memory mappings. It does not dump the environment or a core
|
||
file. Normal compiler exit codes propagate; faults and debugger errors fail the
|
||
diagnostic. The canonical build remains failed. The trace runs one derivation at
|
||
a time and inherits the canonical Nix core setting, preserving Setup's job count
|
||
and the compiler job arguments. It has a two-hour wall-clock/per-derivation
|
||
bound, including cache-only debugger setup; a debugger cache miss fails visibly
|
||
without compiling GDB locally or remotely. The job's six-hour limit stays in
|
||
place. A local `--cores` override also
|
||
changes those job arguments and must be reported when comparing diagnostics.
|
||
`trace-native-ghc.nix` accepts the original component via `--arg original` and an
|
||
optional pinned GDB derivation via `--arg debugger`. Omitting `debugger` retains
|
||
the phase-only trace. GDB leaves ASLR enabled; its host/runtime effects must be
|
||
reported when comparing diagnostics.
|
||
|
||
When the pinned Intel Darwin `ghc-boot` component fails, CI captures a separate
|
||
Setup diagnostic. It selects one exact failed `ghc-boot` derivation from the
|
||
canonical log and checks it against the selected compiler project; a rebuilt
|
||
compiler therefore does not leave a stale diagnostic path. Multiple matching
|
||
failed components cause the probe to skip. It preserves the newly linked Setup
|
||
executable's bytes and
|
||
runs its actual configure arguments with an explicit capture mode. LLDB mode
|
||
records bounded thread backtraces, registers, instructions and loaded images;
|
||
compiler queries and other Setup commands pass through. Child failures remain
|
||
failures, and the canonical job stays failed. The diagnostic has a two-hour
|
||
step/per-derivation limit and one five-minute deadline for LLDB plus any fallback.
|
||
Its log is
|
||
uploaded as `intel-setup-lldb-trace.log`, including after failure. Only this probe
|
||
disables the Nix sandbox to reach host tools; it records that runtime
|
||
difference and the debugger version. Hosted Intel uses `direct-abort` mode:
|
||
LLDB hosts the Python command without attempting debugger attachment, and the
|
||
probe invokes Setup once with a diagnostic abort interposer built by the
|
||
runner's existing Apple Clang. LLDB mode also retains a fallback for an explicit
|
||
permission denial before a child PID is assigned.
|
||
It reports up to 80 current-thread unwind frames, available callee-saved
|
||
registers and image addresses. It symbolicates mapped register addresses without
|
||
dereferencing them, then forwards to real `abort`. Its constructor
|
||
restores the original loader environment before Setup starts compiler children
|
||
and disables core output for the traced process tree. This adds a loaded image
|
||
to Setup; it changes no debugger permissions or security settings. Nonzero
|
||
exits and terminating signals remain failures. The hosted Intel replay captured
|
||
14 frames through `evacuate1` and `scavenge_loop1`. A local Setup built with the
|
||
same cached compiler and target dependency IDs reproduces the panic in 1.55
|
||
seconds, and its traced replay matches nine cloud frame offsets. Native ARM64
|
||
and Rosetta/x86 C controls also cover the helper.
|
||
A successful probe is diagnostic evidence.
|
||
|
||
Hydra continues to consume
|
||
the same `hydraJobs` attributes. The older application release workflow still
|
||
uses its existing GHCup/Docker compiler settings; migrating that packaging and
|
||
release path is separate from this Nix build lane. The legacy GHC 8.10 Linux job
|
||
now reports container memory counters and kernel OOM records after a failed CLI
|
||
build, with a one-minute diagnostic limit. Its observed SIGKILL alone does not
|
||
establish an out-of-memory cause.
|
||
|
||
Hydra needs builders advertising `aarch64-darwin` and `x86_64-darwin` to run the
|
||
corresponding Mac/iOS jobs. Check its [builder pool](https://ci.zw3rk.com/machines)
|
||
when jobs remain queued. For cross builds, the pinned haskell.nix uses
|
||
`recursive-nix` as a scheduling marker; these builds do not need recursive Nix
|
||
operations. This checkout opts `ghc914-sh` Android ARM32 out of that restriction
|
||
on Linux with patched QEMU 9.1–11, after passing normal target TH under emulation.
|
||
Other target/compiler package sets retain the upstream guard, which also covers
|
||
cross-compiler stalls without QEMU. Advertise the marker on a native x86_64 Linux
|
||
builder for those jobs; keep it off Rosetta slots. Nix also requires the
|
||
[`recursive-nix` experimental feature](https://nix.dev/manual/nix/2.35/development/experimental-features.html#xp-feature-recursive-nix)
|
||
when building derivations with this reserved marker. Enable both
|
||
`extra-experimental-features = recursive-nix` and
|
||
`extra-system-features = recursive-nix` on that builder. The GitHub workflow
|
||
sets the complete `experimental-features` list, including `nix-command` and
|
||
`flakes`, because its installer suppresses those defaults when this setting is
|
||
present. It adds `recursive-nix` to both lists only for its native x86_64 Linux
|
||
runner. The upstream fixes and regression jobs
|
||
are in [haskell.nix PR #2585](https://github.com/input-output-hk/haskell.nix/pull/2585).
|
||
|
||
To build both Android APK ABIs from the current checkout, preserving the same
|
||
compiler and source, use the existing builder's source/skip-checkout flags:
|
||
|
||
```sh
|
||
scripts/android/build-android.sh -sg VERSION_CODE
|
||
```
|
||
|
||
This command also needs the Android SDK, Gradle, and its other existing tools.
|
||
The APK reproduction script now checks out the supplied tag for both ABIs.
|
||
Historical releases that used a separate `-armv7a` tag must be reproduced with
|
||
their historical build scripts.
|
||
|
||
## Migration validation
|
||
|
||
On 2026-10-16, all 21 existing Hydra derivations evaluated successfully at
|
||
`f26dbbc0d93a5c359427307b4c57f7f5e2123d69`: 11 on x86_64 Linux, two on ARM64 Linux,
|
||
and four on each Darwin architecture. This includes both Android ABIs, Windows,
|
||
and all three static Linux targets. This establishes derivation evaluation;
|
||
the completed artifact builds are listed below.
|
||
|
||
GitHub run `38939062012` tested head `c5b7a04fe` as integration merge
|
||
`582e88ccf07d5da06cfb0e9d7e99d9133f2f56af`. Both native Linux library/CLI groups
|
||
and all four ARM64 Darwin outputs passed, including encrypted-store and
|
||
profile-password CLI smoke checks. The production x86_64 Linux compiler also
|
||
passed the ELF alignment regression. The x86_64 musl CLI linked with
|
||
`-fully-static`, installed, and completed fixup. These qualify nine built outputs;
|
||
the static group subsequently failed, and its artifact upload did not run.
|
||
|
||
The ARM64 Android normal and profiling interpreters compiled and linked with the
|
||
API-aware symbol table. The next Android failure is SplitMix 0.1.3.2 calling
|
||
`getentropy` at API26. The local fallback described above passes exact-source
|
||
compilation and shared linking for both ABIs with native Clang21/LLD21 and the
|
||
actual NDK headers/libraries. API35 objects remain byte-identical. The ARM32
|
||
interpreter table also compiles with its real `arc4random_buf` registration and
|
||
remains byte-identical at API35. An earlier source variant passed actual NDK
|
||
Clang18; the final Clang18 fixture and production package-tar evaluation were
|
||
blocked before compilation by the local Linux VM/frontier. The final Android
|
||
interpreter runtime and application builds still need CI.
|
||
|
||
The new dynamically C-linked i686 interpreter reaches its link step but fails
|
||
because `symbols.i386-musl.o` references private musl `__aio_close`. Its real GHC
|
||
arguments retain static Haskell libraries and omit `-fully-static`. This proves
|
||
the link-policy change reached the component; the helper ELF and TH execution
|
||
remain unqualified. Normal and profiling plans retain all 95 record identities:
|
||
only three iserv configure arguments disable executable-static; the other 92
|
||
records are byte-identical. The changed project skeleton rebuilds auxiliary boot
|
||
derivations. An i686/QEMU C control reproduces the old static-loader error and
|
||
loads the same `libz.so` with dynamic C linkage.
|
||
|
||
The newer focused static run `38974180411` at `43262a5c0` built and installed
|
||
both normal and profiling i686 interpreters after the private-symbol fix. Its
|
||
x86_64 static CLI also built. The i686 `wai-app-static` Template Haskell splice
|
||
then failed: the runtime linker rejected duplicate `__x86.get_pc_thunk.bx` and
|
||
`.ax` definitions before unresolved lookups, a QEMU target SIGSEGV, and proxy
|
||
EOF. The scoped COMDAT candidate cross-compiles both changed C files against
|
||
real GHC 9.14 headers with the original warning messages unchanged. Its physical
|
||
source producer changes exactly `Linker.c`, `linker/Elf.c`, and `linker/Elf.h`.
|
||
The paired native x86_64 Linux runtime script passes with the exact existing
|
||
GHC 9.14 compiler on the Hydra host: the original RTS rejects the COMDAT fixture,
|
||
and the patched RTS preserves relocations in both load orders while rejecting
|
||
ordinary strong and stale archive-owner conflicts. Both explicit RTS object
|
||
pairs come from the same configured source; their link maps exclude the original
|
||
archive objects. This isolated direct run completes in 4.3 seconds without a Nix
|
||
build or configuration change. A real GCC15 `-m32` PIC fixture then exposed
|
||
zero-sized thunk signatures, which the initial sized-only candidate rejected.
|
||
The corrected paired test passes in 4.8 seconds, including zero-size and mixed
|
||
load orders, local-label preservation, and rejection of extra functions, data,
|
||
global/weak definitions and partial extents. Extra definitions use distinct names
|
||
in each object so unrelated duplicate symbols cannot satisfy negative controls.
|
||
The Nix check and final i686 QEMU TH/application builds remain CI checks,
|
||
including profiling execution.
|
||
|
||
Intel Darwin reproduced its Setup GC abort and produced the direct-abort trace
|
||
used for the info-table diagnosis above. The local renderer regression passes,
|
||
while the patched compiler bootstrap and full Intel outputs await CI. Windows
|
||
is still building; its Basement follow-up passes the focused reproducer.
|
||
|
||
The native ARM64 Darwin library and CLI built with Nix at `f26dbbc0d` and
|
||
passed `python3 scripts/nix/smoke-cli.py /path/to/simplex-chat`, covering encrypted
|
||
stores and profile passwords. Their actual outputs contain ARM64 macOS objects
|
||
and the expected library registration or CLI executable. Earlier native BBS
|
||
checks also passed; the relevant native source, compiler, flags, and dependencies
|
||
remain unchanged.
|
||
|
||
A native build with the iOS Swift/CommonCrypto flags passed mobile C API checks
|
||
using the rolled archive produced by `link-mobile-library.sh`: Swift JSON,
|
||
encrypted stores, UTF-8, reopen behavior, and wrong-key errors with copying and
|
||
nonmoving GC. This ran on macOS before archive retagging. The CommonCrypto
|
||
SQLCipher component also built with Nix and registered both Apple frameworks.
|
||
The replacement RTS and `text` built with Nix and passed ABI,
|
||
instruction-baseline, UTF-8, and GC checks.
|
||
|
||
Both full Nix ARM64 iOS library ZIPs, Swift JSON and tagged JSON, built in that
|
||
GitHub run. The downloaded artifact matched its published SHA256, outer/inner
|
||
ZIP CRCs, and all ten archive hashes recorded in CI. Each ZIP contained its five
|
||
expected archive entries and 3,632 ARM64 Mach-O members with iOS platform markers.
|
||
The earlier `f26dbbc0d` ZIPs contained 3,630 members and valid Hydra product markers.
|
||
The production FFI archive previously passed an instruction-baseline check with
|
||
unchanged inputs. The package uses the full Cabal v2 unit ID in its archive
|
||
filenames; the existing Xcode project updater accepts those names. This verifies
|
||
the packaged library, not execution after retagging.
|
||
|
||
The Android API26 ARMv7 `ghc-internal` built with all 292 Haskell modules and
|
||
C sources. Its unit ID and ABI match the earlier build, and GNU iconv is retained
|
||
in installed paths and consumer dependencies. Canonical `base` and the complete
|
||
normal interpreter dependency closure also built under Rosetta.
|
||
|
||
On 2026-10-16, the normal ARM32 TH trial passed with the production compiler,
|
||
interpreter, and all 40 target-library outputs. A target-side splice asserted
|
||
`System.Info.arch == "arm"` and a 32-bit `Int`, then emitted `42`. The resulting
|
||
shared ELF32 ARM library exported the test function; a static C caller initialized
|
||
the Haskell RTS and received `42` from the same compiled object. This inspects the
|
||
shared library and executes a static caller, rather than loading the shared
|
||
library on an Android device. The byte-identical upstream annotation test also
|
||
compiled through the target interpreter.
|
||
|
||
The identical production interpreter reached Haskell `main` with both x86_64
|
||
QEMU under Rosetta and native ARM64 Linux QEMU. It has no `PT_TLS` segment and
|
||
needed no TLS adjustment. The separate C caller retains the API26 CRT alignment
|
||
fixture used by the C regression. The final QEMU version guard preserves the
|
||
exact compiler, core, base, interpreter, and wrapper identities from this trial.
|
||
These functional changes are integrated at `748becda3eac5173bdbf34ef4eec476a9215326f`.
|
||
|
||
The trial used a normal-only diagnostic wrapper around the real production
|
||
vanilla interpreter. The full mobile wrapper also references a profiling
|
||
interpreter, so normal application builds can still compile profiling dependencies.
|
||
Both normal and profiling ARMv7 `ghc-internal` archives subsequently built in
|
||
the earlier `b4a1cdd54` compiler context: each contains 283 ELF32 ARM objects,
|
||
with profiling objects in the profiling archive. Their installed registration
|
||
retains GNU iconv paths. This does not qualify the later native RTS compiler
|
||
change or the profiling interpreter's final link and execution.
|
||
Profiling interpreter execution, corrected Android/Windows builds, the complete
|
||
static Linux group, Intel Darwin outputs, Android devices/APKs, and an Xcode
|
||
application build remain unverified. Derivation evaluation and the normal trial
|
||
do not establish that those builds and runtime checks pass.
|
||
|
||
## Updating package hashes
|
||
|
||
1. Install `nix`, `gawk` and `jq`.
|
||
|
||
2. Start nix-shell from repo root:
|
||
|
||
```sh
|
||
nix-shell -p nix-prefetch-git
|
||
```
|
||
|
||
3. Run in nix shell:
|
||
|
||
```sh
|
||
gawk -f ./scripts/nix/update-sha256.awk cabal.project > ./scripts/nix/sha256map.nix
|
||
```
|