1{
2 lib,
3 stdenv,
4 runCommand,
5 newScope,
6 fetchFromGitLab,
7 makeWrapper,
8 symlinkJoin,
9 callPackage,
10 callPackages,
11
12 adwaita-icon-theme,
13 dconf,
14 gtk3,
15 wxGTK32,
16 librsvg,
17 cups,
18 gsettings-desktop-schemas,
19 hicolor-icon-theme,
20
21 unzip,
22 jq,
23
24 pname ? "kicad",
25 stable ? true,
26 testing ? false,
27 withNgspice ? !stdenv.hostPlatform.isDarwin,
28 libngspice,
29 withScripting ? true,
30 python3,
31 addons ? [ ],
32 debug ? false,
33 sanitizeAddress ? false,
34 sanitizeThreads ? false,
35 with3d ? true,
36 withI18n ? true,
37 srcs ? { },
38}:
39
40# `addons`: https://dev-docs.kicad.org/en/addons/
41#
42# ```nix
43# kicad = pkgs.kicad.override {
44# addons = with pkgs.kicadAddons; [ kikit kikit-library ];
45# };
46# ```
47
48# The `srcs` parameter can be used to override the kicad source code
49# and all libraries, which are otherwise inaccessible
50# to overlays since most of the kicad build expression has been
51# refactored into base.nix, most of the library build expressions have
52# been refactored into libraries.nix. Overrides are only applied when
53# building `kicad-unstable`. The `srcs` parameter has
54# no effect for stable `kicad`. `srcs` takes an attribute set in which
55# any of the following attributes are meaningful (though none are
56# mandatory): "kicad", "kicadVersion", "symbols", "templates",
57# "footprints", "packages3d", and "libVersion". "kicadVersion" and
58# "libVersion" should be set to a string with the desired value for
59# the version attribute in kicad's `mkDerivation` and the version
60# attribute in any of the library's `mkDerivation`, respectively.
61# "kicad", "symbols", "templates", "footprints", and "packages3d"
62# should be set to an appropriate fetcher (e.g. `fetchFromGitLab`).
63# So, for example, a possible overlay for kicad is:
64#
65# final: prev:
66
67# {
68# kicad-unstable = (prev.kicad-unstable.override {
69# srcs = {
70# kicadVersion = "2020-10-08";
71# kicad = prev.fetchFromGitLab {
72# group = "kicad";
73# owner = "code";
74# repo = "kicad";
75# rev = "fd22fe8e374ce71d57e9f683ba996651aa69fa4e";
76# sha256 = "sha256-F8qugru/jU3DgZSpQXQhRGNFSk0ybFRkpyWb7HAGBdc=";
77# };
78# };
79# });
80# }
81
82let
83 baseName =
84 if (testing) then
85 "kicad-testing"
86 else if (stable) then
87 "kicad"
88 else
89 "kicad-unstable";
90 versionsImport = import ./versions.nix;
91
92 # versions.nix does not provide us with version, src and rev. We
93 # need to turn this into appropriate fetcher calls.
94 kicadSrcFetch = fetchFromGitLab {
95 group = "kicad";
96 owner = "code";
97 repo = "kicad";
98 rev = versionsImport.${baseName}.kicadVersion.src.rev;
99 sha256 = versionsImport.${baseName}.kicadVersion.src.sha256;
100 };
101
102 libSrcFetch =
103 name:
104 fetchFromGitLab {
105 group = "kicad";
106 owner = "libraries";
107 repo = "kicad-${name}";
108 rev = versionsImport.${baseName}.libVersion.libSources.${name}.rev;
109 sha256 = versionsImport.${baseName}.libVersion.libSources.${name}.sha256;
110 };
111
112 # only override `src` or `version` if building `kicad-unstable` with
113 # the appropriate attribute defined in `srcs`.
114 srcOverridep = attr: (!stable && builtins.hasAttr attr srcs);
115
116 # use default source and version (as defined in versions.nix) by
117 # default, or use the appropriate attribute from `srcs` if building
118 # unstable with `srcs` properly defined.
119 kicadSrc = if srcOverridep "kicad" then srcs.kicad else kicadSrcFetch;
120 kicadVersion =
121 if srcOverridep "kicadVersion" then
122 srcs.kicadVersion
123 else
124 versionsImport.${baseName}.kicadVersion.version;
125
126 libSrc = name: if srcOverridep name then srcs.${name} else libSrcFetch name;
127 # TODO does it make sense to only have one version for all libs?
128 libVersion =
129 if srcOverridep "libVersion" then
130 srcs.libVersion
131 else
132 versionsImport.${baseName}.libVersion.version;
133
134 wxGTK = wxGTK32;
135 python = python3;
136 wxPython = python.pkgs.wxpython;
137 addonPath = "addon.zip";
138 addonsDrvs = map (pkg: pkg.override { inherit addonPath python3; }) addons;
139
140 addonsJoined =
141 runCommand "addonsJoined"
142 {
143 inherit addonsDrvs;
144 nativeBuildInputs = [
145 unzip
146 jq
147 ];
148 }
149 ''
150 mkdir $out
151
152 for pkg in $addonsDrvs; do
153 unzip $pkg/addon.zip -d unpacked
154
155 folder_name=$(jq .identifier unpacked/metadata.json --raw-output | tr . _)
156 for d in unpacked/*; do
157 if [ -d "$d" ]; then
158 dest=$out/share/kicad/scripting/$(basename $d)/$folder_name
159 mkdir -p $(dirname $dest)
160
161 mv $d $dest
162 fi
163 done
164 rm -r unpacked
165 done
166 '';
167
168 inherit (lib)
169 concatStringsSep
170 flatten
171 optionalString
172 optionals
173 ;
174in
175stdenv.mkDerivation rec {
176
177 # Common libraries, referenced during runtime, via the wrapper.
178 passthru.libraries = callPackages ./libraries.nix { inherit libSrc; };
179 passthru.callPackage = newScope { inherit addonPath python3; };
180 base = callPackage ./base.nix {
181 inherit stable testing baseName;
182 inherit kicadSrc kicadVersion;
183 inherit wxGTK python wxPython;
184 inherit withNgspice withScripting withI18n;
185 inherit debug sanitizeAddress sanitizeThreads;
186 };
187
188 inherit pname;
189 version = if (stable) then kicadVersion else builtins.substring 0 10 src.src.rev;
190
191 src = base;
192 dontUnpack = true;
193 dontConfigure = true;
194 dontBuild = true;
195 dontFixup = true;
196
197 pythonPath =
198 optionals (withScripting) [
199 wxPython
200 python.pkgs.six
201 python.pkgs.requests
202 ]
203 ++ addonsDrvs;
204
205 nativeBuildInputs = [ makeWrapper ] ++ optionals (withScripting) [ python.pkgs.wrapPython ];
206
207 # KICAD7_TEMPLATE_DIR only works with a single path (it does not handle : separated paths)
208 # but it's used to find both the templates and the symbol/footprint library tables
209 # https://gitlab.com/kicad/code/kicad/-/issues/14792
210 template_dir = symlinkJoin {
211 name = "KiCad_template_dir";
212 paths = with passthru.libraries; [
213 "${templates}/share/kicad/template"
214 "${footprints}/share/kicad/template"
215 "${symbols}/share/kicad/template"
216 ];
217 };
218 # We are emulating wrapGAppsHook3, along with other variables to the wrapper
219 makeWrapperArgs =
220 with passthru.libraries;
221 [
222 "--prefix XDG_DATA_DIRS : ${base}/share"
223 "--prefix XDG_DATA_DIRS : ${hicolor-icon-theme}/share"
224 "--prefix XDG_DATA_DIRS : ${adwaita-icon-theme}/share"
225 "--prefix XDG_DATA_DIRS : ${gtk3}/share/gsettings-schemas/${gtk3.name}"
226 "--prefix XDG_DATA_DIRS : ${gsettings-desktop-schemas}/share/gsettings-schemas/${gsettings-desktop-schemas.name}"
227 # wrapGAppsHook3 did these two as well, no idea if it matters...
228 "--prefix XDG_DATA_DIRS : ${cups}/share"
229 "--prefix GIO_EXTRA_MODULES : ${dconf}/lib/gio/modules"
230 # required to open a bug report link in firefox-wayland
231 "--set-default MOZ_DBUS_REMOTE 1"
232 "--set-default KICAD9_FOOTPRINT_DIR ${footprints}/share/kicad/footprints"
233 "--set-default KICAD9_SYMBOL_DIR ${symbols}/share/kicad/symbols"
234 "--set-default KICAD9_TEMPLATE_DIR ${template_dir}"
235 ]
236 ++ optionals (addons != [ ]) (
237 let
238 stockDataPath = symlinkJoin {
239 name = "kicad_stock_data_path";
240 paths = [
241 "${base}/share/kicad"
242 "${addonsJoined}/share/kicad"
243 ];
244 };
245 in
246 [ "--set-default NIX_KICAD9_STOCK_DATA_PATH ${stockDataPath}" ]
247 )
248 ++ optionals (with3d) [
249 "--set-default KICAD9_3DMODEL_DIR ${packages3d}/share/kicad/3dmodels"
250 ]
251 ++ optionals (withNgspice) [ "--prefix LD_LIBRARY_PATH : ${libngspice}/lib" ]
252
253 # infinisil's workaround for #39493
254 ++ [ "--set GDK_PIXBUF_MODULE_FILE ${librsvg}/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" ];
255
256 # why does $makeWrapperArgs have to be added explicitly?
257 # $out and $program_PYTHONPATH don't exist when makeWrapperArgs gets set?
258 installPhase =
259 let
260 bin = if stdenv.hostPlatform.isDarwin then "*.app/Contents/MacOS" else "bin";
261 tools = [
262 "kicad"
263 "pcbnew"
264 "eeschema"
265 "gerbview"
266 "pcb_calculator"
267 "pl_editor"
268 "bitmap2component"
269 "kicad-cli"
270 ];
271 utils = [
272 "dxf2idf"
273 "idf2vrml"
274 "idfcyl"
275 "idfrect"
276 ];
277 in
278 (concatStringsSep "\n" (flatten [
279 "runHook preInstall"
280
281 (optionalString (withScripting) "buildPythonPath \"${base} $pythonPath\" \n")
282
283 # wrap each of the directly usable tools
284 (map (
285 tool:
286 "makeWrapper ${base}/${bin}/${tool} $out/bin/${tool} $makeWrapperArgs"
287 + optionalString (withScripting) " --set PYTHONPATH \"$program_PYTHONPATH\""
288 ) tools)
289
290 # link in the CLI utils
291 (map (util: "ln -s ${base}/${bin}/${util} $out/bin/${util}") utils)
292
293 "runHook postInstall"
294 ]));
295
296 postInstall = ''
297 mkdir -p $out/share
298 ln -s ${base}/share/applications $out/share/applications
299 ln -s ${base}/share/icons $out/share/icons
300 ln -s ${base}/share/mime $out/share/mime
301 ln -s ${base}/share/metainfo $out/share/metainfo
302 '';
303
304 passthru.updateScript = {
305 command = [
306 ./update.sh
307 "${pname}"
308 ];
309 supportedFeatures = [ "commit" ];
310 };
311
312 meta = {
313 description =
314 (
315 if (stable) then
316 "Open Source Electronics Design Automation suite"
317 else if (testing) then
318 "Open Source EDA suite, latest on stable branch"
319 else
320 "Open Source EDA suite, latest on master branch"
321 )
322 + (lib.optionalString (!with3d) ", without 3D models");
323 homepage = "https://www.kicad.org/";
324 longDescription = ''
325 KiCad is an open source software suite for Electronic Design Automation.
326 The Programs handle Schematic Capture, and PCB Layout with Gerber output.
327 '';
328 license = lib.licenses.gpl3Plus;
329 maintainers = with lib.maintainers; [ evils ];
330 platforms = lib.platforms.all;
331 broken = stdenv.hostPlatform.isDarwin;
332 mainProgram = "kicad";
333 };
334}