1{
2 lib,
3 stdenv,
4 fetchgit,
5 fetchzip,
6 runCommand,
7 xorg,
8 nim,
9 nimOverrides,
10}:
11
12let
13 fetchers = {
14 fetchzip =
15 { url, sha256, ... }:
16 fetchzip {
17 name = "source";
18 inherit url sha256;
19 };
20 fetchgit =
21 {
22 fetchSubmodules ? false,
23 leaveDotGit ? false,
24 rev,
25 sha256,
26 url,
27 ...
28 }:
29 fetchgit {
30 inherit
31 fetchSubmodules
32 leaveDotGit
33 rev
34 sha256
35 url
36 ;
37 };
38 };
39
40 filterPropertiesToAttrs =
41 prefix: properties:
42 lib.pipe properties [
43 (builtins.filter ({ name, ... }: (lib.strings.hasPrefix prefix name)))
44 (map (
45 { name, value }:
46 {
47 name = lib.strings.removePrefix prefix name;
48 inherit value;
49 }
50 ))
51 builtins.listToAttrs
52 ];
53
54 buildNimCfg =
55 { backend, components, ... }:
56 let
57 componentSrcDirs = map (
58 { properties, ... }:
59 let
60 fodProps = filterPropertiesToAttrs "nix:fod:" properties;
61 fod = fetchers.${fodProps.method} fodProps;
62 srcDir = fodProps.srcDir or "";
63 in
64 if srcDir == "" then fod else "${fod}/${srcDir}"
65 ) components;
66 in
67 runCommand "nim.cfg"
68 {
69 outputs = [
70 "out"
71 "src"
72 ];
73 nativeBuildInputs = [ xorg.lndir ];
74 }
75 ''
76 cat << EOF >> $out
77 backend:${backend}
78 path:"$src"
79 EOF
80 mkdir -p "$src"
81 ${lib.strings.concatMapStrings (d: ''
82 lndir "${d}" "$src"
83 '') componentSrcDirs}
84 '';
85
86 buildCommands = lib.attrsets.mapAttrsToList (
87 output: input: ''
88 nim compile $nimFlags --out:${output} ${input}
89 ''
90 );
91
92 installCommands = lib.attrsets.mapAttrsToList (
93 output: input: ''
94 install -Dt $out/bin ${output}
95 ''
96 );
97
98 applySbom =
99 sbom:
100 {
101 nimFlags ? [ ],
102 nimRelease ? true,
103 passthru ? { },
104 ...
105 }@prevAttrs:
106 let
107 properties = # SBOM metadata.component.properties as an attrset.
108 lib.attrsets.recursiveUpdate (builtins.listToAttrs sbom.metadata.component.properties)
109 passthru.properties or { };
110
111 nimBin = # A mapping of Nim module file paths to names of programs.
112 lib.attrsets.recursiveUpdate (lib.pipe properties [
113 (lib.attrsets.filterAttrs (name: value: lib.strings.hasPrefix "nim:bin:" name))
114 (lib.attrsets.mapAttrs' (
115 name: value: {
116 name = lib.strings.removePrefix "nim:bin:" name;
117 value = "${properties."nim:binDir" or (properties."nim:srcDir" or ".")}/${value}";
118 }
119 ))
120 ]) passthru.nimBin or { };
121 in
122 {
123 strictDeps = true;
124
125 pname = prevAttrs.pname or sbom.metadata.component.name;
126 version = prevAttrs.version or sbom.metadata.component.version or null;
127
128 nimFlags =
129 nimFlags
130 ++ (lib.optional nimRelease "-d:release")
131 ++ (
132 let
133 srcDir = properties."nim:srcDir" or "";
134 in
135 lib.optional (srcDir != "") "--path:${srcDir}"
136 );
137
138 configurePhase =
139 prevAttrs.configurePhase or ''
140 runHook preConfigure
141 echo "nim.cfg << $nimCfg"
142 cat $nimCfg >> nim.cfg
143 cat << EOF >> nim.cfg
144 nimcache:"$NIX_BUILD_TOP/nimcache"
145 parallelBuild:$NIX_BUILD_CORES
146 EOF
147 runHook postConfigure
148 '';
149
150 buildPhase =
151 prevAttrs.buildPhase or ''
152 runHook preBuild
153 ${lib.strings.concatLines (buildCommands nimBin)}
154 runHook postBuild
155 '';
156
157 installPhase =
158 prevAttrs.installPhase or ''
159 runHook preInstall
160 ${lib.strings.concatLines (installCommands nimBin)}
161 runHook postInstall
162 '';
163
164 nativeBuildInputs = (prevAttrs.nativeBuildInputs or [ ]) ++ [ nim ];
165
166 nimCfg =
167 prevAttrs.nimCfg or (buildNimCfg {
168 backend = prevAttrs.nimBackend or properties."nim:backend" or "c";
169 inherit (sbom) components;
170 });
171
172 passthru = passthru // {
173 inherit sbom properties nimBin;
174 };
175 };
176
177 applyOverrides =
178 prevAttrs:
179 builtins.foldl' (
180 prevAttrs:
181 { name, ... }@component:
182 if (builtins.hasAttr name nimOverrides) then
183 let
184 result = nimOverrides.${name} component prevAttrs;
185 in
186 prevAttrs // (if builtins.isAttrs result then result else result { })
187 else
188 prevAttrs
189 ) prevAttrs prevAttrs.passthru.sbom.components;
190
191 compose =
192 callerArg: sbom: finalAttrs:
193 let
194 callerAttrs = if builtins.isAttrs callerArg then callerArg else callerArg finalAttrs;
195 sbomAttrs = callerAttrs // (applySbom sbom callerAttrs);
196 overrideAttrs = sbomAttrs // (applyOverrides sbomAttrs);
197 in
198 overrideAttrs;
199in
200callerArg: sbomArg:
201let
202 sbom = if builtins.isAttrs sbomArg then sbomArg else builtins.fromJSON (builtins.readFile sbomArg);
203 overrideSbom = f: stdenv.mkDerivation (compose callerArg (sbom // (f sbom)));
204in
205(stdenv.mkDerivation (compose callerArg sbom)) // { inherit overrideSbom; }