Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,33 @@
This file contains a summary of changes to Haskell.nix and `nix-tools`
that will impact users.

## May 23, 2026

The post-plan `packages.ghc.src` override that
`modules/configuration-nix.nix` used to apply unconditionally is
now opt-in via the new project-level `useLocalGhcLib` option.

If your project depends on / constrains the `ghc` package (e.g.
uses `ghc-lib-reinstallable` or pins `lib:ghc`), add
`useLocalGhcLib = true` to your project arguments:

```nix
haskell-nix.cabalProject {
# ...
useLocalGhcLib = true;
}
```

For cabal projects this injects a `source-repository-package`
block into `cabalProjectLocal` that points at the configured GHC
tree. For stack projects it re-applies the previous
`packages.ghc.src` post-plan override.

Symptoms when the flag is needed but not set: the planner fails
because it can't satisfy a `ghc ==<version>` constraint against
the boot package set, or `lib:ghc` is rejected with
`allow-boot-library-installs` errors.

## Mar 24, 2026

GHC options set in `cabal.project` files (via `package` or
Expand Down
58 changes: 58 additions & 0 deletions modules/cabal-project.nix
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,62 @@ in {
'';
};
};
config = lib.mkIf config.useLocalGhcLib (
let
ghc = (config.compilerSelection pkgs.buildPackages).${config.compiler-nix-name};
ghcSrc = (pkgs.buildPackages.symlinkJoin {
name = ghc.name + "-full-src";
paths = [ ghc.configured-src ghc.generated ];
}) + "/compiler";
ghcMinRepoUrl = "file://${ghcSrc}";
in {
# When `useLocalGhcLib = true`, expose the GHC compiler tree
# (configured + generated) as a `source-repository-package` in
# the project's cabal.project (via cabalProjectLocal), pointing
# at the `compiler/` subdir of `(configured-src + generated)`.
#
# `inputMap` short-circuits haskell.nix's source-repo fetch so
# we don't go through `builtins.fetchGit` (which fails in pure
# eval mode without a sha256). haskell.nix then re-wraps
# `inputMap.<url>` in its own minimal git repo at
# `lib/call-cabal-project-to-nix.nix` -- the wrapper produces
# deterministic content (same rsync + git init + commit), so
# cabal's `pkg-src-sha256` is stable across evaluations.
#
# Why source-repository-package and not `packages:`:
# * `packages:` makes cabal treat the package as *inplace* --
# v2-build registers `<pkg>-<ver>-inplace` in dist-newstyle
# but doesn't copy anything to the cabal-store layout.
# * source-repository-package takes the regular reinstallable
# path: cabal hashes the wrapped repo's content into
# `pkg-src-sha256`, builds the package, and installs it
# like any other reinstallable dep.
#
# `allow-boot-library-installs: True` is needed at project
# level so plan-to-nix's cabal-install doesn't reject ghc's
# source instance.
cabalProjectLocal = lib.mkBefore ''
-- Added by `useLocalGhcLib = true`: expose the GHC compiler
-- tree (configured + generated) as a source-repository-package
-- so cabal treats `lib:ghc` like a regular reinstallable
-- package (installed to the cabal-store, not inplace).
source-repository-package
type: git
location: ${ghcMinRepoUrl}
subdir: .
tag: minimal
allow-boot-library-installs: True
'';
# Key by `<url>/<ref>` (the first lookup form in
# `lib/call-cabal-project-to-nix.nix:fetchPackageRepo`) so we
# short-circuit the `.rev` check that the bare-`<url>` form
# applies — the local source isn't a git derivation and has
# no `.rev` attribute. Strip string context from the key
# because nix forbids attribute names that carry references
# to store paths.
inputMap = {
${builtins.unsafeDiscardStringContext "${ghcMinRepoUrl}/minimal"} = ghcSrc;
};
}
);
}
5 changes: 0 additions & 5 deletions modules/configuration-nix.nix
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,6 @@ in addPackageKeys {
(config.hsPkgs.buildPackages.alex.components.exes.alex or (pkgs.haskell-nix.tool config.compiler.nix-name "alex" {}))
(config.hsPkgs.buildPackages.happy.components.exes.happy or (pkgs.haskell-nix.tool config.compiler.nix-name "happy" {}))
]);
packages.ghc.src = lib.mkForce ((pkgs.symlinkJoin {
name = config.ghc.package.name + "-full-src";
paths = [ config.ghc.package.configured-src config.ghc.package.generated ]; }) + "/compiler");
packages.ghc.package-description-override = pkgs.lib.mkForce null;

# Remove dependency on hsc2hs (hsc2hs should be in ghc derivation)
packages.mintty.components.library.build-tools = lib.mkForce [];

Expand Down
20 changes: 20 additions & 0 deletions modules/project-common.nix
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,25 @@ with lib.types;
If set, prevents nix-tools from attempting to load package.yaml even if it is present.
'';
};
useLocalGhcLib = mkOption {
type = bool;
default = false;
description = ''
Expose the GHC compiler tree (configured-src + generated, the
`compiler/` subdir thereof) to the planner as a regular
reinstallable package source. Use this when the project
depends on / constrains the `ghc` package — e.g.
`ghc-lib-reinstallable`.

Cabal projects (see `modules/cabal-project.nix`) inject a
`source-repository-package` block into `cabalProjectLocal`
so cabal hashes the wrapped repo's content into
`pkg-src-sha256`. Stack projects (see
`modules/stack-project.nix`) re-add the post-plan
`packages.ghc.src` override that
`modules/configuration-nix.nix` used to apply
unconditionally.
'';
};
};
}
12 changes: 12 additions & 0 deletions modules/stack-project.nix
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,17 @@ with types;
# For stack projects we normally do not want to include the tool dependencies
# of all the hsPkgs (all of stackage).
shell.allToolDeps = mkDefault false;
# When `useLocalGhcLib = true` (project-common option), re-apply
# the `packages.ghc.src` post-plan override that
# `modules/configuration-nix.nix` used to do unconditionally.
modules = mkIf config.useLocalGhcLib [
({ config, lib, pkgs, ... }: {
packages.ghc.src = lib.mkForce ((pkgs.symlinkJoin {
name = config.ghc.package.name + "-full-src";
paths = [ config.ghc.package.configured-src config.ghc.package.generated ];
}) + "/compiler");
packages.ghc.package-description-override = lib.mkForce null;
})
];
};
}
5 changes: 5 additions & 0 deletions test/ghc-lib-reinstallable/cabal.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ let
project = haskell-nix.cabalProject {
inherit compiler-nix-name evalPackages;
src = testSrc "ghc-lib-reinstallable";
# Expose the GHC compiler tree as a `source-repository-package`
# so cabal can install `lib:ghc` like any other reinstallable
# dep. Required since the unconditional `packages.ghc.src`
# override was removed from `modules/configuration-nix.nix`.
useLocalGhcLib = true;
cabalProjectLocal = ''
constraints: ghc ==${ghcVersion}
'';
Expand Down
4 changes: 4 additions & 0 deletions test/ghc-lib-reinstallable/stack.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ let
project = haskell-nix.stackProject {
inherit stackYaml;
src = testSrc "ghc-lib-reinstallable";
# Re-apply the `packages.ghc.src` post-plan override that
# `modules/configuration-nix.nix` used to do unconditionally.
# Required since the override was made opt-in.
useLocalGhcLib = true;
modules = [{
# Configure packages for os-string
packages.directory.components.library.configureFlags = ["-f os-string"];
Expand Down
Loading