lol
1{
2 lib,
3 stdenv,
4 blueprint-compiler,
5 bzip2,
6 callPackage,
7 fetchFromGitHub,
8 fontconfig,
9 freetype,
10 glib,
11 glslang,
12 gtk4-layer-shell,
13 harfbuzz,
14 libGL,
15 libX11,
16 libadwaita,
17 ncurses,
18 nixosTests,
19 oniguruma,
20 pandoc,
21 pkg-config,
22 removeReferencesTo,
23 util-linux,
24 versionCheckHook,
25 wrapGAppsHook4,
26 writeShellApplication,
27 zig_0_14,
28
29 # Usually you would override `zig.hook` with this, but we do that internally
30 # since upstream recommends a non-default level
31 # https://github.com/ghostty-org/ghostty/blob/4b4d4062dfed7b37424c7210d1230242c709e990/PACKAGING.md#build-options
32 optimizeLevel ? "ReleaseFast",
33}:
34let
35 zig = zig_0_14;
36
37 # HACK:
38 # Work around a Zig bug where embedding a large enough file could crash
39 # the compiler when too many cores are used, which causes Hydra builds to
40 # reliably fail. See these links for more info:
41 #
42 # * https://github.com/ziglang/zig/issues/25297
43 # * https://github.com/ziglang/zig/issues/22867
44 # * https://github.com/ghostty-org/ghostty/discussions/8676
45 #
46 # Note that the `-j` parameter does NOT fix this. It seems like the faulty
47 # intern pool logic always depends on the full amount of available cores
48 # instead of the value of `-j`, so we have to use `taskset` to trick Zig
49 # into thinking it only has access to a limited amount of cores.
50 zigWithLimitedCores = writeShellApplication {
51 name = "zig";
52 passthru = {
53 inherit (zig) version meta;
54 };
55 runtimeInputs = [
56 zig
57 util-linux
58 ];
59 text = ''
60 maxCores=$(nproc)
61 # 32 cores seem to be the upper limit through empiric testing
62 coreLimit=$((maxCores < 32 ? maxCores : 32))
63 # Also take NIX_BUILD_CORES into account so the build respects the `--cores` argument
64 effectiveCores=$((NIX_BUILD_CORES > coreLimit ? coreLimit : NIX_BUILD_CORES))
65 taskset -c "0-$((effectiveCores - 1))" zig "$@"
66 '';
67 };
68
69 zig_hook =
70 (zig.hook.override {
71 zig = zigWithLimitedCores;
72 }).overrideAttrs
73 {
74 zig_default_flags = "-Dcpu=baseline -Doptimize=${optimizeLevel} --color off";
75 };
76in
77stdenv.mkDerivation (finalAttrs: {
78 pname = "ghostty";
79 version = "1.2.0";
80
81 outputs = [
82 "out"
83 "man"
84 "shell_integration"
85 "terminfo"
86 "vim"
87 ];
88
89 src = fetchFromGitHub {
90 owner = "ghostty-org";
91 repo = "ghostty";
92 tag = "v${finalAttrs.version}";
93 hash = "sha256-Z6lndpkEqBwgsjIeZhmVIQ5D7YdQSH/fG6NCY+YWEAo=";
94 };
95
96 deps = callPackage ./deps.nix {
97 name = "${finalAttrs.pname}-cache-${finalAttrs.version}";
98 };
99
100 strictDeps = true;
101
102 nativeBuildInputs = [
103 ncurses
104 pandoc
105 pkg-config
106 removeReferencesTo
107 zig_hook
108
109 # GTK frontend
110 glib # Required for `glib-compile-schemas`
111 wrapGAppsHook4
112 blueprint-compiler
113 ];
114
115 buildInputs = [
116 oniguruma
117
118 # GTK frontend
119 libadwaita
120 libX11
121 gtk4-layer-shell
122
123 # OpenGL renderer
124 glslang
125 libGL
126
127 # Font backend
128 bzip2
129 fontconfig
130 freetype
131 harfbuzz
132 ];
133
134 zigBuildFlags = [
135 "--system"
136 "${finalAttrs.deps}"
137 "-Dversion-string=${finalAttrs.version}"
138 ]
139 ++ lib.mapAttrsToList (name: package: "-fsys=${name} --search-prefix ${lib.getLib package}") {
140 inherit glslang;
141 };
142
143 zigCheckFlags = finalAttrs.zigBuildFlags;
144
145 doCheck = true;
146
147 /**
148 Ghostty really likes all of it's resources to be in the same directory, so link them back after we split them
149
150 - https://github.com/ghostty-org/ghostty/blob/4b4d4062dfed7b37424c7210d1230242c709e990/src/os/resourcesdir.zig#L11-L52
151 - https://github.com/ghostty-org/ghostty/blob/4b4d4062dfed7b37424c7210d1230242c709e990/src/termio/Exec.zig#L745-L750
152 - https://github.com/ghostty-org/ghostty/blob/4b4d4062dfed7b37424c7210d1230242c709e990/src/termio/Exec.zig#L818-L834
153
154 terminfo and shell integration should also be installable on remote machines
155
156 ```nix
157 { pkgs, ... }: {
158 environment.systemPackages = [ pkgs.ghostty.terminfo ];
159
160 programs.bash = {
161 interactiveShellInit = ''
162 if [[ "$TERM" == "xterm-ghostty" ]]; then
163 builtin source ${pkgs.ghostty.shell_integration}/bash/ghostty.bash
164 fi
165 '';
166 };
167 }
168 ```
169 */
170 postFixup = ''
171 ln -s $man/share/man $out/share/man
172
173 moveToOutput share/terminfo $terminfo
174 ln -s $terminfo/share/terminfo $out/share/terminfo
175
176 mv $out/share/ghostty/shell-integration $shell_integration
177 ln -s $shell_integration $out/share/ghostty/shell-integration
178
179 mv $out/share/vim/vimfiles $vim
180 rmdir $out/share/vim
181 ln -s $vim $out/share/vim-plugins
182
183
184 remove-references-to -t ${finalAttrs.deps} $out/bin/.ghostty-wrapped
185 '';
186
187 nativeInstallCheckInputs = [
188 versionCheckHook
189 ];
190
191 doInstallCheck = true;
192
193 versionCheckProgramArg = "--version";
194
195 passthru = {
196 tests = lib.optionalAttrs stdenv.hostPlatform.isLinux {
197 inherit (nixosTests) allTerminfo;
198 nixos = nixosTests.terminal-emulators.ghostty;
199 };
200 };
201
202 meta = {
203 description = "Fast, native, feature-rich terminal emulator pushing modern features";
204 longDescription = ''
205 Ghostty is a terminal emulator that differentiates itself by being
206 fast, feature-rich, and native. While there are many excellent terminal
207 emulators available, they all force you to choose between speed,
208 features, or native UIs. Ghostty provides all three.
209 '';
210 homepage = "https://ghostty.org/";
211 downloadPage = "https://ghostty.org/download";
212 changelog = "https://ghostty.org/docs/install/release-notes/${
213 builtins.replaceStrings [ "." ] [ "-" ] finalAttrs.version
214 }";
215 license = lib.licenses.mit;
216 mainProgram = "ghostty";
217 maintainers = with lib.maintainers; [
218 jcollie
219 pluiedev
220 getchoo
221 ];
222 outputsToInstall = [
223 "out"
224 ];
225 platforms = lib.platforms.linux;
226 };
227})