|
1 | 1 | # Use cleanSourceWith to filter just the files needed for a particular |
2 | 2 | # component of the package |
3 | | -{ lib, cleanSourceWith }: package: component: src: |
| 3 | +{ lib, cleanSourceWith, canCleanSource }: package: component: componentName: src: |
4 | 4 | let |
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 | 15 | # 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; |
17 | 46 | in if p == "" || p == "/" |
18 | 47 | then "" |
19 | 48 | else if lib.hasSuffix "/" p |
20 | 49 | then p |
21 | 50 | else p + "/"; |
22 | 51 | in |
23 | | - if srcStr' == null || package.detailLevel != "FullDetails" |
| 52 | + if srcStr' == null || package.detailLevel != "FullDetails" || !canCleanSource src |
24 | 53 | then src |
25 | 54 | else |
26 | 55 | let |
27 | 56 | 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] |
33 | 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 | 71 | fileMatch = dir: list: |
36 | 72 | let |
37 | | - prefixes = builtins.map (f: dir + f) ( |
| 73 | + prefixes = builtins.map (f: combinePaths dir f) ( |
38 | 74 | lib.lists.remove null (lib.lists.flatten ( |
39 | 75 | builtins.map (f: builtins.match "([^*]*)[*].*" f) list))); |
40 | | - exactMatches = builtins.map (f: dataDir + f) ( |
| 76 | + exactMatches = builtins.map (f: combinePaths dir f) ( |
41 | 77 | lib.lists.remove null (lib.lists.flatten ( |
42 | 78 | builtins.map (f: builtins.match "([^*]*)" f) list))); |
43 | 79 | in rPath: lib.any (d: lib.strings.hasPrefix d rPath) prefixes |
44 | 80 | || lib.any (d: d == rPath) exactMatches; |
45 | 81 | dataFileMatch = fileMatch dataDir package.dataFiles; |
46 | | - licenseMatch = fileMatch "" package.licenseFiles; |
47 | | - extraSrcMatch = fileMatch "" ( |
| 82 | + licenseMatch = fileMatch subDir package.licenseFiles; |
| 83 | + extraSrcMatch = fileMatch subDir ( |
48 | 84 | package.extraSrcFiles |
49 | 85 | ++ component.extraSrcFiles); |
50 | | - extraDocMatch = fileMatch "" package.extraDocFiles; |
51 | | - otherSourceFiles = |
| 86 | + extraDocMatch = fileMatch subDir package.extraDocFiles; |
| 87 | + otherSourceFiles = builtins.map (f: combinePaths subDir f) ( |
52 | 88 | component.asmSources |
53 | 89 | ++ component.cmmSources |
54 | 90 | ++ component.cSources |
55 | 91 | ++ component.cxxSources |
56 | | - ++ component.jsSources; |
| 92 | + ++ component.jsSources); |
57 | 93 | 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 | + }; |
89 | 127 | } |
0 commit comments