@@ -1,89 +1,127 @@
11# Use cleanSourceWith to filter just the files needed for a particular
22# component of the package
3−{ lib, cleanSourceWith }: package: component: src:
3+{ lib, cleanSourceWith, canCleanSource }: package: component: componentName: src:
44let
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);
615 # Transform
7− # "." -> ""
8− # "./." -> ""
9− # "./xyz" -> "xyz"
10− normalizeRelativePath = rel:
11− if rel == "." || rel == "./."
12− then ""
13− else lib.strings.removePrefix "./" rel;
14− # Like normalizeRelativePath but with a trailing / when needed
15− normalizeRelativeDir = dir:
16− let p = normalizeRelativePath dir;
16+ # "." -> ""
17+ # "./." -> ""
18+ # "./xyz" -> "xyz"
19+ # "../abc" -> ERROR
20+ # "abc/.." -> ""
21+ # "abc/../xyz" -> "xyz"
22+ # "abc/./xyz" -> "abc/xyz"
23+ # "abc/./../xyz" -> "xyz"
24+ # "abc/.././xyz" -> "xyz"
25+ # "abc/" -> "abc/"
26+ normalizeRelativePath = path:
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
37+ then "/"
38+ else ""
39+ ) + normalizeRelativePath path;
40+ combinePaths = a: b: if isAbsolutePath b
41+ then b
42+ else normalizePath (a + "/" + b);
43+ # Like normalizePath but with a trailing / when needed
44+ normalizeDir = dir:
45+ let p = normalizePath dir;
1746 in if p == "" || p == "/"
1847 then ""
1948 else if lib.hasSuffix "/" p
2049 then p
2150 else p + "/";
2251in
23− if srcStr' == null || package.detailLevel != "FullDetails"
52+ if srcStr' == null || package.detailLevel != "FullDetails" || !canCleanSource src
2453 then src
2554 else
2655 let
2756 srcStr = toString srcStr';
28− dataDir = normalizeRelativeDir package.dataDir;
29− hsSourceDirs = builtins.map normalizeRelativeDir component.hsSourceDirs
30− ++ (if component.hsSourceDirs == [] then [""] else []);
31− includeDirs = builtins.map normalizeRelativeDir component.includeDirs;
32− dirsNeeded = [dataDir]
57+ dataDir = combinePaths subDir package.dataDir;
58+ hsSourceDirs = builtins.map (d: combinePaths subDir d) component.hsSourceDirs
59+ ++ (if component.hsSourceDirs == [] then [subDir] else []);
60+ includeDirs = builtins.map (d: combinePaths subDir d) component.includeDirs;
61+ dirsNeeded = builtins.map (d: combinePaths subDir d) (
62+ [dataDir]
3363 ++ 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);
3571 fileMatch = dir: list:
3672 let
37− prefixes = builtins.map (f: dir + f) (
73+ prefixes = builtins.map (f: combinePaths dir f) (
3874 lib.lists.remove null (lib.lists.flatten (
3975 builtins.map (f: builtins.match "([^*]*)[*].*" f) list)));
40− exactMatches = builtins.map (f: dataDir + f) (
76+ exactMatches = builtins.map (f: combinePaths dir f) (
4177 lib.lists.remove null (lib.lists.flatten (
4278 builtins.map (f: builtins.match "([^*]*)" f) list)));
4379 in rPath: lib.any (d: lib.strings.hasPrefix d rPath) prefixes
4480 || lib.any (d: d == rPath) exactMatches;
4581 dataFileMatch = fileMatch dataDir package.dataFiles;
46− licenseMatch = fileMatch "" package.licenseFiles;
47− extraSrcMatch = fileMatch "" (
82+ licenseMatch = fileMatch subDir package.licenseFiles;
83+ extraSrcMatch = fileMatch subDir (
4884 package.extraSrcFiles
4985 ++ component.extraSrcFiles);
50− extraDocMatch = fileMatch "" package.extraDocFiles;
51− otherSourceFiles =
86+ extraDocMatch = fileMatch subDir package.extraDocFiles;
87+ otherSourceFiles = builtins.map (f: combinePaths subDir f) (
5288 component.asmSources
5389 ++ component.cmmSources
5490 ++ component.cSources
5591 ++ component.cxxSources
56− ++ component.jsSources;
92+ ++ component.jsSources);
5793 in cleanSourceWith {
58− inherit src;
59− filter = path: type:
60− assert (if !lib.strings.hasPrefix (srcStr + "/") (path + "/")
61− then throw ("Unexpected path " + path + " (expected something in " + srcStr + "/)")
62− else true);
63− let
64− srcStrLen = lib.strings.stringLength srcStr;
65− 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
67− # traceReason = reason: v: if v then builtins.trace (rPath + " : " + reason) true else false;
68− traceReason = reason: v: v;
69− in
70− traceReason "directory is needed" (
71− lib.any (d: lib.strings.hasPrefix (rPath + "/") d) (
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)
80− || traceReason "hpack package defintion" (rPath == "package.yaml")
81− || traceReason "data file" (lib.strings.hasPrefix dataDir rPath
82− && dataFileMatch rPath)
83− || 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)
85− || traceReason "license file" (licenseMatch rPath)
86− || traceReason "extra source file" (extraSrcMatch rPath)
87− || traceReason "extra doc file" (extraDocMatch rPath)
88− || traceReason "other source file" (lib.any (f: f == rPath) otherSourceFiles);
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;
99+ filter = path: type:
100+ (!(src ? filter) || src.filter path type) && (
101+ assert (if !lib.strings.hasPrefix (srcStr + "/") (path + "/")
102+ then throw ("Unexpected path " + path + " (expected something in " + srcStr + "/)")
103+ else true);
104+ let
105+ srcStrLen = lib.strings.stringLength srcStr;
106+ rPath = lib.strings.substring (srcStrLen + 1) (lib.strings.stringLength path - srcStrLen - 1) path;
107+ # This is a handy way to find out why different files are included
108+ # traceReason = reason: v: if v then builtins.trace (rPath + " : " + reason) true else false;
109+ traceReason = reason: v: v;
110+ in
111+ traceReason "directory is needed" (
112+ lib.any (d: lib.strings.hasPrefix (rPath + "/") d) dirsNeeded)
113+ || traceReason "cabal package definition" (lib.strings.hasPrefix subDir rPath
114+ && lib.strings.hasSuffix ".cabal" rPath)
115+ || traceReason "hpack package defintion" (lib.strings.hasPrefix subDir rPath
116+ && rPath == "package.yaml")
117+ || traceReason "data file" (lib.strings.hasPrefix dataDir rPath
118+ && dataFileMatch rPath)
119+ || traceReason "haskell source dir" (lib.any (d: lib.strings.hasPrefix d rPath) hsSourceDirs)
120+ || traceReason "include dir" (lib.any (d: lib.strings.hasPrefix d rPath) includeDirs)
121+ || traceReason "license file" (licenseMatch rPath)
122+ || traceReason "extra source file" (extraSrcMatch rPath)
123+ || traceReason "extra doc file" (extraDocMatch rPath)
124+ || traceReason "other source file" (lib.any (f: f == rPath) otherSourceFiles)
125+ );
126+ };
89127 }