Skip to content

Commit 5ab033e

Browse files
committed
fixup! Add prebuilt-depends for proprietary dependencies.
Add commentary
1 parent 458a058 commit 5ab033e

File tree

4 files changed

+73
-3
lines changed

4 files changed

+73
-3
lines changed

builder/comp-builder.nix

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,63 @@
9191
, useLLVM ? ghc.useLLVM or false
9292
, smallAddressSpace ? false
9393

94+
# Note [prebuilt dependencies]
95+
#
96+
# Typical cabal project planning starts with the libraries that come with
97+
# the compiler and then plans to build every other needed dependency from
98+
# source (fetched through hackage repositories or source-repository
99+
# dependencies). In cases where some library that isn't part of the compiler
100+
# is only available as a pre-built shared object file (such as for some
101+
# closed-source module from a vendor, or in principle a component whose
102+
# compilation is extremely expensive), we need to be able to tell cabal
103+
# about additional prebuilt dependencies to include in its plan and link to
104+
# as needed at build time.
105+
#
106+
# This can be done by passing the needed libraries in prebuilt-depends. During
107+
# cabal planning and builds, these libraries (and their dependencies) will be
108+
# present in the ghc-pkg database that cabal will draw from for its dependency
109+
# resolution, thereby skipping lookup from hackage or building from source.
110+
#
111+
# The entries in the prebuilt dependencies list may have dependencies that are
112+
# part of the compiler-provided package set, or may have overlap with each other.
113+
# GHC can actually handle this use case fine, since types from different packages
114+
# (even of the same name) will not unify, so at worst you will get a compile error,
115+
# but cabal will need to choose one for the packages you are building. The entries
116+
# in the list are given priority over the compiler-provided ones, with the later
117+
# entries having greater priority than the earlier ones.
118+
#
119+
# For your build to succeed, your prebuilt-dependencies must meet the following:
120+
#
121+
# 1. They are built with the same compiler version and RTS way.
122+
# 2. They have all of their dependencies within the package db directory or
123+
# included as propagatedBuildInputs
124+
# 3. They must include the `envDep` and `exactDep` files that make-config.files.nix
125+
# expects for configuring cabal precisely.
126+
#
127+
# The recommended way to meet this requirement is to build the relevant libraries
128+
# with haskell.nix too, since it sets up the dependencies appropriately. An example
129+
# workflow would be:
130+
#
131+
# 1. Build libraries foo and bar with haskell.nix (the same plan)
132+
# 2. Note down the store paths for foo and bar library outputs
133+
# 3. Make a full nix export of those store paths (using e.g. `nix-store --export $(nix-store --query --requisites $barPath $fooPath) > foobar.closure`
134+
# 4. On the consumer machine, import the store paths (e.g. `nix-store --import foobar.closure` and then add gc roots)
135+
# 5. In the consumer haskell.nix build, add the imported store paths to your prebuilt-depends. E.g.:
136+
#
137+
# prebuilt-depends = let foo = {
138+
# # We need to make this look like a call to derivation for stdenv to do the right thing
139+
# name = "foo-lib-foo-0.1.0.0";
140+
# type = "derivation";
141+
# outputs = [ "out" ];
142+
# out = foo;
143+
# all = [ foo ];
144+
# # $fooPath is already in the store due to the import, so we use the storePath
145+
# # builtin to add it as a source dependency to the build. Note that this does
146+
# # not work in pure evaluation mode, you must use --impure with flakes. An
147+
# # alternative would be to bundle up all of the needed libraries into tarballs that
148+
# # are fetched and unpacked as proper fixed-output derivations.
149+
# outPath = builtins.storePath $fooPath;
150+
# }; in [ foo ]; # Could also do the same for bar of course
94151
, prebuilt-depends ? []
95152
}:
96153
# makeOverridable is called here after all the `? DEFAULT` arguments

builder/make-config-files.nix

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,12 @@ let
161161
cat $ghcDeps/exactDeps/$p/cabal.config >> $configFiles/cabal.config
162162
fi
163163
done
164-
'' + ''
164+
''
165+
# We put the packages in buildInputs *after* the GHC deps on the command line
166+
# to ensure that any prebuilt dependencies (see Note [prebuilt dependencies])
167+
# take priority over the GHC-provided ones. We can't relink the prebuilt
168+
# libraries, so this is the most likely to avoid conflicts.
169+
+ ''
165170
for p in "''${pkgsHostTarget[@]}"; do
166171
if [ -e $p/envDep ]; then
167172
cat $p/envDep >> $configFiles/ghc-environment

modules/cabal-project.nix

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,11 @@ in {
147147
prebuilt-depends = mkOption {
148148
type = listOf package;
149149
default = [];
150-
description = "pre-built (perhaps proprietary) Haskell packages to make available as dependencies";
150+
description = ''
151+
pre-built (perhaps proprietary) Haskell packages to make available as dependencies
152+
153+
See Note [prebuilt dependencies] for more details
154+
'';
151155
};
152156
};
153157
}

modules/component-driver.nix

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@ in
4040
options.prebuilt-depends = lib.mkOption {
4141
type = lib.types.listOf lib.types.package;
4242
default = [];
43-
description = "pre-built (perhaps proprietary) Haskell packages to make available as dependencies";
43+
description = ''
44+
pre-built (perhaps proprietary) Haskell packages to make available as dependencies
45+
46+
See Note [prebuilt dependencies] for more details
47+
'';
4448
};
4549

4650
# Dependencies (with reinstallable-lib:ghc)

0 commit comments

Comments
 (0)