1{
2 pname,
3 version,
4 variant,
5 src,
6 patches ? _: [ ],
7 meta,
8}:
9
10{
11 lib,
12 stdenv,
13 Xaw3d,
14 acl,
15 alsa-lib,
16 apple-sdk,
17 autoreconfHook,
18 cairo,
19 dbus,
20 emacsPackagesFor,
21 fetchpatch,
22 gettext,
23 giflib,
24 glib-networking,
25 gnutls,
26 gpm,
27 gsettings-desktop-schemas,
28 gtk3,
29 gtk3-x11,
30 harfbuzz,
31 imagemagick,
32 jansson,
33 libXaw,
34 libXcursor,
35 libXft,
36 libXi,
37 libXpm,
38 libXrandr,
39 libgccjit,
40 libjpeg,
41 libotf,
42 libpng,
43 librsvg,
44 libselinux,
45 libtiff,
46 libwebp,
47 libxml2,
48 llvmPackages_14,
49 m17n_lib,
50 mailcap,
51 mailutils,
52 makeWrapper,
53 motif,
54 ncurses,
55 nixosTests,
56 pkg-config,
57 recurseIntoAttrs,
58 sigtool,
59 sqlite,
60 replaceVars,
61 systemd,
62 tree-sitter,
63 texinfo,
64 webkitgtk_4_0,
65 wrapGAppsHook3,
66 writeText,
67 zlib,
68
69 # Boolean flags
70 withNativeCompilation ? stdenv.buildPlatform.canExecute stdenv.hostPlatform,
71 noGui ? false,
72 srcRepo ? true,
73 withAcl ? false,
74 withAlsaLib ? false,
75 withAthena ? false,
76 withCairo ? withX,
77 withCsrc ? true,
78 withDbus ? stdenv.hostPlatform.isLinux,
79 withGTK3 ? withPgtk && !noGui,
80 withGlibNetworking ? withPgtk || withGTK3 || (withX && withXwidgets),
81 withGpm ? stdenv.hostPlatform.isLinux,
82 # https://github.com/emacs-mirror/emacs/blob/master/etc/NEWS.27#L140-L142
83 withImageMagick ? false,
84 # Emacs 30+ has native JSON support
85 withJansson ? lib.versionOlder version "30",
86 withMailutils ? true,
87 withMotif ? false,
88 withNS ? stdenv.hostPlatform.isDarwin && !(variant == "macport" || noGui),
89 withPgtk ? false,
90 withSelinux ? stdenv.hostPlatform.isLinux,
91 withSQLite3 ? true,
92 withSystemd ? lib.meta.availableOn stdenv.hostPlatform systemd,
93 withToolkitScrollBars ? true,
94 withTreeSitter ? true,
95 withWebP ? true,
96 withX ? !(stdenv.hostPlatform.isDarwin || noGui || withPgtk),
97 withXinput2 ? withX,
98 withXwidgets ?
99 !stdenv.hostPlatform.isDarwin
100 && !noGui
101 && (withGTK3 || withPgtk)
102 && (lib.versionOlder version "30"), # XXX: upstream bug 66068 precludes newer versions of webkit2gtk (https://lists.gnu.org/archive/html/bug-gnu-emacs/2024-09/msg00695.html)
103 withSmallJaDic ? false,
104 withCompressInstall ? true,
105
106 # Options
107 siteStart ? ./site-start.el,
108 toolkit ? (
109 if withGTK3 then
110 "gtk3"
111 else if withMotif then
112 "motif"
113 else if withAthena then
114 "athena"
115 else
116 "lucid"
117 ),
118
119 # test
120 callPackage,
121}:
122
123assert (withGTK3 && !withNS && variant != "macport") -> withX || withPgtk;
124
125assert noGui -> !(withX || withGTK3 || withNS || variant == "macport");
126assert withAcl -> stdenv.hostPlatform.isLinux;
127assert withAlsaLib -> stdenv.hostPlatform.isLinux;
128assert withGpm -> stdenv.hostPlatform.isLinux;
129assert withImageMagick -> (withX || withNS);
130assert withNS -> stdenv.hostPlatform.isDarwin && !(withX || variant == "macport");
131assert withPgtk -> withGTK3 && !withX;
132assert withXwidgets -> !noGui && (withGTK3 || withPgtk);
133
134let
135 libGccJitLibraryPaths = [
136 "${lib.getLib libgccjit}/lib/gcc"
137 "${lib.getLib stdenv.cc.libc}/lib"
138 ]
139 ++ lib.optionals (stdenv.cc ? cc.lib.libgcc) [
140 "${lib.getLib stdenv.cc.cc.lib.libgcc}/lib"
141 ];
142
143 inherit (if variant == "macport" then llvmPackages_14.stdenv else stdenv)
144 mkDerivation
145 ;
146in
147mkDerivation (finalAttrs: {
148 pname =
149 pname
150 + (
151 if noGui then
152 "-nox"
153 else if variant == "macport" then
154 "-macport"
155 else if withPgtk then
156 "-pgtk"
157 else if withGTK3 then
158 "-gtk3"
159 else
160 ""
161 );
162 inherit version;
163
164 inherit src;
165
166 patches =
167 patches fetchpatch
168 ++ lib.optionals withNativeCompilation [
169 (replaceVars
170 (
171 if lib.versionOlder finalAttrs.version "30" then
172 ./native-comp-driver-options.patch
173 else
174 ./native-comp-driver-options-30.patch
175 )
176 {
177 backendPath = (
178 lib.concatStringsSep " " (
179 builtins.map (x: ''"-B${x}"'') (
180 [
181 # Paths necessary so the JIT compiler finds its libraries:
182 "${lib.getLib libgccjit}/lib"
183 ]
184 ++ libGccJitLibraryPaths
185 ++ [
186 # Executable paths necessary for compilation (ld, as):
187 "${lib.getBin stdenv.cc.cc}/bin"
188 "${lib.getBin stdenv.cc.bintools}/bin"
189 "${lib.getBin stdenv.cc.bintools.bintools}/bin"
190 ]
191 ++ lib.optionals stdenv.hostPlatform.isDarwin [
192 # The linker needs to know where to find libSystem on Darwin.
193 "${apple-sdk.sdkroot}/usr/lib"
194 ]
195 )
196 )
197 );
198 }
199 )
200 ];
201
202 postPatch = lib.concatStringsSep "\n" [
203 (lib.optionalString srcRepo ''
204 rm -fr .git
205 '')
206
207 # Add the name of the wrapped gvfsd
208 # This used to be carried as a patch but it often got out of sync with
209 # upstream and was hard to maintain for emacs-overlay.
210 (lib.concatStrings (
211 map
212 (fn: ''
213 sed -i 's#(${fn} "gvfs-fuse-daemon")#(${fn} "gvfs-fuse-daemon") (${fn} ".gvfsd-fuse-wrapped")#' lisp/net/tramp-gvfs.el
214 '')
215 [
216 "tramp-compat-process-running-p"
217 "tramp-process-running-p"
218 ]
219 ))
220
221 # Reduce closure size by cleaning the environment of the emacs dumper
222 ''
223 substituteInPlace src/Makefile.in \
224 --replace-warn 'RUN_TEMACS = ./temacs' 'RUN_TEMACS = env -i ./temacs'
225 ''
226
227 ''
228 substituteInPlace lisp/international/mule-cmds.el \
229 --replace-warn /usr/share/locale ${gettext}/share/locale
230
231 for makefile_in in $(find . -name Makefile.in -print); do
232 substituteInPlace $makefile_in --replace-warn /bin/pwd pwd
233 done
234 ''
235
236 ''
237 substituteInPlace lisp/net/mailcap.el \
238 --replace-fail '"/etc/mime.types"' \
239 '"/etc/mime.types" "${mailcap}/etc/mime.types"' \
240 --replace-fail '("/etc/mailcap" system)' \
241 '("/etc/mailcap" system) ("${mailcap}/etc/mailcap" system)'
242 ''
243
244 ""
245 ];
246
247 nativeBuildInputs = [
248 makeWrapper
249 pkg-config
250 ]
251 ++ lib.optionals (variant == "macport") [
252 texinfo
253 ]
254 ++ lib.optionals srcRepo [
255 autoreconfHook
256 texinfo
257 ]
258 ++ lib.optionals (withPgtk || withX && (withGTK3 || withXwidgets)) [ wrapGAppsHook3 ];
259
260 buildInputs = [
261 gettext
262 gnutls
263 (lib.getDev harfbuzz)
264 ]
265 ++ lib.optionals withJansson [
266 jansson
267 ]
268 ++ [
269 libxml2
270 ncurses
271 ]
272 ++ lib.optionals withAcl [
273 acl
274 ]
275 ++ lib.optionals withAlsaLib [
276 alsa-lib
277 ]
278 ++ lib.optionals withGpm [
279 gpm
280 ]
281 ++ lib.optionals withDbus [
282 dbus
283 ]
284 ++ lib.optionals withSelinux [
285 libselinux
286 ]
287 ++ lib.optionals (!stdenv.hostPlatform.isDarwin && withGTK3) [
288 gsettings-desktop-schemas
289 ]
290 ++ lib.optionals (stdenv.hostPlatform.isLinux && withX) [
291 libotf
292 m17n_lib
293 ]
294 ++ lib.optionals (withX && withGTK3) [
295 gtk3-x11
296 ]
297 ++ lib.optionals (withX && withMotif) [
298 motif
299 ]
300 ++ lib.optionals withGlibNetworking [
301 glib-networking
302 ]
303 ++ lib.optionals withNativeCompilation [
304 libgccjit
305 zlib
306 ]
307 ++ lib.optionals withImageMagick [
308 imagemagick
309 ]
310 ++ lib.optionals withPgtk [
311 giflib
312 gtk3
313 libXpm
314 libjpeg
315 libpng
316 librsvg
317 libtiff
318 ]
319 ++ lib.optionals withSQLite3 [
320 sqlite
321 ]
322 ++ lib.optionals withSystemd [
323 systemd
324 ]
325 ++ lib.optionals withTreeSitter [
326 tree-sitter
327 ]
328 ++ lib.optionals withWebP [
329 libwebp
330 ]
331 ++ lib.optionals withX [
332 Xaw3d
333 giflib
334 libXaw
335 libXpm
336 libXrandr
337 libjpeg
338 libpng
339 librsvg
340 libtiff
341 ]
342 ++ lib.optionals withCairo [
343 cairo
344 ]
345 ++ lib.optionals (withX && !withCairo) [
346 libXft
347 ]
348 ++ lib.optionals withXinput2 [
349 libXi
350 ]
351 ++ lib.optionals withXwidgets [
352 webkitgtk_4_0
353 ]
354 ++ lib.optionals stdenv.hostPlatform.isDarwin [
355 sigtool
356 ]
357 ++ lib.optionals withNS [
358 librsvg
359 ];
360
361 # Emacs needs to find movemail at run time, see info (emacs) Movemail
362 propagatedUserEnvPkgs = lib.optionals withMailutils [
363 mailutils
364 ];
365
366 hardeningDisable = [ "format" ];
367
368 configureFlags = [
369 (lib.enableFeature false "build-details") # for a (more) reproducible build
370 (lib.withFeature true "modules")
371 ]
372 ++ (
373 if withNS then
374 [
375 (lib.enableFeature false "ns-self-contained")
376 ]
377 else if withX then
378 [
379 (lib.withFeatureAs true "x-toolkit" toolkit)
380 (lib.withFeature withCairo "cairo")
381 (lib.withFeature (!withCairo) "xft")
382 ]
383 else if withPgtk then
384 [
385 (lib.withFeature true "pgtk")
386 ]
387 else
388 [
389 (lib.withFeature false "gif")
390 (lib.withFeature false "jpeg")
391 (lib.withFeature false "png")
392 (lib.withFeature false "tiff")
393 (lib.withFeature false "x")
394 (lib.withFeature false "xpm")
395 ]
396 )
397 ++ lib.optionals (variant == "macport") [
398 (lib.enableFeatureAs true "mac-app" "$$out/Applications")
399 (lib.withFeature true "gnutls")
400 (lib.withFeature true "mac")
401 (lib.withFeature true "xml2")
402 ]
403 ++ lib.optionals stdenv.hostPlatform.isDarwin [
404 (lib.withFeature withNS "ns")
405 ]
406 ++ [
407 (lib.withFeature withCompressInstall "compress-install")
408 (lib.withFeature withToolkitScrollBars "toolkit-scroll-bars")
409 (lib.withFeature withNativeCompilation "native-compilation")
410 (lib.withFeature withImageMagick "imagemagick")
411 (lib.withFeature withMailutils "mailutils")
412 (lib.withFeature withSmallJaDic "small-ja-dic")
413 (lib.withFeature withTreeSitter "tree-sitter")
414 (lib.withFeature withXinput2 "xinput2")
415 (lib.withFeature withXwidgets "xwidgets")
416 (lib.withFeature withDbus "dbus")
417 (lib.withFeature withSelinux "selinux")
418 ];
419
420 env =
421 lib.optionalAttrs withNativeCompilation {
422 NATIVE_FULL_AOT = "1";
423 LIBRARY_PATH = lib.concatStringsSep ":" libGccJitLibraryPaths;
424 }
425 // lib.optionalAttrs (variant == "macport") {
426 # Fixes intermittent segfaults when compiled with LLVM >= 7.0.
427 # See https://github.com/NixOS/nixpkgs/issues/127902
428 NIX_CFLAGS_COMPILE = "-include ${./macport_noescape_noop.h}";
429 };
430
431 enableParallelBuilding = true;
432
433 installTargets = [
434 "tags"
435 "install"
436 ];
437
438 postInstall = ''
439 mkdir -p $out/share/emacs/site-lisp
440 cp ${siteStart} $out/share/emacs/site-lisp/site-start.el
441
442 $out/bin/emacs --batch -f batch-byte-compile $out/share/emacs/site-lisp/site-start.el
443
444 siteVersionDir=`ls $out/share/emacs | grep -v site-lisp | head -n 1`
445
446 rm -r $out/share/emacs/$siteVersionDir/site-lisp
447 ''
448 + lib.optionalString withCsrc ''
449 for srcdir in src lisp lwlib ; do
450 dstdir=$out/share/emacs/$siteVersionDir/$srcdir
451 mkdir -p $dstdir
452 find $srcdir -name "*.[chm]" -exec cp {} $dstdir \;
453 cp $srcdir/TAGS $dstdir
454 echo '((nil . ((tags-file-name . "TAGS"))))' > $dstdir/.dir-locals.el
455 done
456 ''
457 + lib.optionalString withNS ''
458 mkdir -p $out/Applications
459 mv nextstep/Emacs.app $out/Applications
460 ''
461 + lib.optionalString (withNativeCompilation && (withNS || variant == "macport")) ''
462 ln -snf $out/lib/emacs/*/native-lisp $out/Applications/Emacs.app/Contents/native-lisp
463 ''
464 + lib.optionalString withNativeCompilation ''
465 echo "Generating native-compiled trampolines..."
466 # precompile trampolines in parallel, but avoid spawning one process per trampoline.
467 # 1000 is a rough lower bound on the number of trampolines compiled.
468 $out/bin/emacs --batch --eval "(mapatoms (lambda (s) \
469 (when (subr-primitive-p (symbol-function s)) (print s))))" \
470 | xargs -n $((1000/NIX_BUILD_CORES + 1)) -P $NIX_BUILD_CORES \
471 $out/bin/emacs --batch -l comp --eval "(while argv \
472 (comp-trampoline-compile (intern (pop argv))))"
473 mkdir -p $out/share/emacs/native-lisp
474 $out/bin/emacs --batch \
475 --eval "(add-to-list 'native-comp-eln-load-path \"$out/share/emacs/native-lisp\")" \
476 -f batch-native-compile $out/share/emacs/site-lisp/site-start.el
477 '';
478
479 postFixup = lib.optionalString (stdenv.hostPlatform.isLinux && withX && toolkit == "lucid") ''
480 patchelf --add-rpath ${lib.makeLibraryPath [ libXcursor ]} $out/bin/emacs
481 patchelf --add-needed "libXcursor.so.1" "$out/bin/emacs"
482 '';
483
484 setupHook = ./setup-hook.sh;
485
486 passthru = {
487 inherit withNativeCompilation;
488 inherit withTreeSitter;
489 inherit withXwidgets;
490 pkgs = recurseIntoAttrs (emacsPackagesFor finalAttrs.finalPackage);
491 tests = {
492 inherit (nixosTests) emacs-daemon;
493 withPackages = callPackage ./build-support/wrapper-test.nix {
494 emacs = finalAttrs.finalPackage;
495 };
496 };
497 };
498
499 meta = {
500 broken = withNativeCompilation && !(stdenv.buildPlatform.canExecute stdenv.hostPlatform);
501 knownVulnerabilities = lib.optionals (lib.versionOlder version "30") [
502 "CVE-2024-53920 CVE-2025-1244, please use newer versions such as emacs30"
503 ];
504 }
505 // meta;
506})