diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 04b588f05a..53a0d13b6e 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -96,7 +96,9 @@ jobs: - 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 + run: | + nix eval --impure --json --file scripts/nix/check-musl-iserv-scope.nix \ + --apply 'f: f { lib = (builtins.getFlake (toString ./.)).inputs.nixpkgs.lib; }' - name: Check Intel Setup failure selection if: ${{ matrix.system == 'x86_64-darwin' && matrix.target == 'all' }} diff --git a/scripts/nix/README.md b/scripts/nix/README.md index 40b96fb66f..e336af8eb6 100644 --- a/scripts/nix/README.md +++ b/scripts/nix/README.md @@ -56,8 +56,9 @@ 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 pure scope check runs in the Linux static CI group: -`nix eval --json --file scripts/nix/check-musl-iserv-scope.nix`. +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; }'`. Windows patches Basement's `INVALID_HANDLE_VALUE` expression to `INT_PTR`, so hsc2hs receives the signed pointer-sized integer expected by the Haskell API. diff --git a/scripts/nix/check-musl-iserv-scope.nix b/scripts/nix/check-musl-iserv-scope.nix index 9776e96a4d..35973e121e 100644 --- a/scripts/nix/check-musl-iserv-scope.nix +++ b/scripts/nix/check-musl-iserv-scope.nix @@ -1,13 +1,9 @@ -# Pure policy regression: nix eval --json --file scripts/nix/check-musl-iserv-scope.nix +# Use the pinned nixpkgs lib; no compiler, VM, or project IFD is evaluated. +# nix eval --impure --json --file scripts/nix/check-musl-iserv-scope.nix \ +# --apply 'f: f { lib = (builtins.getFlake (toString ./.)).inputs.nixpkgs.lib; }' +{ lib, overlay ? import ./musl-iserv-overlay.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; - mkForce = value: value; - }; - apply = { musl ? true, linux ? true, compiler ? "ghc914-sh", name ? "iserv-proxy", builderVersion ? 2, i386 ? true }: + apply = { musl ? true, linux ? true, compiler ? "ghc914-sh", name ? "iserv-proxy", builderVersion ? 2, i386 ? true, profiling ? false }: let prev = { inherit lib; @@ -19,27 +15,45 @@ let }; }; 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 {}; + modules = if result ? haskell-nix then result.haskell-nix.cabalProject' [] else []; + evaluated = lib.evalModules { + modules = [ { + options = { + name = lib.mkOption { type = lib.types.str; }; + compiler-nix-name = lib.mkOption { type = lib.types.str; }; + builderVersion = lib.mkOption { type = lib.types.int; }; + profiling = lib.mkOption { type = lib.types.bool; }; + cabalProjectLocal = lib.mkOption { type = lib.types.lines; default = ""; }; + src = lib.mkOption { type = lib.types.anything; default = null; }; + }; + config = { + inherit name builderVersion profiling; + compiler-nix-name = compiler; + # Real cabalProject' supplies this through the module fixed point, + # not specialArgs. Eager optionalAttrs on pkgs recurses here. + _module.args.pkgs = prev; + }; + } ] ++ modules; + }; + in { + inherit (evaluated.config) cabalProjectLocal src; + }; selected = apply {}; + disabled = { cabalProjectLocal = ""; src = null; }; in -assert builtins.attrNames selected == [ "cabalProjectLocal" "src" ]; assert selected.src.src == "original-iserv-proxy-source"; assert selected.src.patches == [ ./musl-iserv-public-libc.patch ]; assert builtins.match ".*executable-static: False.*" (builtins.replaceStrings [ "\n" ] [ " " ] selected.cabalProjectLocal) != null; -assert builtins.attrNames (apply { i386 = false; }) == [ "cabalProjectLocal" ]; +assert apply { profiling = true; } == selected; +assert (apply { i386 = false; }).src == null; assert (apply { i386 = false; }).cabalProjectLocal == selected.cabalProjectLocal; -assert apply { name = "simplex-chat"; } == {}; -assert apply { musl = false; } == {}; -assert apply { linux = false; } == {}; -assert apply { compiler = "ghc963"; } == {}; -assert apply { builderVersion = 1; } == {}; +assert apply { name = "simplex-chat"; } == disabled; +assert apply { musl = false; } == disabled; +assert apply { linux = false; } == disabled; +assert apply { compiler = "ghc963"; } == disabled; +assert apply { builderVersion = 1; } == disabled; { + moduleArgumentFixedPointEvaluated = true; muslInterpreterCLoadingEnabled = true; i386SharedLibcSourcePatchedBeforePlanning = true; otherMuslSymbolTablesUnchanged = true; diff --git a/scripts/nix/musl-iserv-overlay.nix b/scripts/nix/musl-iserv-overlay.nix index bc44c671b1..8ab81129f4 100644 --- a/scripts/nix/musl-iserv-overlay.nix +++ b/scripts/nix/musl-iserv-overlay.nix @@ -11,7 +11,7 @@ prev.lib.optionalAttrs ( && config.compiler-nix-name == "ghc914-sh" && config.name == "iserv-proxy" && config.builderVersion == 2 - ) ({ + ) (lib.mkMerge [ { # 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 @@ -20,7 +20,9 @@ prev.lib.optionalAttrs ( package iserv-proxy executable-static: False ''; - } // lib.optionalAttrs pkgs.stdenv.hostPlatform.isx86_32 { + } (lib.mkIf pkgs.stdenv.hostPlatform.isx86_32 { + # Keep module shape independent of pkgs: cabalProject' supplies it + # through _module.args, so eager optionalAttrs recurses in config. # This helper links shared libc. Keep public close registered, but # do not take the address of musl's hidden AIO implementation hook. src = lib.mkForce (pkgs.pkgsBuildBuild.applyPatches { @@ -28,7 +30,7 @@ prev.lib.optionalAttrs ( src = pkgs.haskell-nix.sources.iserv-proxy; patches = [ ./musl-iserv-public-libc.patch ]; }); - })) ] + }) ])) ] ++ (if builtins.isList projectModule then projectModule else [ projectModule ]) ); };