Fix Android pthread metadata before mobile shared linking

Patch crypton and direct-sqlcipher before planning: Bionic supplies pthread in libc and the NDK has no separate libpthread. Preserve compiler threading flags and the shared-link adapter.

Check Android-only source selection and allow the existing Android CI group to be dispatched independently for final-link validation.
This commit is contained in:
Moritz Angermann committed 2026-10-17 08:20:19 +09:00
1 parent 43262a5c05
commit 4051b8f71f
9 files changed
+101 -7

No files matched your search

+4 -2
View File
@@ -8,7 +8,7 @@ on:
type: choice
required: true
default: all
options: [all, linux-static]
options: [all, linux-static, android]
push:
branches: [master, stable, build/stable-ghc-9.14]
paths:
@@ -96,7 +96,9 @@ jobs:
- name: Check Android iserv overlay scope
if: ${{ matrix.target == 'android' }}
timeout-minutes: 1
run: nix eval --json --file scripts/nix/check-android-iserv-scope.nix
run: |
nix eval --json --file scripts/nix/check-android-iserv-scope.nix
nix eval --json --file scripts/nix/check-android-pthread-scope.nix
- name: Check musl interpreter link policy scope
if: ${{ matrix.target == 'linux-static' }}
+7 -1
View File
@@ -60,12 +60,17 @@
let
# v2 restores Hackage description overrides after package patches.
# Plan Windows x509 from its patched local source to retain crypt32.
androidPthreads = import ./scripts/nix/android-pthread-libraries.nix {
pkgs = config.evalPackages;
hostPlatform = pkgs'.stdenv.hostPlatform;
};
patchedPackageSources = pkgs.lib.optional (
config.compiler-nix-name == "ghc914-sh"
&& pkgs'.stdenv.buildPlatform.config != pkgs'.stdenv.hostPlatform.config
) (import ./scripts/nix/composition-source.nix { pkgs = config.evalPackages; })
++ pkgs.lib.optional pkgs'.stdenv.hostPlatform.isWindows
(import ./scripts/nix/windows-x509-source.nix { pkgs = config.evalPackages; inherit haskellNix; });
(import ./scripts/nix/windows-x509-source.nix { pkgs = config.evalPackages; inherit haskellNix; })
++ androidPthreads.packageSources;
in {
compiler-nix-name = "ghc914-sh";
inherit compilerSelection;
@@ -127,6 +132,7 @@
else [ ./scripts/nix/direct-sqlcipher-2.3.27.patch ]
++ pkgs.lib.optional pkgs'.stdenv.hostPlatform.isAndroid
./scripts/nix/direct-sqlcipher-android-log.patch
++ androidPthreads.directSqlcipherPatches
++ pkgs.lib.optional pkgs'.stdenv.hostPlatform.isMusl
./scripts/nix/direct-sqlcipher-static-libraries.patch;
} // pkgs.lib.optionalAttrs pkgs'.stdenv.hostPlatform.isDarwin {
+8 -2
View File
@@ -135,6 +135,11 @@ 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. 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
@@ -192,8 +197,9 @@ outputs once: x86_64 Linux runs `native`, `android`, `windows`, and `linux-stati
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 only the existing three `linux-static` outputs with
`gh workflow run nix.yml --ref BRANCH -f group=linux-static`. The registered
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`. The registered
workflow can run on a branch containing this workflow file; it does not require
that file on the default branch. New runs for the same workflow/ref queue
without canceling its active run. Legacy PR build jobs also preserve running
+16
View File
@@ -0,0 +1,16 @@
# Bionic supplies pthread symbols in libc, with no separate libpthread.
# Remove the two Linux library declarations before planning so Cabal's v2
# source and package registrations agree; keep compiler -pthread flags intact.
{ pkgs, hostPlatform }:
{
packageSources = pkgs.lib.optional hostPlatform.isAndroid (pkgs.applyPatches {
name = "crypton-0.34-android-source";
src = pkgs.fetchurl {
url = "https://hackage.haskell.org/package/crypton-0.34/crypton-0.34.tar.gz";
hash = "sha256-RESEaSTKVWFfzhBJE6WmhnWhgM/q3DUKsrEk+6G8HtY=";
};
patches = [ ./crypton-android-pthread.patch ];
});
directSqlcipherPatches = pkgs.lib.optional hostPlatform.isAndroid
./direct-sqlcipher-android-pthread.patch;
}
@@ -0,0 +1,31 @@
# nix eval --json --file scripts/nix/check-android-pthread-scope.nix
let
select = import ./android-pthread-libraries.nix;
pkgs = {
lib.optional = condition: value: if condition then [ value ] else [];
fetchurl = args: args;
applyPatches = args: args;
};
android = select { inherit pkgs; hostPlatform.isAndroid = true; };
# The false branch must not even evaluate Android sources on other targets.
other = select {
pkgs = pkgs // {
fetchurl = _: throw "Unexpected Android source fetch";
applyPatches = _: throw "Unexpected Android source patch";
};
hostPlatform.isAndroid = false;
};
source = builtins.head android.packageSources;
in
assert builtins.length android.packageSources == 1;
assert source.name == "crypton-0.34-android-source";
assert source.src.url == "https://hackage.haskell.org/package/crypton-0.34/crypton-0.34.tar.gz";
assert source.src.hash == "sha256-RESEaSTKVWFfzhBJE6WmhnWhgM/q3DUKsrEk+6G8HtY=";
assert source.patches == [ ./crypton-android-pthread.patch ];
assert android.directSqlcipherPatches == [ ./direct-sqlcipher-android-pthread.patch ];
assert other == { packageSources = []; directSqlcipherPatches = []; };
{
androidUsesBothPreplanningPatches = true;
cryptonVersionAndSourcePinned = true;
otherPlatformsUnchangedAndLazy = true;
}
+2 -2
View File
@@ -15,7 +15,7 @@ ROWS = (
def select(group):
if group not in ("all", "linux-static"):
if group not in ("all", "linux-static", "android"):
raise ValueError(f"unknown Nix CI group: {group}")
rows = ROWS if group == "all" else tuple(row for row in ROWS if row[2] == group)
return {"include": [dict(system=system, runner=runner, target=target)
@@ -24,5 +24,5 @@ def select(group):
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--group", choices=["all", "linux-static"], default="all")
parser.add_argument("--group", choices=["all", "linux-static", "android"], default="all")
print(json.dumps(select(parser.parse_args().group), separators=(",", ":")))
+13
View File
@@ -0,0 +1,13 @@
--- a/crypton.cabal
+++ b/crypton.cabal
@@ -310,10 +310,7 @@
else
build-depends: base
- if os(linux)
- extra-libraries: pthread
-
if flag(old_toolchain_inliner)
cc-options: -fgnu89-inline
if (arch(x86_64) || arch(aarch64))
@@ -0,0 +1,12 @@
--- a/direct-sqlcipher.cabal
+++ b/direct-sqlcipher.cabal
@@ -91,9 +91,6 @@
extra-libraries: dl
- if !os(windows) && !os(android)
- extra-libraries: pthread
-
if flag(fulltextsearch)
cc-options: -DSQLITE_ENABLE_FTS3 -DSQLITE_ENABLE_FTS3_PARENTHESIS
-DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5
+8
View File
@@ -36,6 +36,14 @@ class Matrix(unittest.TestCase):
"hydraJobs.x86_64-linux.aarch64-linux-static:exe:simplex-chat",
])
def test_android_dispatch_is_exactly_the_existing_four_outputs(self):
self.assertEqual(outputs("android"), [
"hydraJobs.x86_64-linux.aarch64-android:lib:simplex-chat",
"hydraJobs.x86_64-linux.aarch64-android:lib:support",
"hydraJobs.x86_64-linux.armv7a-android:lib:simplex-chat",
"hydraJobs.x86_64-linux.armv7a-android:lib:support",
])
def test_legacy_pr_jobs_do_not_cancel_active_runs(self):
source = (HERE.parent.parent / ".github/workflows/build.yml").read_text()
expressions = re.findall(r"^ cancel-in-progress: \$\{\{ (.*) \}\}$", source, re.MULTILINE)