Allow musl cross interpreters to load C dependencies

Keep static Haskell libraries and fully static application outputs while planning ghc914-sh iserv projects with a dynamic musl C loader. Scope the policy to Linux-built musl Cabal v2 interpreters and check that scope in static Linux CI.

Normal and profiled target plans preserve all unit IDs, source records and Haskell ways; only the iserv executable-static flag changes. The shared V2 project skeleton changes auxiliary derivation hashes. A small i686 musl probe reproduces static dlopen failure and verifies the dynamic loader under QEMU; full interpreter TH validation remains for canonical CI.
This commit is contained in:
Moritz Angermann committed 2026-10-17 02:17:58 +09:00
1 parent 465b6dbfac
commit 89ffaea1be
5 files changed
+81 -1

No files matched your search

+5
View File
@@ -93,6 +93,11 @@ jobs:
timeout-minutes: 1
run: nix eval --json --file scripts/nix/check-android-iserv-scope.nix
- name: Check musl interpreter link policy scope
if: ${{ matrix.target == 'linux-static' }}
timeout-minutes: 1
run: nix eval --json --file scripts/nix/check-musl-iserv-scope.nix
- name: Check native Linux GHC ELF section alignment
if: ${{ matrix.system == 'x86_64-linux' && matrix.target == 'native' }}
timeout-minutes: 120
+2 -1
View File
@@ -39,7 +39,8 @@
let nativePackages = import ./scripts/nix/native-packages.nix { inherit haskellNix; }; in
let compilerCache = import ./scripts/nix/compiler-cache-overlay.nix { inherit haskellNix; }; in
let androidEmulator = import ./scripts/nix/android-emulator-overlay.nix { inherit haskellNix; }; in
let pkgs = (nativePackages system).appendOverlays [android26 compilerCache androidEmulator]; in
let muslIserv = import ./scripts/nix/musl-iserv-overlay.nix; in
let pkgs = (nativePackages system).appendOverlays [android26 compilerCache androidEmulator muslIserv]; in
let compilerSelection = import ./scripts/nix/compiler-selection.nix { inherit haskellNix; }; in
let mobileRuntime = import ./scripts/nix/mobile-runtime.nix { inherit pkgs compilerSelection; }; in
let drv' = { extra-modules, pkgs', package-flags ? {}, package-ghc-options ? {}, mobile ? false, ... }:
+9
View File
@@ -33,6 +33,15 @@ 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.
The pure scope check runs in the Linux static CI group:
`nix eval --json --file scripts/nix/check-musl-iserv-scope.nix`.
Windows patches Basement's `INVALID_HANDLE_VALUE` expression to `INT_PTR`, so
hsc2hs receives the signed pointer-sized integer expected by the Haskell API.
+38
View File
@@ -0,0 +1,38 @@
# Pure policy regression: nix eval --json --file scripts/nix/check-musl-iserv-scope.nix
let
overlay = import ./musl-iserv-overlay.nix;
lib = {
optionalAttrs = condition: attrs: if condition then attrs else {};
mkIf = condition: attrs: if condition then attrs else {};
mkAfter = value: value;
};
apply = { musl ? true, linux ? true, compiler ? "ghc914-sh", name ? "iserv-proxy", builderVersion ? 2 }:
let
prev = {
inherit lib;
stdenv = { hostPlatform.isMusl = musl; buildPlatform.isLinux = linux; };
haskell-nix.cabalProject' = modules: modules;
};
result = overlay prev prev;
in if result ? haskell-nix then
(builtins.head (result.haskell-nix.cabalProject' [])) {
inherit lib;
pkgs = prev;
config = { inherit name builderVersion; compiler-nix-name = compiler; };
}
else {};
selected = apply {};
in
assert builtins.attrNames selected == [ "cabalProjectLocal" ];
assert builtins.match ".*executable-static: False.*" (builtins.replaceStrings [ "\n" ] [ " " ] selected.cabalProjectLocal) != null;
assert apply { name = "simplex-chat"; } == {};
assert apply { musl = false; } == {};
assert apply { linux = false; } == {};
assert apply { compiler = "ghc963"; } == {};
assert apply { builderVersion = 1; } == {};
{
muslInterpreterCLoadingEnabled = true;
applicationPolicyUnchanged = true;
nonMuslAndDarwinBuildersUnchanged = true;
otherCompilersAndLegacyBuilderUnchanged = true;
}
+27
View File
@@ -0,0 +1,27 @@
final: prev:
prev.lib.optionalAttrs (
prev.stdenv.hostPlatform.isMusl && prev.stdenv.buildPlatform.isLinux
) {
haskell-nix = prev.haskell-nix // {
cabalProject' = projectModule: prev.haskell-nix.cabalProject' (
[ ({ config, lib, pkgs, ... }:
lib.mkIf (
pkgs.stdenv.hostPlatform.isMusl
&& pkgs.stdenv.buildPlatform.isLinux
&& config.compiler-nix-name == "ghc914-sh"
&& config.name == "iserv-proxy"
&& config.builderVersion == 2
) {
# The build-time interpreter loads foreign libraries for TH.
# Fully static musl uses a dlopen stub; retain static Haskell
# libraries while allowing its C loader. Final CLI projects keep
# haskell.nix's default executable-static setting.
cabalProjectLocal = lib.mkAfter ''
package iserv-proxy
executable-static: False
'';
}) ]
++ (if builtins.isList projectModule then projectModule else [ projectModule ])
);
};
}