mirror of
https://github.com/ilyakooo0/haskell.nix.git
synced 2024-09-11 06:55:40 +03:00
support cabal-doctest (#427)
* Add test. * wip * Fix merge regression * Combine drv, drv.source and drv.dist for doctest * Skip cabal-doctests test when cross compiling * Add the --no-magic flag to the .cabal file for the cabal-doctests test. This appears to be necessary on OSX. The --no-magic flag stops the doctest executable from expanding path arguments, trying to locate the package db, etc. This shouldn't be necessary with cabal-doctest, since all the necessary options to pass to doctest are computed when running the Setup.hs script. See https://github.com/input-output-hk/haskell.nix/pull/427#issuecomment-605761780. * Fix cabal-doctest support * Skip cabal-doctest test plan cross compiling * More fixes for cabal-doctest * Skip cabal-doctest tests when cross compiling Co-authored-by: Moritz Angermann <moritz.angermann@gmail.com> Co-authored-by: Hamish Mackenzie <Hamish.Mackenzie@iohk.io> Co-authored-by: Hamish Mackenzie <Hamish.K.Mackenzie@gmail.com>
This commit is contained in:
parent
d655dbf7b5
commit
6c627b429b
@ -23,6 +23,14 @@ let self =
|
||||
, preInstall ? component.preInstall , postInstall ? component.postInstall
|
||||
, preHaddock ? component.preHaddock , postHaddock ? component.postHaddock
|
||||
, shellHook ? ""
|
||||
, configureAllComponents ? component.configureAllComponents ||
|
||||
# When set, configure all the components in the package
|
||||
# (not just the one we are building).
|
||||
# Enable for tests in packages that use cabal-doctest.
|
||||
( haskellLib.isTest componentId &&
|
||||
lib.any (x: x.identifier.name or "" == "cabal-doctest") package.setup-depends
|
||||
)
|
||||
, allComponent # Used when `configureAllComponents` is set to get a suitable configuration.
|
||||
|
||||
, build-tools ? component.build-tools
|
||||
, pkgconfig ? component.pkgconfig
|
||||
@ -47,7 +55,7 @@ let self =
|
||||
, doHoogle ? component.doHoogle # Also build a hoogle index
|
||||
, hyperlinkSource ? component.doHyperlinkSource # Link documentation to the source code
|
||||
, quickjump ? component.doQuickjump # Generate an index for interactive documentation navigation
|
||||
, keepSource ? component.keepSource # Build from `source` output in the store then delete `dist`
|
||||
, keepSource ? component.keepSource || configureAllComponents # Build from `source` output in the store then delete `dist`
|
||||
, setupHaddockFlags ? component.setupHaddockFlags
|
||||
|
||||
# Profiling
|
||||
@ -77,6 +85,11 @@ let self =
|
||||
}@drvArgs:
|
||||
|
||||
let
|
||||
componentForSetup =
|
||||
if configureAllComponents
|
||||
then allComponent
|
||||
else component;
|
||||
|
||||
# Ignore attempts to include DWARF info when it is not possible
|
||||
enableDWARF = drvArgs.enableDWARF or false
|
||||
&& stdenv.hostPlatform.isLinux
|
||||
@ -96,7 +109,7 @@ let
|
||||
# is the sub directory in that root path that contains the package.
|
||||
# `cleanSrc.subDir` is used in `prePatch` and `lib/cover.nix`.
|
||||
cleanSrc = haskellLib.rootAndSubDir (if canCleanSource
|
||||
then haskellLib.cleanCabalComponent package component "${componentId.ctype}-${componentId.cname}" src
|
||||
then haskellLib.cleanCabalComponent package componentForSetup "${componentId.ctype}-${componentId.cname}" src
|
||||
else
|
||||
# We can clean out the siblings though to at least avoid changes to other packages
|
||||
# from triggering a rebuild of this one.
|
||||
@ -118,8 +131,9 @@ let
|
||||
needsProfiling = enableExecutableProfiling || enableLibraryProfiling;
|
||||
|
||||
configFiles = makeConfigFiles {
|
||||
component = componentForSetup;
|
||||
inherit (package) identifier;
|
||||
inherit component fullName flags needsProfiling enableDWARF;
|
||||
inherit fullName flags needsProfiling enableDWARF;
|
||||
};
|
||||
|
||||
enableFeature = enable: feature:
|
||||
@ -129,8 +143,13 @@ let
|
||||
|
||||
finalConfigureFlags = lib.concatStringsSep " " (
|
||||
[ "--prefix=$out"
|
||||
"${haskellLib.componentTarget componentId}"
|
||||
"$(cat ${configFiles}/configure-flags)"
|
||||
] ++ (
|
||||
# If configureAllComponents is set we should not specify the component
|
||||
# and Setup will attempt to configure them all.
|
||||
if configureAllComponents
|
||||
then ["--enable-tests" "--enable-benchmarks"]
|
||||
else ["${haskellLib.componentTarget componentId}"]
|
||||
) ++ [ "$(cat ${configFiles}/configure-flags)"
|
||||
] ++ commonConfigureFlags);
|
||||
|
||||
# From nixpkgs 20.09, the pkg-config exe has a prefix matching the ghc one
|
||||
@ -328,7 +347,7 @@ let
|
||||
++ (lib.optional enableSeparateDataOutput "data")
|
||||
++ (lib.optional keepSource "source");
|
||||
|
||||
configurePhase =
|
||||
prePatch =
|
||||
# emcc is very slow if it cannot cache stuff in $HOME
|
||||
(lib.optionalString (stdenv.hostPlatform.isGhcjs) ''
|
||||
export HOME=$(mktemp -d)
|
||||
@ -340,7 +359,9 @@ let
|
||||
cp -r . $source
|
||||
cd $source
|
||||
chmod -R +w .
|
||||
'') + ''
|
||||
'') + commonAttrs.prePatch;
|
||||
|
||||
configurePhase = ''
|
||||
runHook preConfigure
|
||||
echo Configure flags:
|
||||
printf "%q " ${finalConfigureFlags}
|
||||
@ -368,7 +389,11 @@ let
|
||||
target-pkg-and-db = "${ghc.targetPrefix}ghc-pkg -v0 --package-db $out/package.conf.d";
|
||||
in ''
|
||||
runHook preInstall
|
||||
$SETUP_HS copy ${lib.concatStringsSep " " setupInstallFlags}
|
||||
$SETUP_HS copy ${lib.concatStringsSep " " (
|
||||
setupInstallFlags
|
||||
++ lib.optional configureAllComponents
|
||||
(haskellLib.componentTarget componentId)
|
||||
)}
|
||||
${lib.optionalString (haskellLib.isLibrary componentId) ''
|
||||
$SETUP_HS register --gen-pkg-config=${name}.conf
|
||||
${ghc.targetPrefix}ghc-pkg -v0 init $out/package.conf.d
|
||||
@ -455,9 +480,24 @@ let
|
||||
'')
|
||||
}
|
||||
runHook postInstall
|
||||
'' + (lib.optionalString keepSource ''
|
||||
rm -rf dist
|
||||
'') + (lib.optionalString (haskellLib.isTest componentId) ''
|
||||
'' + (
|
||||
# Keep just the autogen files and package.conf.inplace package
|
||||
# DB (probably empty unless this is a library component).
|
||||
# We also have to remove any refernces to $out to avoid
|
||||
# circular references.
|
||||
if configureAllComponents
|
||||
then ''
|
||||
mv dist dist-tmp-dir
|
||||
mkdir -p dist/build
|
||||
mv dist-tmp-dir/build/${componentId.cname}/autogen dist/build/
|
||||
mv dist-tmp-dir/package.conf.inplace dist/
|
||||
remove-references-to -t $out dist/build/autogen/*
|
||||
rm -rf dist-tmp-dir
|
||||
''
|
||||
else lib.optionalString keepSource ''
|
||||
rm -rf dist
|
||||
''
|
||||
) + (lib.optionalString (haskellLib.isTest componentId) ''
|
||||
echo The test ${package.identifier.name}.components.tests.${componentId.cname} was built. To run the test build ${package.identifier.name}.checks.${componentId.cname}.
|
||||
'');
|
||||
|
||||
|
@ -107,14 +107,14 @@ let
|
||||
inherit (pkg) preUnpack postUnpack;
|
||||
};
|
||||
|
||||
buildComp = componentId: component: comp-builder {
|
||||
inherit componentId component package name src flags setup cabalFile cabal-generator patches revision
|
||||
buildComp = allComponent: componentId: component: comp-builder {
|
||||
inherit allComponent componentId component package name src flags setup cabalFile cabal-generator patches revision
|
||||
shellHook
|
||||
;
|
||||
};
|
||||
|
||||
in rec {
|
||||
components = haskellLib.applyComponents buildComp pkg;
|
||||
components = haskellLib.applyComponents (buildComp pkg.allComponent) pkg;
|
||||
checks = pkgs.recurseIntoAttrs (builtins.mapAttrs
|
||||
(_: d: haskellLib.check d)
|
||||
(lib.filterAttrs (_: d: d.config.doCheck) components.tests));
|
||||
|
@ -10,7 +10,7 @@ let
|
||||
in stdenv.mkDerivation ({
|
||||
name = (drv.name + "-check");
|
||||
|
||||
# Useing `srcOnly` (rather than getting the `src` via a `drv.passthru`)
|
||||
# Using `srcOnly` (rather than getting the `src` via a `drv.passthru`)
|
||||
# should correctly apply the patches from `drv` (if any).
|
||||
src = drv.source or (srcOnly drv);
|
||||
|
||||
|
@ -21,7 +21,86 @@ with types;
|
||||
|
||||
# Work around issue that can cause _lots_ of files to be copied into the store.
|
||||
# See https://github.com/NixOS/nixpkgs/pull/64691
|
||||
let path = types.path // { check = x: types.path.check (x.origSrc or x); };
|
||||
let
|
||||
path = types.path // { check = x: types.path.check (x.origSrc or x); };
|
||||
|
||||
componentType = submodule {
|
||||
# add the shared componentOptions
|
||||
options = (packageOptions config) // {
|
||||
buildable = mkOption {
|
||||
type = bool;
|
||||
default = true;
|
||||
};
|
||||
depends = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
libs = mkOption {
|
||||
type = listOfFilteringNulls (nullOr package);
|
||||
default = [];
|
||||
};
|
||||
frameworks = mkOption {
|
||||
type = listOfFilteringNulls package;
|
||||
default = [];
|
||||
};
|
||||
pkgconfig = mkOption {
|
||||
type = listOf (listOfFilteringNulls package);
|
||||
default = [];
|
||||
};
|
||||
build-tools = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
modules = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
asmSources = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
cmmSources = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
cSources = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
cxxSources = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
jsSources = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
hsSourceDirs = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = ["."];
|
||||
};
|
||||
includeDirs = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
includes = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
mainPath = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
extraSrcFiles = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
platforms = mkOption {
|
||||
type = nullOr (listOfFilteringNulls unspecified);
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
in {
|
||||
# This is how the Nix expressions generated by *-to-nix receive
|
||||
@ -138,85 +217,7 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
components = let
|
||||
componentType = submodule {
|
||||
# add the shared componentOptions
|
||||
options = (packageOptions config) // {
|
||||
buildable = mkOption {
|
||||
type = bool;
|
||||
default = true;
|
||||
};
|
||||
depends = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
libs = mkOption {
|
||||
type = listOfFilteringNulls (nullOr package);
|
||||
default = [];
|
||||
};
|
||||
frameworks = mkOption {
|
||||
type = listOfFilteringNulls package;
|
||||
default = [];
|
||||
};
|
||||
pkgconfig = mkOption {
|
||||
type = listOf (listOfFilteringNulls package);
|
||||
default = [];
|
||||
};
|
||||
build-tools = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
modules = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
asmSources = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
cmmSources = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
cSources = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
cxxSources = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
jsSources = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
hsSourceDirs = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = ["."];
|
||||
};
|
||||
includeDirs = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
includes = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
mainPath = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
extraSrcFiles = mkOption {
|
||||
type = listOfFilteringNulls unspecified;
|
||||
default = [];
|
||||
};
|
||||
platforms = mkOption {
|
||||
type = nullOr (listOfFilteringNulls unspecified);
|
||||
default = null;
|
||||
};
|
||||
};
|
||||
};
|
||||
in {
|
||||
components = {
|
||||
setup = mkOption {
|
||||
type = nullOr componentType;
|
||||
default = {
|
||||
@ -295,5 +296,43 @@ in {
|
||||
type = listOf (either unspecified path);
|
||||
default = [];
|
||||
};
|
||||
# This used to be `components.all` but it has been added back as `allComponent` to
|
||||
# to avoid confusion. It is not mapped by `builder/hspkg-builder.nix` to anything
|
||||
# you can build. Instead it is used internally when `configureAllComponents`
|
||||
# is set or for tests whe on `cabal-doctest` is in the `setup-depends` of the package.
|
||||
allComponent = mkOption {
|
||||
type = componentType;
|
||||
apply = all: all // {
|
||||
# TODO: Should this check for the entire component
|
||||
# definition to match, rather than just the identifier?
|
||||
depends = builtins.filter (p: p.identifier != config.package.identifier) all.depends;
|
||||
};
|
||||
description = "The merged dependencies of all other components";
|
||||
};
|
||||
};
|
||||
|
||||
# This has one quirk. Manually setting options on the all component
|
||||
# will be considered a conflict. This is almost always fine; most
|
||||
# settings should be modified in either the package options, or an
|
||||
# individual component's options. When this isn't sufficient,
|
||||
# mkForce is a reasonable workaround.
|
||||
#
|
||||
# An alternative solution to mkForce for many of the options where
|
||||
# this is relevant would be to switch from the bool type to
|
||||
# something like an anyBool type, which would merge definitions by
|
||||
# returning true if any is true.
|
||||
config.allComponent =
|
||||
let allComps = haskellLib.getAllComponents config;
|
||||
in lib.mkMerge (
|
||||
builtins.map (c:
|
||||
# Exclude attributes that are likely to have conflicting definitions
|
||||
# (a common use case for `all` is in `shellFor` and it only has an
|
||||
# install phase).
|
||||
builtins.removeAttrs c ["preCheck" "postCheck" "keepSource"]
|
||||
) allComps
|
||||
) // {
|
||||
# If any one of the components needs us to keep the source
|
||||
# then keep it for the `all` component
|
||||
keepSource = lib.foldl' (x: comp: x || comp.keepSource) false allComps;
|
||||
};
|
||||
}
|
||||
|
@ -138,6 +138,11 @@ let
|
||||
type = bool;
|
||||
default = (def.enableShared or true);
|
||||
};
|
||||
configureAllComponents = mkOption {
|
||||
description = "If set all the components in the package are configured (useful for cabal-doctest).";
|
||||
type = bool;
|
||||
default = false;
|
||||
};
|
||||
shellHook = mkOption {
|
||||
description = "Hook to run when entering a shell";
|
||||
type = unspecified; # Can be either a string or a function
|
||||
|
6
test/cabal-doctests/Setup.hs
Normal file
6
test/cabal-doctests/Setup.hs
Normal file
@ -0,0 +1,6 @@
|
||||
module Main where
|
||||
|
||||
import Distribution.Extra.Doctest (defaultMainWithDoctests)
|
||||
|
||||
main :: IO ()
|
||||
main = defaultMainWithDoctests "doctests"
|
31
test/cabal-doctests/cabal-doctests-test.cabal
Normal file
31
test/cabal-doctests/cabal-doctests-test.cabal
Normal file
@ -0,0 +1,31 @@
|
||||
cabal-version: 1.12
|
||||
|
||||
name: cabal-doctests-test
|
||||
version: 0.1.0.0
|
||||
build-type: Custom
|
||||
|
||||
custom-setup
|
||||
setup-depends:
|
||||
base >= 4 && <5,
|
||||
Cabal,
|
||||
cabal-doctest >= 1 && <1.1
|
||||
|
||||
library
|
||||
hs-source-dirs: src
|
||||
exposed-modules: Lib
|
||||
other-modules: Paths_cabal_doctests_test
|
||||
build-depends:
|
||||
base >=4.7 && <5,
|
||||
aeson
|
||||
default-language: Haskell2010
|
||||
|
||||
test-suite doctests
|
||||
hs-source-dirs: doctests
|
||||
x-doctest-options: --no-magic
|
||||
type: exitcode-stdio-1.0
|
||||
main-is: Doctests.hs
|
||||
ghc-options: -threaded -rtsopts -with-rtsopts=-N
|
||||
build-depends:
|
||||
base >=4.7 && <5
|
||||
, doctest
|
||||
default-language: Haskell2010
|
47
test/cabal-doctests/default.nix
Normal file
47
test/cabal-doctests/default.nix
Normal file
@ -0,0 +1,47 @@
|
||||
# Test a package set
|
||||
{ stdenv, util, cabalProject', haskellLib, gmp6, zlib, recurseIntoAttrs, runCommand, testSrc, compiler-nix-name }:
|
||||
|
||||
with stdenv.lib;
|
||||
|
||||
let
|
||||
project =
|
||||
cabalProject' {
|
||||
inherit compiler-nix-name;
|
||||
src = testSrc "cabal-doctests";
|
||||
index-state = "2021-01-11T00:00:00Z";
|
||||
};
|
||||
|
||||
packages = project.hsPkgs;
|
||||
|
||||
meta = {
|
||||
platforms = platforms.all;
|
||||
# Making this work for cross compilers will be difficult.
|
||||
disabled = stdenv.buildPlatform != stdenv.hostPlatform;
|
||||
};
|
||||
|
||||
in recurseIntoAttrs ({
|
||||
ifdInputs = {
|
||||
plan-nix = addMetaAttrs meta project.plan-nix;
|
||||
};
|
||||
run = stdenv.mkDerivation {
|
||||
name = "cabal-doctests-test";
|
||||
|
||||
buildCommand = ''
|
||||
printf "Checking that doctest tests have run ... " >& 2
|
||||
cat ${packages.cabal-doctests-test.checks.doctests}/test-stdout >& 2
|
||||
|
||||
touch $out
|
||||
'';
|
||||
|
||||
meta = {
|
||||
platforms = platforms.all;
|
||||
# Making cabal-doctest work for cross compilers will be difficult.
|
||||
disabled = stdenv.buildPlatform != stdenv.hostPlatform;
|
||||
};
|
||||
|
||||
passthru = {
|
||||
# Used for debugging with nix repl
|
||||
inherit project packages;
|
||||
};
|
||||
};
|
||||
})
|
16
test/cabal-doctests/doctests/Doctests.hs
Normal file
16
test/cabal-doctests/doctests/Doctests.hs
Normal file
@ -0,0 +1,16 @@
|
||||
module Main where
|
||||
|
||||
import Build_doctests (flags, pkgs, module_sources)
|
||||
import Data.Foldable (traverse_)
|
||||
import System.Environment (unsetEnv)
|
||||
import Test.DocTest (doctest)
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
putStrLn ""
|
||||
traverse_ putStrLn args -- optionally print arguments
|
||||
unsetEnv "GHC_ENVIRONMENT" -- see 'Notes'; you may not need this
|
||||
doctest args
|
||||
where
|
||||
args :: [String]
|
||||
args = flags ++ pkgs ++ module_sources -- ++ ["-v"]
|
18
test/cabal-doctests/src/Lib.hs
Normal file
18
test/cabal-doctests/src/Lib.hs
Normal file
@ -0,0 +1,18 @@
|
||||
module Lib
|
||||
( someFunc
|
||||
) where
|
||||
|
||||
import Data.Aeson (encode)
|
||||
import Paths_cabal_doctests_test (version)
|
||||
|
||||
-- |
|
||||
-- >>> 1 + 1
|
||||
-- 2
|
||||
--
|
||||
-- >>> version
|
||||
-- Version {versionBranch = [0,1,0,0], versionTags = []}
|
||||
--
|
||||
-- >>> encode (Just 1 :: Maybe Int)
|
||||
-- "1"
|
||||
someFunc :: IO ()
|
||||
someFunc = putStrLn "someFunc"
|
@ -179,6 +179,7 @@ let
|
||||
ghc-options-stack = callTest ./ghc-options/stack.nix {};
|
||||
exe-only = callTest ./exe-only { inherit util compiler-nix-name; };
|
||||
stack-source-repo = callTest ./stack-source-repo {};
|
||||
cabal-doctests = callTest ./cabal-doctests { inherit util compiler-nix-name; };
|
||||
extra-hackage = callTest ./extra-hackage { inherit compiler-nix-name; };
|
||||
hls-cabal = callTest ./haskell-language-server/cabal.nix { inherit compiler-nix-name; };
|
||||
hls-stack = callTest ./haskell-language-server/stack.nix { inherit compiler-nix-name; };
|
||||
|
Loading…
Reference in New Issue
Block a user