1{
2 lib,
3 crane,
4 rustToolchain,
5 rustPlatform,
6 cmake,
7 copyDesktopItems,
8 fetchFromGitHub,
9 curl,
10 clang,
11 perl,
12 pkg-config,
13 protobuf,
14 fontconfig,
15 freetype,
16 libgit2,
17 openssl,
18 sqlite,
19 zlib,
20 zstd,
21 alsa-lib,
22 libxkbcommon,
23 wayland,
24 libglvnd,
25 xorg,
26 stdenv,
27 makeFontsConf,
28 vulkan-loader,
29 envsubst,
30 cargo-about,
31 cargo-bundle,
32 git,
33 livekit-libwebrtc,
34 apple-sdk_15,
35 darwin,
36 darwinMinVersionHook,
37 makeWrapper,
38 nodejs_22,
39
40 withGLES ? false,
41}:
42
43assert withGLES -> stdenv.hostPlatform.isLinux;
44
45let
46 mkIncludeFilter =
47 root': path: type:
48 let
49 # note: under lazy-trees this introduces an extra copy
50 root = toString root' + "/";
51 relPath = lib.removePrefix root path;
52 topLevelIncludes = [
53 "crates"
54 "assets"
55 "extensions"
56 "script"
57 "tooling"
58 "Cargo.toml"
59 ".config" # nextest?
60 ];
61 firstComp = builtins.head (lib.path.subpath.components relPath);
62 in
63 builtins.elem firstComp topLevelIncludes;
64
65 craneLib = crane.overrideToolchain rustToolchain;
66 gpu-lib = if withGLES then libglvnd else vulkan-loader;
67 commonArgs =
68 let
69 zedCargoLock = builtins.fromTOML (builtins.readFile ../crates/zed/Cargo.toml);
70 in
71 rec {
72 pname = "zed-editor";
73 version = zedCargoLock.package.version + "-nightly";
74 src = builtins.path {
75 path = ../.;
76 filter = mkIncludeFilter ../.;
77 name = "source";
78 };
79
80 cargoLock = ../Cargo.lock;
81
82 nativeBuildInputs =
83 [
84 clang # TODO: use pkgs.clangStdenv or ignore cargo config?
85 cmake
86 copyDesktopItems
87 curl
88 perl
89 pkg-config
90 protobuf
91 cargo-about
92 rustPlatform.bindgenHook
93 ]
94 ++ lib.optionals stdenv.hostPlatform.isLinux [ makeWrapper ]
95 ++ lib.optionals stdenv.hostPlatform.isDarwin [
96 # TODO: move to overlay so it's usable in the shell
97 (cargo-bundle.overrideAttrs (old: {
98 version = "0.6.0-zed";
99 src = fetchFromGitHub {
100 owner = "zed-industries";
101 repo = "cargo-bundle";
102 rev = "zed-deploy";
103 hash = "sha256-OxYdTSiR9ueCvtt7Y2OJkvzwxxnxu453cMS+l/Bi5hM=";
104 };
105 }))
106 ];
107
108 buildInputs =
109 [
110 curl
111 fontconfig
112 freetype
113 # TODO: need staticlib of this for linking the musl remote server.
114 # should make it a separate derivation/flake output
115 # see https://crane.dev/examples/cross-musl.html
116 libgit2
117 openssl
118 sqlite
119 zlib
120 zstd
121 ]
122 ++ lib.optionals stdenv.hostPlatform.isLinux [
123 alsa-lib
124 libxkbcommon
125 wayland
126 gpu-lib
127 xorg.libxcb
128 ]
129 ++ lib.optionals stdenv.hostPlatform.isDarwin [
130 apple-sdk_15
131 darwin.apple_sdk.frameworks.System
132 (darwinMinVersionHook "10.15")
133 ];
134
135 cargoExtraArgs = "--package=zed --package=cli --features=gpui/runtime_shaders";
136
137 env = {
138 ZSTD_SYS_USE_PKG_CONFIG = true;
139 FONTCONFIG_FILE = makeFontsConf {
140 fontDirectories = [
141 ../assets/fonts/plex-mono
142 ../assets/fonts/plex-sans
143 ];
144 };
145 ZED_UPDATE_EXPLANATION = "Zed has been installed using Nix. Auto-updates have thus been disabled.";
146 RELEASE_VERSION = version;
147 RUSTFLAGS = if withGLES then "--cfg gles" else "";
148 # TODO: why are these not handled by the linker given that they're in buildInputs?
149 NIX_LDFLAGS = "-rpath ${
150 lib.makeLibraryPath [
151 gpu-lib
152 wayland
153 ]
154 }";
155 LK_CUSTOM_WEBRTC = livekit-libwebrtc;
156 };
157
158 cargoVendorDir = craneLib.vendorCargoDeps {
159 inherit src cargoLock;
160 overrideVendorGitCheckout =
161 let
162 hasWebRtcSys = builtins.any (crate: crate.name == "webrtc-sys");
163 # `webrtc-sys` expects a staticlib; nixpkgs' `livekit-webrtc` has been patched to
164 # produce a `dylib`... patching `webrtc-sys`'s build script is the easier option
165 # TODO: send livekit sdk a PR to make this configurable
166 postPatch = ''
167 substituteInPlace webrtc-sys/build.rs --replace-fail \
168 "cargo:rustc-link-lib=static=webrtc" "cargo:rustc-link-lib=dylib=webrtc"
169 '';
170 in
171 crates: drv:
172 if hasWebRtcSys crates then
173 drv.overrideAttrs (o: {
174 postPatch = (o.postPatch or "") + postPatch;
175 })
176 else
177 drv;
178 };
179 };
180 cargoArtifacts = craneLib.buildDepsOnly (
181 commonArgs
182 // {
183 # TODO: figure out why the main derivation is still rebuilding deps...
184 # disable pre-building the deps for now
185 buildPhaseCargoCommand = "true";
186
187 # forcibly inhibit `doInstallCargoArtifacts`...
188 # https://github.com/ipetkov/crane/blob/1d19e2ec7a29dcc25845eec5f1527aaf275ec23e/lib/setupHooks/installCargoArtifactsHook.sh#L111
189 #
190 # it is, unfortunately, not overridable in `buildDepsOnly`:
191 # https://github.com/ipetkov/crane/blob/1d19e2ec7a29dcc25845eec5f1527aaf275ec23e/lib/buildDepsOnly.nix#L85
192 preBuild = "postInstallHooks=()";
193 doCheck = false;
194 }
195 );
196in
197craneLib.buildPackage (
198 lib.recursiveUpdate commonArgs {
199 inherit cargoArtifacts;
200
201 patches = lib.optionals stdenv.hostPlatform.isDarwin [
202 # Livekit requires Swift 6
203 # We need this until livekit-rust sdk is used
204 ../script/patches/use-cross-platform-livekit.patch
205 ];
206
207 dontUseCmakeConfigure = true;
208
209 # without the env var generate-licenses fails due to crane's fetchCargoVendor, see:
210 # https://github.com/zed-industries/zed/issues/19971#issuecomment-2688455390
211 preBuild = ''
212 ALLOW_MISSING_LICENSES=yes bash script/generate-licenses
213 echo nightly > crates/zed/RELEASE_CHANNEL
214 '';
215
216 # TODO: try craneLib.cargoNextest separate output
217 # for now we're not worried about running our test suite in the nix sandbox
218 doCheck = false;
219
220 installPhase =
221 if stdenv.hostPlatform.isDarwin then
222 ''
223 runHook preInstall
224
225 pushd crates/zed
226 sed -i "s/package.metadata.bundle-nightly/package.metadata.bundle/" Cargo.toml
227 export CARGO_BUNDLE_SKIP_BUILD=true
228 app_path="$(cargo bundle --release | xargs)"
229 popd
230
231 mkdir -p $out/Applications $out/bin
232 # Zed expects git next to its own binary
233 ln -s ${git}/bin/git "$app_path/Contents/MacOS/git"
234 mv target/release/cli "$app_path/Contents/MacOS/cli"
235 mv "$app_path" $out/Applications/
236
237 # Physical location of the CLI must be inside the app bundle as this is used
238 # to determine which app to start
239 ln -s "$out/Applications/Zed Nightly.app/Contents/MacOS/cli" $out/bin/zed
240
241 runHook postInstall
242 ''
243 else
244 # TODO: icons should probably be named "zed-nightly". fix bundle-linux first
245 ''
246 runHook preInstall
247
248 mkdir -p $out/bin $out/libexec
249 cp target/release/zed $out/libexec/zed-editor
250 cp target/release/cli $out/bin/zed
251
252 install -D "crates/zed/resources/app-icon-nightly@2x.png" \
253 "$out/share/icons/hicolor/1024x1024@2x/apps/zed.png"
254 install -D crates/zed/resources/app-icon-nightly.png \
255 $out/share/icons/hicolor/512x512/apps/zed.png
256
257 # extracted from ../script/bundle-linux (envsubst) and
258 # ../script/install.sh (final desktop file name)
259 (
260 export DO_STARTUP_NOTIFY="true"
261 export APP_CLI="zed"
262 export APP_ICON="zed"
263 export APP_NAME="Zed Nightly"
264 export APP_ARGS="%U"
265 mkdir -p "$out/share/applications"
266 ${lib.getExe envsubst} < "crates/zed/resources/zed.desktop.in" > "$out/share/applications/dev.zed.Zed-Nightly.desktop"
267 )
268
269 runHook postInstall
270 '';
271
272 # TODO: why isn't this also done on macOS?
273 postFixup = lib.optionalString stdenv.hostPlatform.isLinux ''
274 wrapProgram $out/libexec/zed-editor --suffix PATH : ${lib.makeBinPath [ nodejs_22 ]}
275 '';
276
277 meta = {
278 description = "High-performance, multiplayer code editor from the creators of Atom and Tree-sitter";
279 homepage = "https://zed.dev";
280 changelog = "https://zed.dev/releases/preview";
281 license = lib.licenses.gpl3Only;
282 mainProgram = "zed";
283 platforms = lib.platforms.linux ++ lib.platforms.darwin;
284 };
285 }
286)