on iko/ghcjs-patch-master help sign in
Support githash package and cross package refs (#843)
64efc98Hamish Mackenzie
builder/comp-builder.nix+46−11
@@ -77,7 +77,27 @@ let self =
77let
77let
78 # TODO fix cabal wildcard support so hpack wildcards can be mapped to cabal wildcards
78 # TODO fix cabal wildcard support so hpack wildcards can be mapped to cabal wildcards
79 canCleanSource = !(cabal-generator == "hpack" && !(package.cleanHpack or false));
79 canCleanSource = !(cabal-generator == "hpack" && !(package.cleanHpack or false));
80+ # In order to support relative references to other packages we need to use
81+ # the `origSrc` diretory as the root `src` for the derivation.
82+ # We use `rootAndSubDir` here to split the cleaned source into a `cleanSrc.root`
83+ # path (that respects the filtering) and a `cleanSrc.subDir` that
84+ # is the sub directory in that root path that contains the package.
85+ # `cleanSrc.subDir` is used in `prePatch` and `lib/cover.nix`.
86+ cleanSrc = haskellLib.rootAndSubDir (if canCleanSource
80− cleanSrc = if canCleanSource then haskellLib.cleanCabalComponent package component src else src;
87+ then haskellLib.cleanCabalComponent package component "${componentId.ctype}-${componentId.cname}" src
88+ else
89+ # We can clean out the siblings though to at least avoid changes to other packages
90+ # from triggering a rebuild of this one.
91+ # Passing `subDir` but not `includeSiblings = true;` will exclude anything not
92+ # in the `subDir`.
93+ if src ? origSrc && src ? filter && src.origSubDir or "" != ""
94+ then haskellLib.cleanSourceWith {
95+ name = src.name or "source";
96+ src = src.origSrc;
97+ subDir = lib.removePrefix "/" src.origSubDir;
98+ inherit (src) filter;
99+ }
100+ else src);
81
101
82 nameOnly = "${package.identifier.name}-${componentId.ctype}-${componentId.cname}";
102 nameOnly = "${package.identifier.name}-${componentId.ctype}-${componentId.cname}";
83
103
@@ -189,7 +209,7 @@ let
189
209
190 # Attributes that are common to both the build and haddock derivations
210 # Attributes that are common to both the build and haddock derivations
191 commonAttrs = {
211 commonAttrs = {
192− src = cleanSrc;
212+ src = cleanSrc.root;
193
213
194 LANG = "en_US.UTF-8"; # GHC needs the locale configured during the Haddock phase.
214 LANG = "en_US.UTF-8"; # GHC needs the locale configured during the Haddock phase.
195 LC_ALL = "en_US.UTF-8";
215 LC_ALL = "en_US.UTF-8";
@@ -198,14 +218,27 @@ let
198
218
199 SETUP_HS = setup + /bin/Setup;
219 SETUP_HS = setup + /bin/Setup;
200
220
221+ prePatch =
222+ # If the package is in a sub directory `cd` there first.
223+ # In some cases the `cleanSrc.subDir` will be empty and the `.cabal`
224+ # file will be in the root of `src` (`cleanSrc.root`). This
225+ # will happen when:
226+ # * the .cabal file is in the projects `src.origSrc or src`
227+ # * the package src was overridden with a value that does not
228+ # include an `origSubDir`
229+ (lib.optionalString (cleanSrc.subDir != "") ''
230+ cd ${lib.removePrefix "/" cleanSrc.subDir}
231+ ''
232+ ) +
201− prePatch = if (cabalFile != null)
233+ (if cabalFile != null
202− then ''cat ${cabalFile} > ${package.identifier.name}.cabal''
234+ then ''cat ${cabalFile} > ${package.identifier.name}.cabal''
203− else
235+ else
204− # When building hpack package we use the internal nix-tools
236+ # When building hpack package we use the internal nix-tools
205− # (compiled with a fixed GHC version)
237+ # (compiled with a fixed GHC version)
206− lib.optionalString (cabal-generator == "hpack") ''
238+ lib.optionalString (cabal-generator == "hpack") ''
207− ${buildPackages.haskell-nix.internal-nix-tools}/bin/hpack
239+ ${buildPackages.haskell-nix.internal-nix-tools}/bin/hpack
208− '';
240+ ''
241+ );
209 }
242 }
210 # patches can (if they like) depend on the version and revision of the package.
243 # patches can (if they like) depend on the version and revision of the package.
211 // lib.optionalAttrs (patches != []) {
244 // lib.optionalAttrs (patches != []) {
@@ -244,7 +277,9 @@ let
244 passthru = {
277 passthru = {
245 inherit (package) identifier;
278 inherit (package) identifier;
246 config = component;
279 config = component;
280+ srcSubDir = cleanSrc.subDir;
281+ srcSubDirPath = cleanSrc.root + cleanSrc.subDir;
247− inherit configFiles executableToolDepends cleanSrc exeName;
282+ inherit configFiles executableToolDepends exeName;
248 exePath = drv + "/bin/${exeName}";
283 exePath = drv + "/bin/${exeName}";
249 env = shellWrappers;
284 env = shellWrappers;
250 profiled = self (drvArgs // { enableLibraryProfiling = true; });
285 profiled = self (drvArgs // { enableLibraryProfiling = true; });
builder/hspkg-builder.nix+1−1
@@ -118,7 +118,7 @@ in rec {
118 checks = pkgs.recurseIntoAttrs (builtins.mapAttrs
118 checks = pkgs.recurseIntoAttrs (builtins.mapAttrs
119 (_: d: haskellLib.check d)
119 (_: d: haskellLib.check d)
120 (lib.filterAttrs (_: d: d.config.doCheck) components.tests));
120 (lib.filterAttrs (_: d: d.config.doCheck) components.tests));
121− inherit (package) identifier detailLevel isLocal;
121+ inherit (package) identifier detailLevel isLocal isProject;
122 inherit setup cabalFile;
122 inherit setup cabalFile;
123 isHaskell = true;
123 isHaskell = true;
124 inherit src;
124 inherit src;
builder/setup-builder.nix+15−3
@@ -5,10 +5,12 @@
5, prePatch ? null, postPatch ? null
5, prePatch ? null, postPatch ? null
6, preBuild ? component.preBuild , postBuild ? component.postBuild
6, preBuild ? component.preBuild , postBuild ? component.postBuild
7, preInstall ? component.preInstall , postInstall ? component.postInstall
7, preInstall ? component.preInstall , postInstall ? component.postInstall
8−, cleanSrc ? haskellLib.cleanCabalComponent package component src
8+, cleanSrc ? haskellLib.cleanCabalComponent package component "setup" src
9}:
9}:
10
10
11let
11let
12+ cleanSrc' = haskellLib.rootAndSubDir cleanSrc;
13+
12 fullName = "${name}-setup";
14 fullName = "${name}-setup";
13
15
14 includeGhcPackage = lib.any (p: p.identifier.name == "ghc") component.depends;
16 includeGhcPackage = lib.any (p: p.identifier.name == "ghc") component.depends;
@@ -35,7 +37,7 @@ let
35 drv =
37 drv =
36 stdenv.mkDerivation ({
38 stdenv.mkDerivation ({
37 name = "${ghc.targetPrefix}${fullName}";
39 name = "${ghc.targetPrefix}${fullName}";
38− src = cleanSrc;
40+ src = cleanSrc'.root;
39 buildInputs = component.libs
41 buildInputs = component.libs
40 ++ component.frameworks
42 ++ component.frameworks
41 ++ builtins.concatLists component.pkgconfig;
43 ++ builtins.concatLists component.pkgconfig;
@@ -44,7 +46,10 @@ let
44 passthru = {
46 passthru = {
45 inherit (package) identifier;
47 inherit (package) identifier;
46 config = component;
48 config = component;
49+ srcSubDir = cleanSrc'.subDir;
50+ srcSubDirPath = cleanSrc'.root + cleanSrc'.subDir;
51+ cleanSrc = cleanSrc';
47− inherit configFiles cleanSrc;
52+ inherit configFiles;
48 };
53 };
49
54
50 meta = {
55 meta = {
@@ -83,6 +88,13 @@ let
83 runHook postInstall
88 runHook postInstall
84 '';
89 '';
85 }
90 }
91+ // (lib.optionalAttrs (cleanSrc'.subDir != "") {
92+ prePatch =
93+ # If the package is in a sub directory `cd` there first
94+ ''
95+ cd ${lib.removePrefix "/" cleanSrc'.subDir}
96+ '';
97+ })
86 // (lib.optionalAttrs (patches != []) { patches = map (p: if builtins.isFunction p then p { inherit (package.identifier) version; inherit revision; } else p) patches; })
98 // (lib.optionalAttrs (patches != []) { patches = map (p: if builtins.isFunction p then p { inherit (package.identifier) version; inherit revision; } else p) patches; })
87 // hooks
99 // hooks
88 );
100 );
changelog.md+9
@@ -1,6 +1,15 @@
1This file contains a summary of changes to Haskell.nix and `nix-tools`
1This file contains a summary of changes to Haskell.nix and `nix-tools`
2that will impact users.
2that will impact users.
3
3
4+## Jan 14, 2021
5+* Added support for cross package refs (with a project). Relative
6+ directory references between packages within a project should now
7+ work.
8+* Added `includeSiblings` to `cleanSourceWith`. When `true` it
9+ prevents the `subDir` arg from causing filtering of other directories.
10+* Added `keepGitDir` to `cleanGit` to allow `.git` directory to be kept
11+ (useful for components that use the `githash` package).
12+
4## Nov 26, 2020
13## Nov 26, 2020
5* Renamed `otherShells` arg for `shellFor` to `inputsFrom
14* Renamed `otherShells` arg for `shellFor` to `inputsFrom
6
15
lib/clean-cabal-component.nix+95−57
@@ -1,89 +1,127 @@
1# Use cleanSourceWith to filter just the files needed for a particular
1# Use cleanSourceWith to filter just the files needed for a particular
2# component of the package
2# component of the package
3−{ lib, cleanSourceWith }: package: component: src:
3+{ lib, cleanSourceWith, canCleanSource }: package: component: componentName: src:
4let
4let
5− srcStr' = src.origSrcSubDir or src.origSrc or null;
5+ srcStr' = src.origSrc or null;
6+ subDir = if src.origSubDir or "" == ""
7+ then ""
8+ else lib.removePrefix "/" src.origSubDir + "/";
9+ # Remove a directory for each .. part of a path.
10+ removeDotDots = parts: lib.reverseList (
11+ builtins.foldl' (a: b:
12+ if b == ".."
13+ then builtins.tail a
14+ else builtins.concatLists [ [b] a ]) [] parts);
6 # Transform
15 # Transform
7− # "." -> ""
16+ # "." -> ""
17+ # "./." -> ""
18+ # "./xyz" -> "xyz"
19+ # "../abc" -> ERROR
20+ # "abc/.." -> ""
21+ # "abc/../xyz" -> "xyz"
22+ # "abc/./xyz" -> "abc/xyz"
8− # "./." -> ""
23+ # "abc/./../xyz" -> "xyz"
9− # "./xyz" -> "xyz"
24+ # "abc/.././xyz" -> "xyz"
25+ # "abc/" -> "abc/"
10− normalizeRelativePath = rel:
26+ normalizeRelativePath = path:
11− if rel == "." || rel == "./."
27+ let
28+ # Split the path into component parts and remove the empty ones and single dots.
29+ nonEmptyParts = lib.filter (x: x != "" && x != ".") (lib.splitString "/" path);
30+ in lib.concatStringsSep "/" (removeDotDots nonEmptyParts)
31+ # Keep the trailing slash if there was one.
32+ + (if lib.hasSuffix "/" path then "/" else "");
33+ isAbsolutePath = path: lib.hasPrefix "/" path;
34+ isRelativePath = path: !(isAbsolutePath path);
35+ normalizePath = path:
36+ (if isAbsolutePath path
12− then ""
37+ then "/"
13− else lib.strings.removePrefix "./" rel;
38+ else ""
39+ ) + normalizeRelativePath path;
40+ combinePaths = a: b: if isAbsolutePath b
41+ then b
42+ else normalizePath (a + "/" + b);
14− # Like normalizeRelativePath but with a trailing / when needed
43+ # Like normalizePath but with a trailing / when needed
15− normalizeRelativeDir = dir:
44+ normalizeDir = dir:
16− let p = normalizeRelativePath dir;
45+ let p = normalizePath dir;
17 in if p == "" || p == "/"
46 in if p == "" || p == "/"
18 then ""
47 then ""
19 else if lib.hasSuffix "/" p
48 else if lib.hasSuffix "/" p
20 then p
49 then p
21 else p + "/";
50 else p + "/";
22in
51in
23− if srcStr' == null || package.detailLevel != "FullDetails"
52+ if srcStr' == null || package.detailLevel != "FullDetails" || !canCleanSource src
24 then src
53 then src
25 else
54 else
26 let
55 let
27 srcStr = toString srcStr';
56 srcStr = toString srcStr';
28− dataDir = normalizeRelativeDir package.dataDir;
57+ dataDir = combinePaths subDir package.dataDir;
29− hsSourceDirs = builtins.map normalizeRelativeDir component.hsSourceDirs
58+ hsSourceDirs = builtins.map (d: combinePaths subDir d) component.hsSourceDirs
30− ++ (if component.hsSourceDirs == [] then [""] else []);
59+ ++ (if component.hsSourceDirs == [] then [subDir] else []);
31− includeDirs = builtins.map normalizeRelativeDir component.includeDirs;
60+ includeDirs = builtins.map (d: combinePaths subDir d) component.includeDirs;
61+ dirsNeeded = builtins.map (d: combinePaths subDir d) (
32− dirsNeeded = [dataDir]
62+ [dataDir]
33 ++ hsSourceDirs
63 ++ hsSourceDirs
34− ++ includeDirs;
64+ ++ includeDirs
65+ ++ package.licenseFiles
66+ ++ package.extraSrcFiles
67+ ++ component.extraSrcFiles
68+ ++ package.extraDocFiles
69+ ++ builtins.map (f: dataDir + f) package.dataFiles
70+ ++ otherSourceFiles);
35 fileMatch = dir: list:
71 fileMatch = dir: list:
36 let
72 let
37− prefixes = builtins.map (f: dir + f) (
73+ prefixes = builtins.map (f: combinePaths dir f) (
38 lib.lists.remove null (lib.lists.flatten (
74 lib.lists.remove null (lib.lists.flatten (
39 builtins.map (f: builtins.match "([^*]*)[*].*" f) list)));
75 builtins.map (f: builtins.match "([^*]*)[*].*" f) list)));
40− exactMatches = builtins.map (f: dataDir + f) (
76+ exactMatches = builtins.map (f: combinePaths dir f) (
41 lib.lists.remove null (lib.lists.flatten (
77 lib.lists.remove null (lib.lists.flatten (
42 builtins.map (f: builtins.match "([^*]*)" f) list)));
78 builtins.map (f: builtins.match "([^*]*)" f) list)));
43 in rPath: lib.any (d: lib.strings.hasPrefix d rPath) prefixes
79 in rPath: lib.any (d: lib.strings.hasPrefix d rPath) prefixes
44 || lib.any (d: d == rPath) exactMatches;
80 || lib.any (d: d == rPath) exactMatches;
45 dataFileMatch = fileMatch dataDir package.dataFiles;
81 dataFileMatch = fileMatch dataDir package.dataFiles;
46− licenseMatch = fileMatch "" package.licenseFiles;
82+ licenseMatch = fileMatch subDir package.licenseFiles;
47− extraSrcMatch = fileMatch "" (
83+ extraSrcMatch = fileMatch subDir (
48 package.extraSrcFiles
84 package.extraSrcFiles
49 ++ component.extraSrcFiles);
85 ++ component.extraSrcFiles);
50− extraDocMatch = fileMatch "" package.extraDocFiles;
86+ extraDocMatch = fileMatch subDir package.extraDocFiles;
51− otherSourceFiles =
87+ otherSourceFiles = builtins.map (f: combinePaths subDir f) (
52 component.asmSources
88 component.asmSources
53 ++ component.cmmSources
89 ++ component.cmmSources
54 ++ component.cSources
90 ++ component.cSources
55 ++ component.cxxSources
91 ++ component.cxxSources
56− ++ component.jsSources;
92+ ++ component.jsSources);
57 in cleanSourceWith {
93 in cleanSourceWith {
58− inherit src;
94+ name = src.name or "source" + "-${componentName}";
95+ subDir = lib.removePrefix "/" (src.origSubDir or "");
96+ includeSiblings = true;
97+ src = cleanSourceWith {
98+ src = src.origSrc or src;
59− filter = path: type:
99+ filter = path: type:
100+ (!(src ? filter) || src.filter path type) && (
60− assert (if !lib.strings.hasPrefix (srcStr + "/") (path + "/")
101+ assert (if !lib.strings.hasPrefix (srcStr + "/") (path + "/")
61− then throw ("Unexpected path " + path + " (expected something in " + srcStr + "/)")
102+ then throw ("Unexpected path " + path + " (expected something in " + srcStr + "/)")
62− else true);
103+ else true);
63− let
104+ let
64− srcStrLen = lib.strings.stringLength srcStr;
105+ srcStrLen = lib.strings.stringLength srcStr;
65− rPath = lib.strings.substring (srcStrLen + 1) (lib.strings.stringLength path - srcStrLen - 1) path;
106+ rPath = lib.strings.substring (srcStrLen + 1) (lib.strings.stringLength path - srcStrLen - 1) path;
66− # This is a handy way to find out why different files are included
107+ # This is a handy way to find out why different files are included
67− # traceReason = reason: v: if v then builtins.trace (rPath + " : " + reason) true else false;
108+ # traceReason = reason: v: if v then builtins.trace (rPath + " : " + reason) true else false;
68− traceReason = reason: v: v;
109+ traceReason = reason: v: v;
69− in
110+ in
70− traceReason "directory is needed" (
111+ traceReason "directory is needed" (
71− lib.any (d: lib.strings.hasPrefix (rPath + "/") d) (
112+ lib.any (d: lib.strings.hasPrefix (rPath + "/") d) dirsNeeded)
72− dirsNeeded
73− ++ package.licenseFiles
74− ++ package.extraSrcFiles
75− ++ component.extraSrcFiles
76− ++ package.extraDocFiles
77− ++ builtins.map (f: dataDir + f) package.dataFiles
78− ++ otherSourceFiles))
79− || traceReason "cabal package definition" (lib.strings.hasSuffix ".cabal" rPath)
113+ || traceReason "cabal package definition" (lib.strings.hasPrefix subDir rPath
114+ && lib.strings.hasSuffix ".cabal" rPath)
80− || traceReason "hpack package defintion" (rPath == "package.yaml")
115+ || traceReason "hpack package defintion" (lib.strings.hasPrefix subDir rPath
116+ && rPath == "package.yaml")
81− || traceReason "data file" (lib.strings.hasPrefix dataDir rPath
117+ || traceReason "data file" (lib.strings.hasPrefix dataDir rPath
82− && dataFileMatch rPath)
118+ && dataFileMatch rPath)
83− || traceReason "haskell source dir" (lib.any (d: lib.strings.hasPrefix d rPath) hsSourceDirs)
119+ || traceReason "haskell source dir" (lib.any (d: lib.strings.hasPrefix d rPath) hsSourceDirs)
84− || traceReason "include dir" (lib.any (d: lib.strings.hasPrefix d rPath) includeDirs)
120+ || traceReason "include dir" (lib.any (d: lib.strings.hasPrefix d rPath) includeDirs)
85− || traceReason "license file" (licenseMatch rPath)
121+ || traceReason "license file" (licenseMatch rPath)
86− || traceReason "extra source file" (extraSrcMatch rPath)
122+ || traceReason "extra source file" (extraSrcMatch rPath)
87− || traceReason "extra doc file" (extraDocMatch rPath)
123+ || traceReason "extra doc file" (extraDocMatch rPath)
88− || traceReason "other source file" (lib.any (f: f == rPath) otherSourceFiles);
124+ || traceReason "other source file" (lib.any (f: f == rPath) otherSourceFiles)
125+ );
126+ };
89 }
127 }
lib/clean-git.nix+28−26
@@ -1,6 +1,6 @@
1# From https://github.com/NixOS/nix/issues/2944
1# From https://github.com/NixOS/nix/issues/2944
2{ lib, runCommand, git, cleanSourceWith }:
2{ lib, runCommand, git, cleanSourceWith }:
3−{ name ? null, src, subDir ? "" }:
3+{ name ? null, src, subDir ? "", includeSiblings ? false, keepGitDir ? false }:
4
4
5# The function call
5# The function call
6#
6#
@@ -24,26 +24,6 @@ with builtins;
24# is shared among multiple invocations of gitSource:
24# is shared among multiple invocations of gitSource:
25
25
26let
26let
27− filter_from_list = root: files:
28− let
29− all_paren_dirs = p:
30− if p == "." || p == "/"
31− then []
32− else [ p ] ++ all_paren_dirs (dirOf p);
33−
34− whitelist_set = listToAttrs (
35− concatMap (p:
36− # Using `origSrcSubDir` (if present) makes it possible to cleanGit src that
37− # has already been cleaned with cleanSrcWith.
38− let full_path = root.origSrcSubDir or (toString root) + "/${p}"; in
39− map (p': { name = p'; value = true; }) (all_paren_dirs full_path)
40− ) files
41− );
42− in
43− p: t: hasAttr (toString p) whitelist_set;
44−
45− has_prefix = prefix: s:
46− prefix == builtins.substring 0 (builtins.stringLength prefix) s;
47 remove_prefix = prefix: s:
27 remove_prefix = prefix: s:
48 builtins.substring
28 builtins.substring
49 (builtins.stringLength prefix)
29 (builtins.stringLength prefix)
@@ -84,7 +64,7 @@ then
84 git_content = lines (readFile (origSrcSubDir + "/.git"));
64 git_content = lines (readFile (origSrcSubDir + "/.git"));
85 first_line = head git_content;
65 first_line = head git_content;
86 prefix = "gitdir: ";
66 prefix = "gitdir: ";
87− ok = length git_content == 1 && has_prefix prefix first_line;
67+ ok = length git_content == 1 && lib.hasPrefix prefix first_line;
88 in
68 in
89 if ok
69 if ok
90 then /. + remove_prefix prefix first_line
70 then /. + remove_prefix prefix first_line
@@ -148,17 +128,39 @@ then
148
128
149 whitelist = lines (readFile (whitelist_file.out));
129 whitelist = lines (readFile (whitelist_file.out));
150
130
131+ all_paren_dirs = p:
132+ if p == "." || p == "/"
133+ then []
134+ else [ p ] ++ all_paren_dirs (dirOf p);
135+
136+ # All the paths that we need to keep as a set (including parent dirs)
137+ whitelist_set = listToAttrs (
138+ concatMap (p:
139+ # Using `origSrcSubDir` (if present) makes it possible to cleanGit src that
140+ # has already been cleaned with cleanSrcWith.
141+ let full_path = src.origSrcSubDir or (toString src) + "/${p}"; in
142+ map (p': { name = p'; value = true; }) (all_paren_dirs full_path)
151− filter = filter_from_list src whitelist;
143+ ) whitelist
144+ );
145+
146+ # Identify files in the `.git` dir
147+ isGitDirPath = path:
148+ path == origSrcSubDir + "/.git"
149+ || lib.hasPrefix (origSrcSubDir + "/.git/") path;
150+
151+ filter = path: type:
152+ hasAttr (toString path) whitelist_set
153+ || (keepGitDir && isGitDirPath path);
152 in
154 in
153 cleanSourceWith {
155 cleanSourceWith {
154 caller = "cleanGit";
156 caller = "cleanGit";
155− inherit name src subDir filter;
157+ inherit name src subDir includeSiblings filter;
156 }
158 }
157
159
158else
160else
159− trace "gitSource.nix: ${origSrcSubDir} does not seem to be a git repository,\nassuming it is a clean checkout." (
161+ trace "haskell-nix.haskellLib.cleanGit: ${origSrcSubDir} does not seem to be a git repository,\nassuming it is a clean checkout." (
160 cleanSourceWith {
162 cleanSourceWith {
161 caller = "cleanGit";
163 caller = "cleanGit";
162− inherit name src subDir;
164+ inherit name src subDir includeSiblings;
163 }
165 }
164 )
166 )
lib/cover-project.nix+1−1
@@ -54,7 +54,7 @@ let
54 (l: "${l}/share/hpc/vanilla/mix/${l.identifier.name}-${l.identifier.version}")
54 (l: "${l}/share/hpc/vanilla/mix/${l.identifier.name}-${l.identifier.version}")
55 libs;
55 libs;
56
56
57− srcDirs = map (l: l.src.outPath) libs;
57+ srcDirs = map (l: l.srcSubDirPath) libs;
58
58
59in pkgs.runCommand "project-coverage-report"
59in pkgs.runCommand "project-coverage-report"
60 ({ nativeBuildInputs = [ (ghc.buildGHC or ghc) pkgs.buildPackages.zip ];
60 ({ nativeBuildInputs = [ (ghc.buildGHC or ghc) pkgs.buildPackages.zip ];
lib/cover.nix+1−1
@@ -21,7 +21,7 @@ let
21 mixDir = l: "${l}/share/hpc/vanilla/mix/${l.identifier.name}-${l.identifier.version}";
21 mixDir = l: "${l}/share/hpc/vanilla/mix/${l.identifier.name}-${l.identifier.version}";
22 mixDirs = map mixDir mixLibraries;
22 mixDirs = map mixDir mixLibraries;
23
23
24− srcDirs = map (l: l.src.outPath) mixLibraries;
24+ srcDirs = map (l: l.srcSubDirPath) mixLibraries;
25
25
26in pkgs.runCommand (name + "-coverage-report")
26in pkgs.runCommand (name + "-coverage-report")
27 ({nativeBuildInputs = [ (ghc.buildGHC or ghc) pkgs.buildPackages.zip ];
27 ({nativeBuildInputs = [ (ghc.buildGHC or ghc) pkgs.buildPackages.zip ];
lib/default.nix+50−2
@@ -94,7 +94,7 @@ in {
94
94
95 # if it's a project package it has a src attribute set with an origSubDir attribute.
95 # if it's a project package it has a src attribute set with an origSubDir attribute.
96 # project packages are a subset of localPackages
96 # project packages are a subset of localPackages
97− isProjectPackage = p: p ? src && p.src ? origSubDir;
97+ isProjectPackage = p: p.isProject or false;
98 selectProjectPackages = ps: lib.filterAttrs (n: p: p != null && isLocalPackage p && isProjectPackage p) ps;
98 selectProjectPackages = ps: lib.filterAttrs (n: p: p != null && isLocalPackage p && isProjectPackage p) ps;
99
99
100 # Format a componentId as it should appear as a target on the
100 # Format a componentId as it should appear as a target on the
@@ -193,7 +193,7 @@ in {
193
193
194 # Use cleanSourceWith to filter just the files needed for a particular
194 # Use cleanSourceWith to filter just the files needed for a particular
195 # component of a package
195 # component of a package
196− cleanCabalComponent = import ./clean-cabal-component.nix { inherit lib cleanSourceWith; };
196+ cleanCabalComponent = import ./clean-cabal-component.nix { inherit lib cleanSourceWith canCleanSource; };
197
197
198 # Clean git directory based on `git ls-files --recurse-submodules`
198 # Clean git directory based on `git ls-files --recurse-submodules`
199 cleanGit = import ./clean-git.nix {
199 cleanGit = import ./clean-git.nix {
@@ -273,4 +273,52 @@ in {
273 inherit (import ./cabal-project-parser.nix {
273 inherit (import ./cabal-project-parser.nix {
274 inherit pkgs;
274 inherit pkgs;
275 }) parseIndexState parseBlock;
275 }) parseIndexState parseBlock;
276+
277+ # This function is like
278+ # `src + (if subDir == "" then "" else "/" + subDir)`
279+ # however when `includeSiblings` is set it maintains
280+ # `src.origSrc` if there is one and instead adds to
281+ # `src.origSubDir`. It uses `cleanSourceWith` when possible
282+ # to keep `cleanSourceWith` support in the result.
283+ appendSubDir = { src, subDir, includeSiblings ? false }:
284+ if subDir == ""
285+ then src
286+ else
287+ if haskellLib.canCleanSource src
288+ then haskellLib.cleanSourceWith {
289+ inherit src subDir includeSiblings;
290+ }
291+ else let name = src.name or "source" + "-" + __replaceStrings ["/"] ["-"] subDir;
292+ in if includeSiblings
293+ then rec {
294+ # Keep `src.origSrc` so it can be used to allow references
295+ # to other parts of that root.
296+ inherit name;
297+ origSrc = src.origSrc or src;
298+ origSubDir = src.origSubDir or "" + "/" + subDir;
299+ outPath = origSrc + origSubDir;
300+ }
301+ else {
302+ # We are not going to need other parts of `origSrc` if there
303+ # was one and we can ignore it
304+ inherit name;
305+ outPath = src + "/" + subDir;
306+ };
307+
308+ # Givin a `src` split it into a `root` path (based on `src.origSrc` if
309+ # present) and `subDir` (based on `src.origSubDir). The
310+ # `root` will still use the `filter` of `src` if there was one.
311+ rootAndSubDir = src: rec {
312+ subDir = src.origSubDir or "";
313+ root =
314+ # Use `cleanSourceWith` to make sure the `filter` is still used
315+ if src ? origSrc && src ? filter && subDir != ""
316+ then haskellLib.cleanSourceWith {
317+ name = src.name or "source" + "-root";
318+ src = src.origSrc;
319+ # Not passing src.origSubDir so that the result points `origSrc`
320+ inherit (src) filter;
321+ }
322+ else src.origSrc or src;
323+ };
276}
324}
lib/import-and-filter-project.nix+10−3
@@ -37,11 +37,18 @@ in project // {
37 then
37 then
38 pkgs.lib.lists.elemAt sourceRepos (
38 pkgs.lib.lists.elemAt sourceRepos (
39 pkgs.lib.strings.toInt (pkgs.lib.strings.removePrefix srcRepoPrefix subDir))
39 pkgs.lib.strings.toInt (pkgs.lib.strings.removePrefix srcRepoPrefix subDir))
40− else if haskellLib.canCleanSource srcRoot
40+ else haskellLib.appendSubDir {
41+ src = srcRoot;
41− then haskellLib.cleanSourceWith { src = srcRoot; inherit subDir; }
42+ inherit subDir;
42− else srcRoot + (if subDir == "" then "" else "/" + subDir);
43+ includeSiblings = true; # Filtering sibling dirs of the package dir is done in the
44+ # component builder so that relative paths can be used to
45+ # reference project directories not in the package subDir.
46+ };
43 in oldPkg // {
47 in oldPkg // {
44 src = (pkgs.lib).mkDefault packageSrc;
48 src = (pkgs.lib).mkDefault packageSrc;
49+ package = oldPkg.package // {
50+ isProject = (pkgs.lib).mkDefault true;
51+ };
45 }) old;
52 }) old;
46 };
53 };
47 }
54 }
modules/package.nix+5
@@ -132,6 +132,11 @@ in {
132 default = false;
132 default = false;
133 };
133 };
134
134
135+ isProject = mkOption {
136+ type = bool;
137+ default = false;
138+ };
139+
135 ghcOptions = mkOption {
140 ghcOptions = mkOption {
136 type = nullOr str;
141 type = nullOr str;
137 default = null;
142 default = null;
overlays/nix-prefetch-git-minimal.nix+15
@@ -1,4 +1,19 @@
1final: prev: {
1final: prev: {
2+ # gitMinimal still ships with perl (breaks for windows cross compilation)
3+ gitReallyMinimal = (
4+ final.git.override {
5+ perlSupport = false;
6+ pythonSupport = false;
7+ withManual = false;
8+ withpcre2 = false;
9+ }
10+ ).overrideAttrs (
11+ _: {
12+ # installCheck is broken when perl is disabled
13+ doInstallCheck = false;
14+ }
15+ );
16+
2 # This can reduce closure size of nix-tools:
17 # This can reduce closure size of nix-tools:
3 # * Eliminates dependency on python3 (70MB)
18 # * Eliminates dependency on python3 (70MB)
4 # * Allows sharing with `fetchgit` as it also uses `gitMinimal` (50MB)
19 # * Allows sharing with `fetchgit` as it also uses `gitMinimal` (50MB)
test/default.nix+9
@@ -136,6 +136,12 @@ let
136
136
137 testSrcRoot = haskell-nix.haskellLib.cleanGit { src = ../.; subDir = "test"; };
137 testSrcRoot = haskell-nix.haskellLib.cleanGit { src = ../.; subDir = "test"; };
138 testSrc = subDir: haskell-nix.haskellLib.cleanSourceWith { src = testSrcRoot; inherit subDir; };
138 testSrc = subDir: haskell-nix.haskellLib.cleanSourceWith { src = testSrcRoot; inherit subDir; };
139+ # Use the following reproduce issues that may arise on hydra as a
140+ # result of building a snapshot not a git repo.
141+ # testSrcRoot = pkgs.copyPathToStore ./.;
142+ # testSrc = subDir: testSrcRoot + "/${subDir}";
143+ testSrcRootWithGitDir = haskell-nix.haskellLib.cleanGit { src = ../.; subDir = "test"; includeSiblings = true; keepGitDir = true; };
144+ testSrcWithGitDir = subDir: haskell-nix.haskellLib.cleanSourceWith { src = testSrcRootWithGitDir; inherit subDir; includeSiblings = true; };
139 callTest = x: args: haskell-nix.callPackage x (args // { inherit testSrc; });
145 callTest = x: args: haskell-nix.callPackage x (args // { inherit testSrc; });
140
146
141 # Run unit tests with: nix-instantiate --eval --strict -A unit.tests
147 # Run unit tests with: nix-instantiate --eval --strict -A unit.tests
@@ -203,6 +209,9 @@ let
203 } // lib.optionalAttrs (!pkgs.haskell-nix.haskellLib.isCrossHost) {
209 } // lib.optionalAttrs (!pkgs.haskell-nix.haskellLib.isCrossHost) {
204 # Haddock is not included with cross compilers currently
210 # Haddock is not included with cross compilers currently
205 sublib-docs = callTest ./sublib-docs { inherit util compiler-nix-name; };
211 sublib-docs = callTest ./sublib-docs { inherit util compiler-nix-name; };
212+ # githash runs git from TH code and this needs a cross compiled git exe
213+ # to work correctly. Cross compiling git is currently brocken.
214+ githash = haskell-nix.callPackage ./githash { inherit compiler-nix-name; testSrc = testSrcWithGitDir; };
206 };
215 };
207
216
208 # This is the same as allTests, but filter out all the key/vaules from the
217 # This is the same as allTests, but filter out all the key/vaules from the
test/githash/default.nixnew file+67
@@ -0,0 +1,67 @@
1+{ stdenv, haskell-nix, recurseIntoAttrs, testSrc, compiler-nix-name, runCommand, gitMinimal, buildPackages }:
2+
3+with stdenv.lib;
4+
5+let
6+ src = testSrc "githash";
7+ git =
8+ # Using the cross compiled version here, but currently git does not
9+ # seem to cross compile (so this test is disabled for cross compilation in
10+ # the test/default.nix file).
11+ # Using buildPackages here is not right, but at least gets musl64 test to pass.
12+ if stdenv.hostPlatform != stdenv.buildPlatform
13+ then buildPackages.buildPackages.gitReallyMinimal
14+ else gitMinimal;
15+ project = haskell-nix.cabalProject' {
16+ inherit src;
17+ # When haskell.nix has come from the store (e.g. on hydra) we need to provide
18+ # a suitable mock of the cleaned source with a .git dir.
19+ modules = (optional (!(src ? origSrc && __pathExists (src.origSrc + "/.git"))) {
20+ packages.githash-test.src =
21+ rec {
22+ origSrc = runCommand "githash-test-src" { nativeBuildInputs = [ git ]; } ''
23+ mkdir -p $out/test/githash
24+ cd $out
25+ git init
26+ cp -r ${src}/* test/githash
27+ git add test/githash
28+ git -c "user.name=unknown" -c "user.email=unknown" commit -m 'Initial Commit'
29+ '';
30+ origSubDir = "/test/githash";
31+ origSrcSubDir = origSrc + origSubDir;
32+ outPath = origSrcSubDir;
33+ };
34+ }) ++ [{
35+ packages.githash-test.components.exes.githash-test.build-tools = mkForce [ git ];
36+ }];
37+ inherit compiler-nix-name;
38+ };
39+
40+ packages = project.hsPkgs;
41+ githash-test =
42+ packages.githash-test.components.exes.githash-test;
43+
44+in recurseIntoAttrs {
45+ ifdInputs = {
46+ inherit (project) plan-nix;
47+ };
48+
49+ run = stdenv.mkDerivation {
50+ name = "run-githash-test";
51+
52+ buildCommand = ''
53+ exe="${githash-test}/bin/githash-test${stdenv.hostPlatform.extensions.executable}"
54+ echo Checking that the error message is generated and that it came from the right place:
55+ (${toString githash-test.config.testWrapper} $exe || true) 2>&1 \
56+ | grep "error, called at src/Main.hs:5:13 in main:Main"
57+ touch $out
58+ '';
59+
60+ meta.platforms = platforms.all;
61+
62+ passthru = {
63+ # Used for debugging with nix repl
64+ inherit project packages;
65+ };
66+ };
67+}
test/githash/githash-test.cabalnew file+16
@@ -0,0 +1,16 @@
1+cabal-version: 2.4
2+name: githash-test
3+version: 0.1.0.0
4+license: MIT
5+author: Hamish Mackenzie
6+build-type: Simple
7+extra-source-files: ../../.git/**/*
8+
9+executable githash-test
10+ hs-source-dirs: src
11+ main-is: Main.hs
12+ build-depends: base >=4.12 && <5,
13+ githash
14+ default-language: Haskell2010
15+ build-tools: git
16+
test/githash/src/Main.hsnew file+15
@@ -0,0 +1,15 @@
1+{-# LANGUAGE TemplateHaskell #-}
2+import GitHash
3+
4+panic :: String -> a
5+panic msg = error panicMsg
6+ where panicMsg =
7+ concat [ "[panic ", giBranch gi, "@", giHash gi
8+ , " (", giCommitDate gi, ")"
9+ , " (", show (giCommitCount gi), " commits in HEAD)"
10+ , dirty, "] ", msg ]
11+ dirty | giDirty gi = " (uncommitted files present)"
12+ | otherwise = ""
13+ gi = $$tGitInfoCwd
14+
15+main = panic "oh no!"