on e94de40 help sign in
haskell.nix/lib/cabal-project-parser.nix
119 lines 4.4 kB history raw
1{ pkgs }:
2let
3 span = pred: list:
4 let n = pkgs.lib.lists.foldr (x: acc: if pred x then acc + 1 else 0) 0 list;
5 in { fst = pkgs.lib.lists.take n list; snd = pkgs.lib.lists.drop n list; };
7 # Look for a index-state: field in the cabal.project file
8 parseIndexState = rawCabalProject:
9 let
10 indexState = pkgs.lib.lists.concatLists (
11 pkgs.lib.lists.filter (l: l != null)
12 (builtins.map (l: builtins.match "^index-state: *(.*)" l)
13 (pkgs.lib.splitString "\n" rawCabalProject)));
14 in
15 pkgs.lib.lists.head (indexState ++ [ null ]);
17 # Remove the indentation of the first line from all the lines
18 unindent = blockLines: (pkgs.lib.foldl' ({matchString, out}: s:
19 let
20 m = builtins.match matchString s;
21 in
22 if m != null
23 then {
24 matchString = "(${builtins.head m})(.*)";
25 out = out ++ [(builtins.elemAt m 1)];
26 }
27 else {
28 inherit matchString out;
29 }
30 ) { matchString = "( *)(.*)"; out = []; } blockLines).out;
32 # Strip comments except `--sha256`
33 stripComments = blockLines: pkgs.lib.filter (s: s != "") (
34 pkgs.lib.foldl' (out: s:
35 let
36 m = builtins.match "(.*)--.*" s;
37 in
38 if !(pkgs.lib.hasPrefix "--sha256" s) && m != null
39 then out ++ [(builtins.head m)]
40 else out ++ [s]
41 ) [] blockLines
42 );
44 # Parse lines of a source-repository-package block
45 parseBlockLines = blockLines: (pkgs.lib.foldl' ({name, attrs}: s:
46 let
47 # Look for a new attribute name
48 pair = builtins.match "([^ :]*): *(.*)" s;
50 # Function to build the next parse state when the attribute name is known
51 nextState = name: value: {
52 inherit name;
53 # Support multi line attributes by appending the value to the named attribute
54 attrs = attrs // {
55 ${name} =
56 if attrs ? ${name}
57 then attrs.${name} + " " + value
58 else value;
59 };
60 };
61 in
62 if pair != null
63 then
64 # First line of a new attribute
65 nextState (builtins.head pair) (builtins.elemAt pair 1)
66 else
67 if name != null
68 then nextState name s # Append another line to the current attribute
69 else __trace "Expected attribute but found `${s}`" { inherit name attrs; }
70 ) { name = null; attrs = {}; } (stripComments (unindent blockLines))).attrs;
72 hashPath = path:
73 builtins.readFile (pkgs.runCommand "hash-path" { preferLocalBuild = true; }
74 "echo -n $(${pkgs.nix}/bin/nix-hash --type sha256 --base32 ${path}) > $out");
76 # Use pkgs.fetchgit if we have a sha256. Add comment like this
77 # --shar256: 003lm3pm0000hbfmii7xcdd9v20000flxf7gdl2pyxia7p014i8z
78 # otherwise use __fetchGit.
79 fetchRepo = cabalProjectFileName: lookupSha256: repo:
80 builtins.map (subdir:
81 let sha256 = repo."--sha256" or (lookupSha256 repo);
82 in (if sha256 != null
83 then pkgs.evalPackages.fetchgit {
84 url = repo.location;
85 rev = repo.tag;
86 inherit sha256;
87 }
88 else
89 let drv = builtins.fetchGit {
90 url = repo.location;
91 ref = repo.tag;
92 };
93 in __trace "WARNING: No sha256 found for source-repository-package ${repo.location} ${repo.tag} download may fail in restricted mode (hydra)"
94 (__trace "Consider adding `--sha256: ${hashPath drv}` to the ${cabalProjectFileName} file or passing in a lookupSha256 argument"
95 drv)
96 ) + (if subdir == "." then "" else "/" + subdir))
97 (if repo ? subdir
98 then pkgs.lib.filter (x: x != "") (pkgs.lib.splitString " " repo.subdir)
99 else ["."]);
101 # Parse a source-repository-package and fetch it if has `type: git`
102 parseBlock = cabalProjectFileName: lookupSha256: block:
103 let
104 x = span (pkgs.lib.strings.hasPrefix " ") (pkgs.lib.splitString "\n" block);
105 attrs = parseBlockLines x.fst;
106 in
107 if attrs."type" or "" != "git"
108 then {
109 sourceRepo = [];
110 otherText = "\nsource-repository-package\n" + block;
111 }
112 else {
113 sourceRepo = fetchRepo cabalProjectFileName lookupSha256 attrs;
114 otherText = pkgs.lib.strings.concatStringsSep "\n" x.snd;
115 };
117in {
118 inherit parseIndexState parseBlockLines parseBlock;