diff options
Diffstat (limited to 'nixpkgs/pkgs/development/haskell-modules/lib/compose.nix')
-rw-r--r-- | nixpkgs/pkgs/development/haskell-modules/lib/compose.nix | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/nixpkgs/pkgs/development/haskell-modules/lib/compose.nix b/nixpkgs/pkgs/development/haskell-modules/lib/compose.nix new file mode 100644 index 000000000000..a831a83a15f5 --- /dev/null +++ b/nixpkgs/pkgs/development/haskell-modules/lib/compose.nix @@ -0,0 +1,473 @@ +# 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; }); + + /* doBenchmark enables dependency checking, compilation and execution + for benchmarks listed in the package description file. + */ + 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 = 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; }); + + 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 = 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 + ''; + }); + + /* 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. + */ + 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 = with lib; + let args = concatStringsSep " " ( + optional ignoreEmptyImports "--ignore-empty-imports" ++ + optional ignoreMainModule "--ignore-main-module" ++ + map (pkg: "--ignore-package ${pkg}") ignorePackages + ); + in "${pkgs.haskellPackages.packunused}/bin/packunused" + + 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 = ": trigger rebuild ${toString i}"; }); + + /* Override the sources for the package and optionaly 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 && (lib.versionOlder "7.4" ghc.version) + , 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 = builtins.attrNames (builtins.readDir directory); + + toKeyVal = file: { + name = builtins.replaceStrings [ ".nix" ] [ "" ] file; + + value = self.callPackage (directory + "/${file}") { }; + }; + + in + builtins.listToAttrs (map toKeyVal haskellPaths); + + addOptparseApplicativeCompletionScripts = exeName: pkg: + builtins.trace "addOptparseApplicativeCompletionScripts is deprecated in favor of generateOptparseApplicativeCompletion. Please change ${pkg.name} to use the latter or its plural form." + (generateOptparseApplicativeCompletion exeName pkg); + + /* + Modify a Haskell package to add shell completion scripts for the + given executable produced by it. These completion scripts will be + picked up automatically if the resulting derivation is installed, + e.g. by `nix-env -i`. + + Invocation: + generateOptparseApplicativeCompletion command pkg + + + command: name of an executable + pkg: Haskell package that builds the executables + */ + 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 + } + ''; + }); + + /* + Modify a Haskell package to add shell completion scripts for the + given executables produced by it. These completion scripts will be + picked up automatically if the resulting derivation is installed, + e.g. by `nix-env -i`. + + Invocation: + generateOptparseApplicativeCompletions commands pkg + + + commands: name of an executable + pkg: Haskell package that builds the executables + */ + generateOptparseApplicativeCompletions = commands: pkg: + pkgs.lib.foldr generateOptparseApplicativeCompletion pkg commands; + + # 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; + }); +} |