on iko/ghcjs-patch-master help sign in
Coverage: allow projects with no libraries (#962)

* Coverage: allow projects with no libraries - Prevent projects without a library from breaking the coverage report generators. - Added a test to ensure projects without libraries can build coverage reports. * Ensure the correct GHC version is used in project coverage reports - Previously, when trying to determine the version of GHC to use for a project coverage report, we would attempt to find the version of GHC used in the constituent coverage reports, or default to the GHC that is shipped with Nixpkgs. Using the GHC that ships with Nixpkgs is problematic (specifically the coverage-no-libs tests fail to run on Hydra), so we now ask the user to pass in the project to the "projectCoverageReport" function so we can find the correct version of GHC to use in a foolproof manner.

commit4c42100df06a0a1d976e5e883a3eedf053d8b91f authorSamuel Evans-Powell <mail@sevanspowell.net> dateThu 10 Dec 2020, 15:47 (6 years ago) parent2d710b8
9 files changed+140−11
lib/cover-project.nix+7−10 overlays/haskell.nix+1−1 test/coverage-no-libs/cabal.project+1 test/coverage-no-libs/default.nix+101 test/coverage-no-libs/pkga/MainA.hs+4 test/coverage-no-libs/pkga/Setup.hs+2 test/coverage-no-libs/pkga/pkga.cabal+19 test/coverage-no-libs/stack.yaml+4 test/default.nix+1
lib/cover-project.nix+7−10
@@ -2,6 +2,8 @@
22# reports
33{ stdenv, pkgs, lib, haskellLib }:
44
5+# haskell.nix project
6+project:
57# List of coverage reports to accumulate
68coverageReports:
79
@@ -43,21 +45,16 @@ let
4345 </html>
4446 '';
4547
46− ghc =
47− if (builtins.length coverageReports) > 0
48− then (builtins.head coverageReports).library.project.pkg-set.config.ghc.package or pkgs.ghc
49− else pkgs.ghc;
48+ ghc = project.pkg-set.config.ghc.package;
5049
51− libs = map (r: r.library) coverageReports;
52−
53− projectLibs = map (pkg: pkg.components.library) (lib.attrValues (haskellLib.selectProjectPackages ((lib.head libs).project.hsPkgs)));
50+ libs = lib.remove null (map (r: r.library) coverageReports);
5451
5552 mixDirs =
5653 map
5754 (l: "${l}/share/hpc/vanilla/mix/${l.identifier.name}-${l.identifier.version}")
58− (projectLibs);
55+ libs;
5956
60− srcDirs = map (l: l.src.outPath) (projectLibs);
57+ srcDirs = map (l: l.src.outPath) libs;
6158
6259in pkgs.runCommand "project-coverage-report"
6360 ({ nativeBuildInputs = [ (ghc.buildGHC or ghc) pkgs.buildPackages.zip ];
@@ -122,7 +119,7 @@ in pkgs.runCommand "project-coverage-report"
122119 fi
123120
124121 # Copy mix, tix, and html information over from each report
125− cp -Rn $report/share/hpc/vanilla/mix/$identifier/* $out/share/hpc/vanilla/mix/
122+ cp -Rn $report/share/hpc/vanilla/mix/$identifier $out/share/hpc/vanilla/mix/
126123 cp -R $report/share/hpc/vanilla/tix/* $out/share/hpc/vanilla/tix/
127124 cp -R $report/share/hpc/vanilla/html/* $out/share/hpc/vanilla/html/
128125 '') coverageReports)}
overlays/haskell.nix+1−1
@@ -560,7 +560,7 @@ final: prev: {
560560 # These are functions not packages
561561 [ "shellFor" "makeConfigFiles" "ghcWithHoogle" "ghcWithPackages" ]);
562562
563− projectCoverageReport = haskellLib.projectCoverageReport (map (pkg: pkg.coverageReport) (final.lib.attrValues (haskellLib.selectProjectPackages hsPkgs)));
563+ projectCoverageReport = haskellLib.projectCoverageReport project (map (pkg: pkg.coverageReport) (final.lib.attrValues (haskellLib.selectProjectPackages hsPkgs)));
564564
565565 projectCross = (final.lib.mapAttrs (_: pkgs:
566566 rawProject.projectFunction pkgs.haskell-nix rawProject.projectArgs
test/coverage-no-libs/cabal.projectnew file+1
@@ -0,0 +1 @@
1+packages: pkga
test/coverage-no-libs/default.nixnew file+101
@@ -0,0 +1,101 @@
1+{ stdenv, cabal-install, cabalProject', stackProject', recurseIntoAttrs, runCommand, testSrc, compiler-nix-name }:
2+
3+with stdenv.lib;
4+
5+let
6+ projectArgs = {
7+ src = testSrc "coverage-no-libs";
8+ modules = [{
9+ # Package has no exposed modules which causes
10+ # haddock: No input file(s)
11+ packages.bytestring-builder.doHaddock = false;
12+ }];
13+ };
14+
15+ # We can easily select a different compiler when using cabal,
16+ # but for stack we would need a different resolver to be used..
17+ cabalProj = (cabalProject' projectArgs // { inherit compiler-nix-name; });
18+ stackProj = (stackProject' projectArgs);
19+
20+ exeExt = stdenv.hostPlatform.extensions.executable;
21+
22+in recurseIntoAttrs ({
23+ run = stdenv.mkDerivation {
24+ name = "coverage-test";
25+
26+ buildCommand = ''
27+ ########################################################################
28+ # test coverage reports with an example project
29+
30+ fileExistsNonEmpty() {
31+ local file=$1
32+ if [ ! -f "$file" ]; then
33+ echo "Missing: $file"
34+ exit 1
35+ fi
36+ local filesize=$(command stat --format '%s' "$file")
37+ if [ $filesize -eq 0 ]; then
38+ echo "File must not be empty: $file"
39+ exit 1
40+ fi
41+ }
42+ findFileExistsNonEmpty() {
43+ local searchDir=$1
44+ local filePattern=$2
45+
46+ local file="$(find $searchDir -name $filePattern -print -quit)"
47+
48+ if [ -z $file ]; then
49+ echo "Couldn't find file \"$filePattern\" in directory \"$searchDir\"."
50+ exit 1
51+ fi
52+
53+ local filesize=$(command stat --format '%s' "$file")
54+ if [ $filesize -eq 0 ]; then
55+ echo "File must not be empty: $file"
56+ exit 1
57+ fi
58+ }
59+ dirExistsEmpty() {
60+ local dir=$1
61+ if [ ! -d "$dir" ]; then
62+ echo "Missing: $dir"
63+ exit 1
64+ fi
65+ if [ "$(ls -A $dir)" ]; then
66+ echo "Dir should be empty: $dir"
67+ exit 1
68+ fi
69+ }
70+ dirExists() {
71+ local dir=$1
72+ if [ ! -d "$dir" ]; then
73+ echo "Missing: $dir"
74+ exit 1
75+ fi
76+ }
77+
78+ ${concatStringsSep "\n" (map (project: ''
79+ pkga_basedir="${project.hsPkgs.pkga.coverageReport}/share/hpc/vanilla"
80+ dirExistsEmpty "$pkga_basedir/html/pkga-0.1.0.0"
81+ dirExistsEmpty "$pkga_basedir/mix/pkga-0.1.0.0"
82+ dirExistsEmpty "$pkga_basedir/tix/pkga-0.1.0.0"
83+
84+ project_basedir="${project.projectCoverageReport}/share/hpc/vanilla"
85+ dirExistsEmpty "$pkga_basedir/html/pkga-0.1.0.0"
86+ dirExistsEmpty "$pkga_basedir/mix/pkga-0.1.0.0"
87+ dirExistsEmpty "$pkga_basedir/tix/pkga-0.1.0.0"
88+ dirExistsEmpty "$project_basedir/tix/all"
89+ '') [ stackProj ])}
90+
91+ touch $out
92+ '';
93+
94+ meta.platforms = platforms.all;
95+
96+ passthru = {
97+ # Used for debugging with nix repl
98+ inherit cabalProj stackProj;
99+ };
100+ };
101+})
test/coverage-no-libs/pkga/MainA.hsnew file+4
@@ -0,0 +1,4 @@
1+module Main where
2+
3+main :: IO ()
4+main = putStrLn "This is MainA"
test/coverage-no-libs/pkga/Setup.hsnew file+2
@@ -0,0 +1,2 @@
1+import Distribution.Simple
2+main = defaultMain
test/coverage-no-libs/pkga/pkga.cabalnew file+19
@@ -0,0 +1,19 @@
1+cabal-version: 2.2
2+-- Initial package description 'pkga.cabal' generated by 'cabal init'. For
3+-- further documentation, see http://haskell.org/cabal/users-guide/
4+
5+name: pkga
6+version: 0.1.0.0
7+-- synopsis:
8+-- description:
9+-- bug-reports:
10+license: LicenseRef-PublicDomain
11+author: Samuel Evans-Powell
12+maintainer: samuel.evans-powell@iohk.io
13+category: Testing
14+
15+executable pkga-exe
16+ main-is: MainA.hs
17+ build-depends: base
18+ hs-source-dirs: .
19+ default-language: Haskell2010
test/coverage-no-libs/stack.yamlnew file+4
@@ -0,0 +1,4 @@
1+resolver: lts-14.13
2+
3+packages:
4+- pkga/
test/default.nix+1
@@ -193,6 +193,7 @@ let
193193 } // lib.optionalAttrs (!stdenv.hostPlatform.isGhcjs) {
194194 # These do not work on ghcjs because it needs zlib.
195195 coverage = callTest ./coverage { inherit compiler-nix-name; };
196+ coverage-no-libs = callTest ./coverage-no-libs { inherit compiler-nix-name; };
196197 snapshots = callTest ./snapshots {};
197198 } // lib.optionalAttrs (!stdenv.hostPlatform.isGhcjs && compiler-nix-name != "ghc8101" && compiler-nix-name != "ghc8102" && compiler-nix-name != "ghc810220201118" ) {
198199 # Pandoc does not build with ghcjs or ghc 8.10 yet (lookup-sha256 and fully-static build pandoc)