1# Updating? Keep $out/etc synchronized with passthru keys
2
3{ stdenv
4, lib
5, fetchFromGitHub
6, gi-docgen
7, pkg-config
8, gobject-introspection
9, gettext
10, libgudev
11, polkit
12, libxmlb
13, glib
14, gusb
15, sqlite
16, libarchive
17, libredirect
18, curl
19, libjcat
20, elfutils
21, libsmbios
22, efivar
23, valgrind
24, meson
25, libuuid
26, colord
27, ninja
28, gcab
29, gnutls
30, protobufc
31, python3
32, wrapGAppsNoGuiHook
33, ensureNewerSourcesForZipFilesHook
34, json-glib
35, bash-completion
36, shared-mime-info
37, umockdev
38, vala
39, makeFontsConf
40, freefont_ttf
41, pango
42, tpm2-tss
43, bubblewrap
44, efibootmgr
45, flashrom
46, tpm2-tools
47, fwupd-efi
48, nixosTests
49, runCommand
50, unstableGitUpdater
51, modemmanager
52, libqmi
53, libmbim
54, libcbor
55, xz
56, enableFlashrom ? false
57}:
58
59let
60 python = python3.withPackages (p: with p; [
61 pygobject3
62 setuptools
63 ]);
64
65 isx86 = stdenv.hostPlatform.isx86;
66
67 # Dell isn't supported on Aarch64
68 haveDell = isx86;
69
70 # only redfish for x86_64
71 haveRedfish = stdenv.isx86_64;
72
73 # only use msr if x86 (requires cpuid)
74 haveMSR = isx86;
75
76 # # Currently broken on Aarch64
77 # haveFlashrom = isx86;
78 # Experimental
79 haveFlashrom = isx86 && enableFlashrom;
80
81 runPythonCommand =
82 name:
83 buildCommandPython:
84
85 runCommand
86 name
87 {
88 nativeBuildInputs = [ python3 ];
89 inherit buildCommandPython;
90 }
91 ''
92 exec python3 -c "$buildCommandPython"
93 '';
94
95 test-firmware =
96 let
97 version = "unstable-2022-04-02";
98 src = fetchFromGitHub {
99 name = "fwupd-test-firmware-${version}";
100 owner = "fwupd";
101 repo = "fwupd-test-firmware";
102 rev = "39954e434d63e20e85870dd1074818f48a0c08b7";
103 hash = "sha256-d4qG3fKyxkfN91AplRYqARFz+aRr+R37BpE450bPxi0=";
104 passthru = {
105 inherit src version; # For update script
106 updateScript = unstableGitUpdater {
107 url = "${test-firmware.meta.homepage}.git";
108 };
109 };
110 };
111 in
112 src // {
113 meta = src.meta // {
114 # For update script
115 position =
116 let
117 pos = builtins.unsafeGetAttrPos "updateScript" test-firmware;
118 in
119 pos.file + ":" + toString pos.line;
120 };
121 };
122in
123stdenv.mkDerivation (finalAttrs: {
124 pname = "fwupd";
125 version = "1.8.15";
126
127 # libfwupd goes to lib
128 # daemon, plug-ins and libfwupdplugin go to out
129 # CLI programs go to out
130 outputs = [ "out" "lib" "dev" "devdoc" "man" "installedTests" ];
131
132 src = fetchFromGitHub {
133 owner = "fwupd";
134 repo = "fwupd";
135 rev = finalAttrs.version;
136 hash = "sha256-M7uCT8xJ6ym0X6iAgT3rM2ki0T6QgLJWlFU39aC64o4=";
137 };
138
139 patches = [
140 # Since /etc is the domain of NixOS, not Nix,
141 # we cannot install files there.
142 # Let’s install the files to $prefix/etc
143 # while still reading them from /etc.
144 # NixOS module for fwupd will take take care of copying the files appropriately.
145 ./add-option-for-installation-sysconfdir.patch
146
147 # Install plug-ins and libfwupdplugin to $out output,
148 # they are not really part of the library.
149 ./install-fwupdplugin-to-out.patch
150
151 # Installed tests are installed to different output
152 # we also cannot have fwupd-tests.conf in $out/etc since it would form a cycle.
153 ./installed-tests-path.patch
154
155 # EFI capsule is located in fwupd-efi now.
156 ./efi-app-path.patch
157 ];
158
159 nativeBuildInputs = [
160 # required for firmware zipping
161 ensureNewerSourcesForZipFilesHook
162 meson
163 ninja
164 gi-docgen
165 pkg-config
166 gobject-introspection
167 gettext
168 shared-mime-info
169 valgrind
170 gcab
171 gnutls
172 protobufc # for protoc
173 python
174 wrapGAppsNoGuiHook
175 vala
176 ];
177
178 buildInputs = [
179 polkit
180 libxmlb
181 gusb
182 sqlite
183 libarchive
184 curl
185 elfutils
186 libgudev
187 colord
188 libjcat
189 libuuid
190 json-glib
191 umockdev
192 bash-completion
193 pango
194 tpm2-tss
195 efivar
196 fwupd-efi
197 protobufc
198 modemmanager
199 libmbim
200 libcbor
201 libqmi
202 xz # for liblzma
203 ] ++ lib.optionals haveDell [
204 libsmbios
205 ] ++ lib.optionals haveFlashrom [
206 flashrom
207 ];
208
209 mesonFlags = [
210 "-Ddocs=enabled"
211 "-Dplugin_dummy=true"
212 # We are building the official releases.
213 "-Dsupported_build=enabled"
214 # Would dlopen libsoup to preserve compatibility with clients linking against older fwupd.
215 # https://github.com/fwupd/fwupd/commit/173d389fa59d8db152a5b9da7cc1171586639c97
216 "-Dsoup_session_compat=false"
217 "-Dudevdir=lib/udev"
218 "-Dsystemd_root_prefix=${placeholder "out"}"
219 "-Dinstalled_test_prefix=${placeholder "installedTests"}"
220 "--localstatedir=/var"
221 "--sysconfdir=/etc"
222 "-Dsysconfdir_install=${placeholder "out"}/etc"
223 "-Defi_os_dir=nixos"
224 "-Dplugin_modem_manager=enabled"
225
226 # We do not want to place the daemon into lib (cyclic reference)
227 "--libexecdir=${placeholder "out"}/libexec"
228 ] ++ lib.optionals (!haveDell) [
229 "-Dplugin_dell=disabled"
230 "-Dplugin_synaptics_mst=disabled"
231 ] ++ lib.optionals (!haveRedfish) [
232 "-Dplugin_redfish=disabled"
233 ] ++ lib.optionals (!haveFlashrom) [
234 "-Dplugin_flashrom=disabled"
235 ] ++ lib.optionals (!haveMSR) [
236 "-Dplugin_msr=disabled"
237 ];
238
239 # TODO: wrapGAppsHook wraps efi capsule even though it is not ELF
240 dontWrapGApps = true;
241
242 doCheck = true;
243
244 # Environment variables
245
246 # Fontconfig error: Cannot load default config file
247 FONTCONFIG_FILE =
248 let
249 fontsConf = makeFontsConf {
250 fontDirectories = [ freefont_ttf ];
251 };
252 in
253 fontsConf;
254
255 # error: “PolicyKit files are missing”
256 # https://github.com/NixOS/nixpkgs/pull/67625#issuecomment-525788428
257 PKG_CONFIG_POLKIT_GOBJECT_1_ACTIONDIR = "/run/current-system/sw/share/polkit-1/actions";
258
259 # Phase hooks
260
261 postPatch = ''
262 patchShebangs \
263 contrib/generate-version-script.py \
264 po/test-deps
265
266 substituteInPlace data/installed-tests/fwupdmgr-p2p.sh \
267 --replace "gdbus" ${glib.bin}/bin/gdbus
268
269 # tests fail with: Failed to load SMBIOS: neither SMBIOS or DT found
270 sed -i 's/test(.*)//' plugins/lenovo-thinklmi/meson.build
271 sed -i 's/test(.*)//' plugins/mtd/meson.build
272 # fails on amd cpu
273 sed -i 's/test(.*)//' libfwupdplugin/meson.build
274 # in nixos test tries to chmod 0777 $out/share/installed-tests/fwupd/tests/redfish.conf
275 sed -i "s/get_option('tests')/false/" plugins/redfish/meson.build
276
277 # Device tests use device emulation and need to download emulation data from
278 # the internet, which does not work on our test VMs.
279 # It's probably better to disable these tests for NixOS by setting
280 # the device-tests directory to /dev/null.
281 # For more info on device emulation, see:
282 # https://github.com/fwupd/fwupd/blob/eeeac4e9ba8a6513428b456a551bffd95d533e50/docs/device-emulation.md
283 substituteInPlace data/installed-tests/meson.build \
284 --replace "join_paths(datadir, 'fwupd', 'device-tests')" "'/dev/null'"
285 '';
286
287 preBuild = ''
288 # jcat-tool at buildtime requires a home directory
289 export HOME="$(mktemp -d)"
290 '';
291
292 preCheck = ''
293 addToSearchPath XDG_DATA_DIRS "${shared-mime-info}/share"
294
295 echo "12345678901234567890123456789012" > machine-id
296 export NIX_REDIRECTS=/etc/machine-id=$(realpath machine-id) \
297 LD_PRELOAD=${libredirect}/lib/libredirect.so
298 '';
299
300 postInstall = ''
301 # These files have weird licenses so they are shipped separately.
302 cp --recursive --dereference "${test-firmware}/installed-tests/tests" "$installedTests/libexec/installed-tests/fwupd"
303 '';
304
305 preFixup =
306 let
307 binPath = [
308 efibootmgr
309 bubblewrap
310 tpm2-tools
311 ];
312 in
313 ''
314 gappsWrapperArgs+=(
315 --prefix XDG_DATA_DIRS : "${shared-mime-info}/share"
316 # See programs reached with fu_common_find_program_in_path in source
317 --prefix PATH : "${lib.makeBinPath binPath}"
318 )
319 '';
320
321 postFixup = ''
322 # Since we had to disable wrapGAppsHook, we need to wrap the executables manually.
323 find -L "$out/bin" "$out/libexec" -type f -executable -print0 \
324 | while IFS= read -r -d ''' file; do
325 if [[ "$file" != *.efi ]]; then
326 echo "Wrapping program $file"
327 wrapGApp "$file"
328 fi
329 done
330
331 # Cannot be in postInstall, otherwise _multioutDocs hook in preFixup will move right back.
332 moveToOutput "share/doc" "$devdoc"
333 '';
334
335 separateDebugInfo = true;
336
337 passthru = {
338 filesInstalledToEtc = [
339 "fwupd/bios-settings.d/README.md"
340 "fwupd/daemon.conf"
341 "fwupd/remotes.d/lvfs-testing.conf"
342 "fwupd/remotes.d/lvfs.conf"
343 "fwupd/remotes.d/vendor.conf"
344 "fwupd/remotes.d/vendor-directory.conf"
345 "fwupd/uefi_capsule.conf"
346 "pki/fwupd/GPG-KEY-Linux-Foundation-Firmware"
347 "pki/fwupd/GPG-KEY-Linux-Vendor-Firmware-Service"
348 "pki/fwupd/LVFS-CA.pem"
349 "pki/fwupd-metadata/GPG-KEY-Linux-Foundation-Metadata"
350 "pki/fwupd-metadata/GPG-KEY-Linux-Vendor-Firmware-Service"
351 "pki/fwupd-metadata/LVFS-CA.pem"
352 "grub.d/35_fwupd"
353 ] ++ lib.optionals haveDell [
354 "fwupd/remotes.d/dell-esrt.conf"
355 ] ++ lib.optionals haveRedfish [
356 "fwupd/redfish.conf"
357 ] ++ lib.optionals haveMSR [
358 "fwupd/msr.conf"
359 ] ++ lib.optionals isx86 [
360 "fwupd/thunderbolt.conf"
361 ];
362
363 # DisabledPlugins key in fwupd/daemon.conf
364 defaultDisabledPlugins = [
365 "test"
366 "test_ble"
367 ];
368
369 # For updating.
370 inherit test-firmware;
371
372 # For downstream consumers that need the fwupd-efi this was built with.
373 inherit fwupd-efi;
374
375 tests =
376 let
377 listToPy = list: "[${lib.concatMapStringsSep ", " (f: "'${f}'") list}]";
378 in
379 {
380 installedTests = nixosTests.installed-tests.fwupd;
381
382 passthruMatches = runPythonCommand "fwupd-test-passthru-matches" ''
383 import itertools
384 import configparser
385 import os
386 import pathlib
387
388 etc = '${finalAttrs.finalPackage}/etc'
389 package_etc = set(itertools.chain.from_iterable([[os.path.relpath(os.path.join(prefix, file), etc) for file in files] for (prefix, dirs, files) in os.walk(etc)]))
390 passthru_etc = set(${listToPy finalAttrs.passthru.filesInstalledToEtc})
391 assert len(package_etc - passthru_etc) == 0, f'fwupd package contains the following paths in /etc that are not listed in passthru.filesInstalledToEtc: {package_etc - passthru_etc}'
392 assert len(passthru_etc - package_etc) == 0, f'fwupd package lists the following paths in passthru.filesInstalledToEtc that are not contained in /etc: {passthru_etc - package_etc}'
393
394 config = configparser.RawConfigParser()
395 config.read('${finalAttrs.finalPackage}/etc/fwupd/daemon.conf')
396 package_disabled_plugins = config.get('fwupd', 'DisabledPlugins').rstrip(';').split(';')
397 passthru_disabled_plugins = ${listToPy finalAttrs.passthru.defaultDisabledPlugins}
398 assert package_disabled_plugins == passthru_disabled_plugins, f'Default disabled plug-ins in the package {package_disabled_plugins} do not match those listed in passthru.defaultDisabledPlugins {passthru_disabled_plugins}'
399
400 pathlib.Path(os.getenv('out')).touch()
401 '';
402 };
403 };
404
405 meta = with lib; {
406 homepage = "https://fwupd.org/";
407 maintainers = with maintainers; [ ];
408 license = licenses.lgpl21Plus;
409 platforms = platforms.linux;
410 };
411})