|
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