depot/third_party/nixpkgs/pkgs/development/haskell-modules/lib/compose.nix

733 lines
23 KiB
Nix

# TODO(@Ericson2314): Remove `pkgs` param, which is only used for
# `buildStackProject`, `justStaticExecutables` and `checkUnusedPackages`
{ pkgs, lib }:
rec {
/*
This function takes a file like `hackage-packages.nix` and constructs
a full package set out of that.
*/
makePackageSet = import ../make-package-set.nix;
/*
The function overrideCabal lets you alter the arguments to the
mkDerivation function.
Example:
First, note how the aeson package is constructed in hackage-packages.nix:
"aeson" = callPackage ({ mkDerivation, attoparsec, <snip>
}:
mkDerivation {
pname = "aeson";
<snip>
homepage = "https://github.com/bos/aeson";
})
The mkDerivation function of haskellPackages will take care of putting
the homepage in the right place, in meta.
> haskellPackages.aeson.meta.homepage
"https://github.com/bos/aeson"
> x = haskell.lib.compose.overrideCabal (old: { homepage = old.homepage + "#readme"; }) haskellPackages.aeson
> x.meta.homepage
"https://github.com/bos/aeson#readme"
*/
overrideCabal =
f: drv:
(drv.override (
args:
args
// {
mkDerivation = drv: (args.mkDerivation drv).override f;
}
))
// {
overrideScope = scope: overrideCabal f (drv.overrideScope scope);
};
# : Map Name (Either Path VersionNumber) -> HaskellPackageOverrideSet
# Given a set whose values are either paths or version strings, produces
# a package override set (i.e. (self: super: { etc. })) that sets
# the packages named in the input set to the corresponding versions
packageSourceOverrides =
overrides: self: super:
pkgs.lib.mapAttrs (
name: src:
let
isPath = x: builtins.substring 0 1 (toString x) == "/";
generateExprs = if isPath src then self.callCabal2nix else self.callHackage;
in
generateExprs name src { }
) overrides;
/*
doCoverage modifies a haskell package to enable the generation
and installation of a coverage report.
See https://wiki.haskell.org/Haskell_program_coverage
*/
doCoverage = overrideCabal (drv: {
doCoverage = true;
});
/*
dontCoverage modifies a haskell package to disable the generation
and installation of a coverage report.
*/
dontCoverage = overrideCabal (drv: {
doCoverage = false;
});
/*
doHaddock modifies a haskell package to enable the generation and
installation of API documentation from code comments using the
haddock tool.
*/
doHaddock = overrideCabal (drv: {
doHaddock = true;
});
/*
dontHaddock modifies a haskell package to disable the generation and
installation of API documentation from code comments using the
haddock tool.
*/
dontHaddock = overrideCabal (drv: {
doHaddock = false;
});
/*
doJailbreak enables the removal of version bounds from the cabal
file. You may want to avoid this function.
This is useful when a package reports that it can not be built
due to version mismatches. In some cases, removing the version
bounds entirely is an easy way to make a package build, but at
the risk of breaking software in non-obvious ways now or in the
future.
Instead of jailbreaking, you can patch the cabal file.
Note that jailbreaking at this time, doesn't lift bounds on
conditional branches.
https://github.com/peti/jailbreak-cabal/issues/7 has further details.
*/
doJailbreak = overrideCabal (drv: {
jailbreak = true;
});
/*
dontJailbreak restores the use of the version bounds the check
the use of dependencies in the package description.
*/
dontJailbreak = overrideCabal (drv: {
jailbreak = false;
});
/*
doCheck enables dependency checking, compilation and execution
of test suites listed in the package description file.
*/
doCheck = overrideCabal (drv: {
doCheck = true;
});
/*
dontCheck disables dependency checking, compilation and execution
of test suites listed in the package description file.
*/
dontCheck = overrideCabal (drv: {
doCheck = false;
});
/*
The dontCheckIf variant sets doCheck = false if the condition
applies. In any other case the previously set/default value is used.
This prevents accidentally re-enabling tests in a later override.
*/
dontCheckIf = condition: if condition then dontCheck else lib.id;
/*
doBenchmark enables dependency checking and compilation
for benchmarks listed in the package description file.
Benchmarks are, however, not executed at the moment.
*/
doBenchmark = overrideCabal (drv: {
doBenchmark = true;
});
/*
dontBenchmark disables dependency checking, compilation and execution
for benchmarks listed in the package description file.
*/
dontBenchmark = overrideCabal (drv: {
doBenchmark = false;
});
/*
doDistribute enables the distribution of binaries for the package
via hydra.
*/
doDistribute = overrideCabal (drv: {
# lib.platforms.all is the default value for platforms (since GHC can cross-compile)
hydraPlatforms = lib.subtractLists (drv.badPlatforms or [ ]) (drv.platforms or lib.platforms.all);
});
/*
dontDistribute disables the distribution of binaries for the package
via hydra.
*/
dontDistribute = overrideCabal (drv: {
hydraPlatforms = [ ];
});
/*
appendConfigureFlag adds a single argument that will be passed to the
cabal configure command, after the arguments that have been defined
in the initial declaration or previous overrides.
Example:
> haskell.lib.compose.appendConfigureFlag "--profiling-detail=all-functions" haskellPackages.servant
*/
appendConfigureFlag = x: appendConfigureFlags [ x ];
appendConfigureFlags =
xs:
overrideCabal (drv: {
configureFlags = (drv.configureFlags or [ ]) ++ xs;
});
appendBuildFlag =
x:
overrideCabal (drv: {
buildFlags = (drv.buildFlags or [ ]) ++ [ x ];
});
appendBuildFlags =
xs:
overrideCabal (drv: {
buildFlags = (drv.buildFlags or [ ]) ++ xs;
});
/*
removeConfigureFlag drv x is a Haskell package like drv, but with
all cabal configure arguments that are equal to x removed.
> haskell.lib.compose.removeConfigureFlag "--verbose" haskellPackages.servant
*/
removeConfigureFlag =
x:
overrideCabal (drv: {
configureFlags = lib.remove x (drv.configureFlags or [ ]);
});
addBuildTool = x: addBuildTools [ x ];
addBuildTools =
xs:
overrideCabal (drv: {
buildTools = (drv.buildTools or [ ]) ++ xs;
});
addExtraLibrary = x: addExtraLibraries [ x ];
addExtraLibraries =
xs:
overrideCabal (drv: {
extraLibraries = (drv.extraLibraries or [ ]) ++ xs;
});
addBuildDepend = x: addBuildDepends [ x ];
addBuildDepends =
xs:
overrideCabal (drv: {
buildDepends = (drv.buildDepends or [ ]) ++ xs;
});
addTestToolDepend = x: addTestToolDepends [ x ];
addTestToolDepends =
xs:
overrideCabal (drv: {
testToolDepends = (drv.testToolDepends or [ ]) ++ xs;
});
addPkgconfigDepend = x: addPkgconfigDepends [ x ];
addPkgconfigDepends =
xs:
overrideCabal (drv: {
pkg-configDepends = (drv.pkg-configDepends or [ ]) ++ xs;
});
addSetupDepend = x: addSetupDepends [ x ];
addSetupDepends =
xs:
overrideCabal (drv: {
setupHaskellDepends = (drv.setupHaskellDepends or [ ]) ++ xs;
});
enableCabalFlag = x: drv: appendConfigureFlag "-f${x}" (removeConfigureFlag "-f-${x}" drv);
disableCabalFlag = x: drv: appendConfigureFlag "-f-${x}" (removeConfigureFlag "-f${x}" drv);
markBroken = overrideCabal (drv: {
broken = true;
hydraPlatforms = [ ];
});
unmarkBroken = overrideCabal (drv: {
broken = false;
});
markBrokenVersion =
version: drv:
assert drv.version == version;
markBroken drv;
markUnbroken = overrideCabal (drv: {
broken = false;
});
/*
disableParallelBuilding drops the -j<n> option from the GHC
command line for the given package. This can be useful in rare
situations where parallel building of a package causes GHC to
fail for some reason.
*/
disableParallelBuilding = overrideCabal (drv: {
enableParallelBuilding = false;
});
enableLibraryProfiling = overrideCabal (drv: {
enableLibraryProfiling = true;
});
disableLibraryProfiling = overrideCabal (drv: {
enableLibraryProfiling = false;
});
enableExecutableProfiling = overrideCabal (drv: {
enableExecutableProfiling = true;
});
disableExecutableProfiling = overrideCabal (drv: {
enableExecutableProfiling = false;
});
enableSharedExecutables = overrideCabal (drv: {
enableSharedExecutables = true;
});
disableSharedExecutables = overrideCabal (drv: {
enableSharedExecutables = false;
});
enableSharedLibraries = overrideCabal (drv: {
enableSharedLibraries = true;
});
disableSharedLibraries = overrideCabal (drv: {
enableSharedLibraries = false;
});
enableDeadCodeElimination = overrideCabal (drv: {
enableDeadCodeElimination = true;
});
disableDeadCodeElimination = overrideCabal (drv: {
enableDeadCodeElimination = false;
});
enableStaticLibraries = overrideCabal (drv: {
enableStaticLibraries = true;
});
disableStaticLibraries = overrideCabal (drv: {
enableStaticLibraries = false;
});
enableSeparateBinOutput = overrideCabal (drv: {
enableSeparateBinOutput = true;
});
appendPatch = x: appendPatches [ x ];
appendPatches =
xs:
overrideCabal (drv: {
patches = (drv.patches or [ ]) ++ xs;
});
/*
Set a specific build target instead of compiling all targets in the package.
For example, imagine we have a .cabal file with a library, and 2 executables "dev" and "server".
We can build only "server" and not wait on the compilation of "dev" by using setBuildTarget as follows:
> setBuildTarget "server" (callCabal2nix "thePackageName" thePackageSrc {})
*/
setBuildTargets =
xs:
overrideCabal (drv: {
buildTarget = lib.concatStringsSep " " xs;
});
setBuildTarget = x: setBuildTargets [ x ];
doHyperlinkSource = overrideCabal (drv: {
hyperlinkSource = true;
});
dontHyperlinkSource = overrideCabal (drv: {
hyperlinkSource = false;
});
disableHardening =
flags:
overrideCabal (drv: {
hardeningDisable = flags;
});
/*
Let Nix strip the binary files.
This removes debugging symbols.
*/
doStrip = overrideCabal (drv: {
dontStrip = false;
});
/*
Stop Nix from stripping the binary files.
This keeps debugging symbols.
*/
dontStrip = overrideCabal (drv: {
dontStrip = true;
});
/*
Useful for debugging segfaults with gdb.
This includes dontStrip.
*/
enableDWARFDebugging =
drv:
# -g: enables debugging symbols
# --disable-*-stripping: tell GHC not to strip resulting binaries
# dontStrip: see above
appendConfigureFlag "--ghc-options=-g --disable-executable-stripping --disable-library-stripping" (
dontStrip drv
);
/*
Create a source distribution tarball like those found on hackage,
instead of building the package.
*/
sdistTarball =
pkg:
lib.overrideDerivation pkg (drv: {
name = "${drv.pname}-source-${drv.version}";
# Since we disable the haddock phase, we also need to override the
# outputs since the separate doc output will not be produced.
outputs = [ "out" ];
buildPhase = "./Setup sdist";
haddockPhase = ":";
checkPhase = ":";
installPhase = "install -D dist/${drv.pname}-*.tar.gz $out/${drv.pname}-${drv.version}.tar.gz";
fixupPhase = ":";
});
/*
Create a documentation tarball suitable for uploading to Hackage instead
of building the package.
*/
documentationTarball =
pkg:
pkgs.lib.overrideDerivation pkg (drv: {
name = "${drv.name}-docs";
# Like sdistTarball, disable the "doc" output here.
outputs = [ "out" ];
buildPhase = ''
runHook preHaddock
./Setup haddock --for-hackage
runHook postHaddock
'';
haddockPhase = ":";
checkPhase = ":";
installPhase = ''
runHook preInstall
mkdir -p "$out"
tar --format=ustar \
-czf "$out/${drv.name}-docs.tar.gz" \
-C dist/doc/html "${drv.name}-docs"
runHook postInstall
'';
});
/*
Use the gold linker. It is a linker for ELF that is designed
"to run as fast as possible on modern systems"
*/
linkWithGold = appendConfigureFlag "--ghc-option=-optl-fuse-ld=gold --ld-option=-fuse-ld=gold --with-ld=ld.gold";
/*
link executables statically against haskell libs to reduce
closure size
*/
justStaticExecutables = overrideCabal (drv: {
enableSharedExecutables = false;
enableLibraryProfiling = drv.enableExecutableProfiling or false;
isLibrary = false;
doHaddock = false;
postFixup =
drv.postFixup or ""
+ ''
# Remove every directory which could have links to other store paths.
rm -rf $out/lib $out/nix-support $out/share/doc
'';
disallowGhcReference = true;
});
/*
Build a source distribution tarball instead of using the source files
directly. The effect is that the package is built as if it were published
on hackage. This can be used as a test for the source distribution,
assuming the build fails when packaging mistakes are in the cabal file.
A faster implementation using `cabal-install` is available as
`buildFromCabalSdist` in your Haskell package set.
*/
buildFromSdist =
pkg:
overrideCabal (drv: {
src = "${sdistTarball pkg}/${pkg.pname}-${pkg.version}.tar.gz";
# Revising and jailbreaking the cabal file has been handled in sdistTarball
revision = null;
editedCabalFile = null;
jailbreak = false;
}) pkg;
/*
Build the package in a strict way to uncover potential problems.
This includes buildFromSdist and failOnAllWarnings.
*/
buildStrictly = pkg: buildFromSdist (failOnAllWarnings pkg);
# Disable core optimizations, significantly speeds up build time
disableOptimization = appendConfigureFlag "--disable-optimization";
/*
Turn on most of the compiler warnings and fail the build if any
of them occur.
*/
failOnAllWarnings = appendConfigureFlag "--ghc-option=-Wall --ghc-option=-Werror";
/*
Add a post-build check to verify that dependencies declared in
the cabal file are actually used.
The first attrset argument can be used to configure the strictness
of this check and a list of ignored package names that would otherwise
cause false alarms.
*/
checkUnusedPackages =
{
ignoreEmptyImports ? false,
ignoreMainModule ? false,
ignorePackages ? [ ],
}:
drv:
overrideCabal (_drv: {
postBuild =
let
args = lib.concatStringsSep " " (
lib.optional ignoreEmptyImports "--ignore-empty-imports"
++ lib.optional ignoreMainModule "--ignore-main-module"
++ map (pkg: "--ignore-package ${pkg}") ignorePackages
);
in
"${pkgs.haskellPackages.packunused}/bin/packunused" + lib.optionalString (args != "") " ${args}";
}) (appendConfigureFlag "--ghc-option=-ddump-minimal-imports" drv);
buildStackProject = pkgs.callPackage ../generic-stack-builder.nix { };
/*
Add a dummy command to trigger a build despite an equivalent
earlier build that is present in the store or cache.
*/
triggerRebuild =
i:
overrideCabal (drv: {
postUnpack =
drv.postUnpack or ""
+ ''
# trigger rebuild ${toString i}
'';
});
/*
Override the sources for the package and optionally the version.
This also takes of removing editedCabalFile.
*/
overrideSrc =
{
src,
version ? null,
}:
drv:
overrideCabal (_: {
inherit src;
version = if version == null then drv.version else version;
editedCabalFile = null;
}) drv;
# Get all of the build inputs of a haskell package, divided by category.
getBuildInputs = p: p.getBuildInputs;
# Extract the haskell build inputs of a haskell package.
# This is useful to build environments for developing on that
# package.
getHaskellBuildInputs = p: (getBuildInputs p).haskellBuildInputs;
# Under normal evaluation, simply return the original package. Under
# nix-shell evaluation, return a nix-shell optimized environment.
shellAware = p: if lib.inNixShell then p.env else p;
ghcInfo = ghc: rec {
isCross = (ghc.cross or null) != null;
isGhcjs = ghc.isGhcjs or false;
nativeGhc = if isCross || isGhcjs then ghc.bootPkgs.ghc else ghc;
};
### mkDerivation helpers
# These allow external users of a haskell package to extract
# information about how it is built in the same way that the
# generic haskell builder does, by reusing the same functions.
# Each function here has the same interface as mkDerivation and thus
# can be called for a given package simply by overriding the
# mkDerivation argument it used. See getHaskellBuildInputs above for
# an example of this.
# Some information about which phases should be run.
controlPhases =
ghc:
let
inherit (ghcInfo ghc) isCross;
in
{
doCheck ? !isCross,
doBenchmark ? false,
...
}:
{
inherit doCheck doBenchmark;
};
# Utility to convert a directory full of `cabal2nix`-generated files into a
# package override set
#
# packagesFromDirectory : { directory : Directory, ... } -> HaskellPackageOverrideSet
packagesFromDirectory =
{ directory, ... }:
self: super:
let
haskellPaths = lib.filter (lib.hasSuffix ".nix") (builtins.attrNames (builtins.readDir directory));
toKeyVal = file: {
name = builtins.replaceStrings [ ".nix" ] [ "" ] file;
value = self.callPackage (directory + "/${file}") { };
};
in
builtins.listToAttrs (map toKeyVal haskellPaths);
/*
INTERNAL function retained for backwards compatibility, use
haskell.packages.*.generateOptparseApplicativeCompletions instead!
*/
__generateOptparseApplicativeCompletion =
exeName:
overrideCabal (drv: {
postInstall =
(drv.postInstall or "")
+ ''
bashCompDir="''${!outputBin}/share/bash-completion/completions"
zshCompDir="''${!outputBin}/share/zsh/vendor-completions"
fishCompDir="''${!outputBin}/share/fish/vendor_completions.d"
mkdir -p "$bashCompDir" "$zshCompDir" "$fishCompDir"
"''${!outputBin}/bin/${exeName}" --bash-completion-script "''${!outputBin}/bin/${exeName}" >"$bashCompDir/${exeName}"
"''${!outputBin}/bin/${exeName}" --zsh-completion-script "''${!outputBin}/bin/${exeName}" >"$zshCompDir/_${exeName}"
"''${!outputBin}/bin/${exeName}" --fish-completion-script "''${!outputBin}/bin/${exeName}" >"$fishCompDir/${exeName}.fish"
# Sanity check
grep -F ${exeName} <$bashCompDir/${exeName} >/dev/null || {
echo 'Could not find ${exeName} in completion script.'
exit 1
}
'';
});
/*
Retained for backwards compatibility.
Use haskell.packages.*.generateOptparseApplicativeCompletions
which is cross aware instead.
*/
generateOptparseApplicativeCompletions =
commands: pkg:
lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2211)
"haskellLib.generateOptparseApplicativeCompletions is deprecated in favor of haskellPackages.generateOptparseApplicativeCompletions. Please change ${pkg.name} to use the latter and make sure it uses its matching haskell.packages set!"
(pkgs.lib.foldr __generateOptparseApplicativeCompletion pkg commands);
/*
Retained for backwards compatibility.
Use haskell.packages.*.generateOptparseApplicativeCompletions
which is cross aware instead.
*/
generateOptparseApplicativeCompletion =
command: pkg:
lib.warnIf (lib.oldestSupportedReleaseIsAtLeast 2211)
"haskellLib.generateOptparseApplicativeCompletion is deprecated in favor of haskellPackages.generateOptparseApplicativeCompletions (plural!). Please change ${pkg.name} to use the latter and make sure it uses its matching haskell.packages set!"
(__generateOptparseApplicativeCompletion command pkg);
# Don't fail at configure time if there are multiple versions of the
# same package in the (recursive) dependencies of the package being
# built. Will delay failures, if any, to compile time.
allowInconsistentDependencies = overrideCabal (drv: {
allowInconsistentDependencies = true;
});
# Work around a Cabal bug requiring pkg-config --static --libs to work even
# when linking dynamically, affecting Cabal 3.8 and 3.9.
# https://github.com/haskell/cabal/issues/8455
#
# For this, we treat the runtime system/pkg-config dependencies of a Haskell
# derivation as if they were propagated from their dependencies which allows
# pkg-config --static to work in most cases.
#
# Warning: This function may change or be removed at any time, e.g. if we find
# a different workaround, upstream fixes the bug or we patch Cabal.
__CabalEagerPkgConfigWorkaround =
let
# Take list of derivations and return list of the transitive dependency
# closure, only taking into account buildInputs. Loosely based on
# closePropagationFast.
propagatedPlainBuildInputs =
drvs:
builtins.map (i: i.val) (
builtins.genericClosure {
startSet = builtins.map (drv: {
key = drv.outPath;
val = drv;
}) drvs;
operator =
{ val, ... }:
if !lib.isDerivation val then
[ ]
else
builtins.concatMap (
drv:
if !lib.isDerivation drv then
[ ]
else
[
{
key = drv.outPath;
val = drv;
}
]
) (val.buildInputs or [ ] ++ val.propagatedBuildInputs or [ ]);
}
);
in
overrideCabal (old: {
benchmarkPkgconfigDepends = propagatedPlainBuildInputs old.benchmarkPkgconfigDepends or [ ];
executablePkgconfigDepends = propagatedPlainBuildInputs old.executablePkgconfigDepends or [ ];
libraryPkgconfigDepends = propagatedPlainBuildInputs old.libraryPkgconfigDepends or [ ];
testPkgconfigDepends = propagatedPlainBuildInputs old.testPkgconfigDepends or [ ];
});
}