build.nix

  1{
  2  pkgs,
  3  system,
  4  lib,
  5  stdenv,
  6
  7  apple-sdk_15,
  8  darwin,
  9  darwinMinVersionHook,
 10
 11  cargo-about,
 12  cargo-bundle,
 13  crane,
 14  rustPlatform,
 15  rustToolchain,
 16
 17  copyDesktopItems,
 18  envsubst,
 19  fetchFromGitHub,
 20  makeFontsConf,
 21  makeWrapper,
 22
 23  alsa-lib,
 24  cmake,
 25  curl,
 26  fontconfig,
 27  freetype,
 28  git,
 29  glib,
 30  libdrm,
 31  libgbm,
 32  libgit2,
 33  libglvnd,
 34  libva,
 35  libxcomposite,
 36  libxdamage,
 37  libxext,
 38  libxfixes,
 39  libxkbcommon,
 40  libxrandr,
 41  nodejs_22,
 42  openssl,
 43  perl,
 44  pkg-config,
 45  protobuf,
 46  sqlite,
 47  vulkan-loader,
 48  wayland,
 49  xorg,
 50  zlib,
 51  zstd,
 52
 53  withGLES ? false,
 54  profile ? "release",
 55  commitSha ? null,
 56}:
 57assert withGLES -> stdenv.hostPlatform.isLinux;
 58let
 59  mkIncludeFilter =
 60    root': path: type:
 61    let
 62      # note: under lazy-trees this introduces an extra copy
 63      root = toString root' + "/";
 64      relPath = lib.removePrefix root path;
 65      topLevelIncludes = [
 66        "crates"
 67        "assets"
 68        "extensions"
 69        "script"
 70        "tooling"
 71        "Cargo.toml"
 72        ".config" # nextest?
 73        ".cargo"
 74      ];
 75      firstComp = builtins.head (lib.path.subpath.components relPath);
 76    in
 77    builtins.elem firstComp topLevelIncludes;
 78
 79  craneLib = crane.overrideToolchain rustToolchain;
 80  gpu-lib = if withGLES then libglvnd else vulkan-loader;
 81  commonArgs =
 82    let
 83      zedCargoLock = builtins.fromTOML (builtins.readFile ../crates/zed/Cargo.toml);
 84      stdenv' = stdenv;
 85    in
 86    rec {
 87      pname = "zed-editor";
 88      version =
 89        zedCargoLock.package.version
 90        + "-nightly"
 91        + lib.optionalString (commitSha != null) "+${builtins.substring 0 7 commitSha}";
 92      src = builtins.path {
 93        path = ../.;
 94        filter = mkIncludeFilter ../.;
 95        name = "source";
 96      };
 97
 98      cargoLock = ../Cargo.lock;
 99
100      nativeBuildInputs = [
101        cmake
102        copyDesktopItems
103        curl
104        perl
105        pkg-config
106        protobuf
107        # Pin cargo-about to 0.8.2. Newer versions don't work with the current license identifiers
108        # See https://github.com/zed-industries/zed/pull/44012
109        (cargo-about.overrideAttrs (
110          new: old: rec {
111            version = "0.8.2";
112
113            src = fetchFromGitHub {
114              owner = "EmbarkStudios";
115              repo = "cargo-about";
116              tag = version;
117              sha256 = "sha256-cNKZpDlfqEXeOE5lmu79AcKOawkPpk4PQCsBzNtIEbs=";
118            };
119
120            cargoHash = "sha256-NnocSs6UkuF/mCM3lIdFk+r51Iz2bHuYzMT/gEbT/nk=";
121
122            # NOTE: can drop once upstream uses `finalAttrs` here:
123            # https://github.com/NixOS/nixpkgs/blob/10214747f5e6e7cb5b9bdf9e018a3c7b3032f5af/pkgs/build-support/rust/build-rust-package/default.nix#L104
124            #
125            # See (for context): https://github.com/NixOS/nixpkgs/pull/382550
126            cargoDeps = rustPlatform.fetchCargoVendor {
127              inherit (new) src;
128              hash = new.cargoHash;
129              patches = new.cargoPatches or [ ];
130              name = new.cargoDepsName or new.finalPackage.name;
131            };
132          }
133        ))
134        rustPlatform.bindgenHook
135      ]
136      ++ lib.optionals stdenv'.hostPlatform.isLinux [ makeWrapper ]
137      ++ lib.optionals stdenv'.hostPlatform.isDarwin [
138        (cargo-bundle.overrideAttrs (
139          new: old: {
140            version = "0.6.1-zed";
141            src = fetchFromGitHub {
142              owner = "zed-industries";
143              repo = "cargo-bundle";
144              rev = "2be2669972dff3ddd4daf89a2cb29d2d06cad7c7";
145              hash = "sha256-cSvW0ND148AGdIGWg/ku0yIacVgW+9f1Nsi+kAQxVrI=";
146            };
147            cargoHash = "sha256-urn+A3yuw2uAO4HGmvQnKvWtHqvG9KHxNCCWTiytE4k=";
148
149            # NOTE: can drop once upstream uses `finalAttrs` here:
150            # https://github.com/NixOS/nixpkgs/blob/10214747f5e6e7cb5b9bdf9e018a3c7b3032f5af/pkgs/build-support/rust/build-rust-package/default.nix#L104
151            #
152            # See (for context): https://github.com/NixOS/nixpkgs/pull/382550
153            cargoDeps = rustPlatform.fetchCargoVendor {
154              inherit (new) src;
155              hash = new.cargoHash;
156              patches = new.cargoPatches or [ ];
157              name = new.cargoDepsName or new.finalPackage.name;
158            };
159          }
160        ))
161      ];
162
163      buildInputs = [
164        curl
165        fontconfig
166        freetype
167        # TODO: need staticlib of this for linking the musl remote server.
168        # should make it a separate derivation/flake output
169        # see https://crane.dev/examples/cross-musl.html
170        libgit2
171        openssl
172        sqlite
173        zlib
174        zstd
175      ]
176      ++ lib.optionals stdenv'.hostPlatform.isLinux [
177        alsa-lib
178        glib
179        libva
180        libxkbcommon
181        wayland
182        gpu-lib
183        libglvnd
184        xorg.libX11
185        xorg.libxcb
186        libdrm
187        libgbm
188        libva
189        libxcomposite
190        libxdamage
191        libxext
192        libxfixes
193        libxrandr
194      ]
195      ++ lib.optionals stdenv'.hostPlatform.isDarwin [
196        apple-sdk_15
197        (darwinMinVersionHook "10.15")
198      ];
199
200      cargoExtraArgs = "-p zed -p cli --locked --features=gpui_platform/runtime_shaders";
201
202      stdenv =
203        pkgs:
204        let
205          base = pkgs.llvmPackages.stdenv;
206          addBinTools = old: {
207            cc = old.cc.override {
208              inherit (pkgs.llvmPackages) bintools;
209            };
210          };
211          custom = lib.pipe base [
212            (stdenv: stdenv.override addBinTools)
213            pkgs.stdenvAdapters.useMoldLinker
214          ];
215        in
216        if stdenv'.hostPlatform.isLinux then custom else base;
217
218      env = {
219        ZSTD_SYS_USE_PKG_CONFIG = true;
220        FONTCONFIG_FILE = makeFontsConf {
221          fontDirectories = [
222            ../assets/fonts/lilex
223            ../assets/fonts/ibm-plex-sans
224          ];
225        };
226        ZED_UPDATE_EXPLANATION = "Zed has been installed using Nix. Auto-updates have thus been disabled.";
227        RELEASE_VERSION = version;
228        ZED_COMMIT_SHA = lib.optionalString (commitSha != null) "${commitSha}";
229        LK_CUSTOM_WEBRTC = pkgs.callPackage ./livekit-libwebrtc/package.nix { };
230        PROTOC = "${protobuf}/bin/protoc";
231
232        CARGO_PROFILE = profile;
233        # need to handle some profiles specially https://github.com/rust-lang/cargo/issues/11053
234        TARGET_DIR = "target/" + (if profile == "dev" then "debug" else profile);
235
236        # for some reason these deps being in buildInputs isn't enough, the only thing
237        # about them that's special is that they're manually dlopened at runtime
238        NIX_LDFLAGS = lib.optionalString stdenv'.hostPlatform.isLinux "-rpath ${
239          lib.makeLibraryPath [
240            gpu-lib
241            wayland
242            libva
243          ]
244        }";
245
246        NIX_OUTPATH_USED_AS_RANDOM_SEED = "norebuilds";
247      };
248
249      # prevent nix from removing the "unused" wayland/gpu-lib rpaths
250      dontPatchELF = stdenv'.hostPlatform.isLinux;
251
252      # TODO: try craneLib.cargoNextest separate output
253      # for now we're not worried about running our test suite (or tests for deps) in the nix sandbox
254      doCheck = false;
255
256      cargoVendorDir = craneLib.vendorCargoDeps {
257        inherit src cargoLock;
258        overrideVendorGitCheckout =
259          let
260            hasWebRtcSys = builtins.any (crate: crate.name == "webrtc-sys");
261            # we can't set $RUSTFLAGS because that clobbers the cargo config
262            # see https://github.com/rust-lang/cargo/issues/5376#issuecomment-2163350032
263            glesConfig = builtins.toFile "config.toml" ''
264              [target.'cfg(all())']
265              rustflags = ["--cfg", "gles"]
266            '';
267
268            # `webrtc-sys` expects a staticlib; nixpkgs' `livekit-webrtc` has been patched to
269            # produce a `dylib`... patching `webrtc-sys`'s build script is the easier option
270            # TODO: send livekit sdk a PR to make this configurable
271            postPatch = ''
272              substituteInPlace webrtc-sys/build.rs --replace-fail \
273                "cargo:rustc-link-lib=static=webrtc" "cargo:rustc-link-lib=dylib=webrtc"
274
275              substituteInPlace webrtc-sys/build.rs --replace-fail \
276                'add_gio_headers(&mut builder);' \
277                'for lib_name in ["glib-2.0", "gio-2.0"] {
278                    if let Ok(lib) = pkg_config::Config::new().cargo_metadata(false).probe(lib_name) {
279                        for path in lib.include_paths {
280                            builder.include(&path);
281                        }
282                    }
283                }'
284            ''
285            + lib.optionalString withGLES ''
286              cat ${glesConfig} >> .cargo/config/config.toml
287            '';
288          in
289          crates: drv:
290          if hasWebRtcSys crates then
291            drv.overrideAttrs (o: {
292              postPatch = (o.postPatch or "") + postPatch;
293            })
294          else
295            drv;
296      };
297    };
298  cargoArtifacts = craneLib.buildDepsOnly commonArgs;
299in
300craneLib.buildPackage (
301  lib.recursiveUpdate commonArgs {
302    inherit cargoArtifacts;
303
304    dontUseCmakeConfigure = true;
305
306    # without the env var generate-licenses fails due to crane's fetchCargoVendor, see:
307    # https://github.com/zed-industries/zed/issues/19971#issuecomment-2688455390
308    # TODO: put this in a separate derivation that depends on src to avoid running it on every build
309    preBuild = ''
310      ALLOW_MISSING_LICENSES=yes bash script/generate-licenses
311      echo nightly > crates/zed/RELEASE_CHANNEL
312    '';
313
314    installPhase =
315      if stdenv.hostPlatform.isDarwin then
316        ''
317          runHook preInstall
318
319          pushd crates/zed
320          sed -i "s/package.metadata.bundle-nightly/package.metadata.bundle/" Cargo.toml
321          export CARGO_BUNDLE_SKIP_BUILD=true
322          app_path="$(cargo bundle --profile $CARGO_PROFILE | xargs)"
323          popd
324
325          mkdir -p $out/Applications $out/bin
326          # Zed expects git next to its own binary
327          ln -s ${git}/bin/git "$app_path/Contents/MacOS/git"
328          mv $TARGET_DIR/cli "$app_path/Contents/MacOS/cli"
329          mv "$app_path" $out/Applications/
330
331          # Physical location of the CLI must be inside the app bundle as this is used
332          # to determine which app to start
333          ln -s "$out/Applications/Zed Nightly.app/Contents/MacOS/cli" $out/bin/zed
334
335          runHook postInstall
336        ''
337      else
338        ''
339          runHook preInstall
340
341          mkdir -p $out/bin $out/libexec
342          cp $TARGET_DIR/zed $out/libexec/zed-editor
343          cp $TARGET_DIR/cli  $out/bin/zed
344          ln -s $out/bin/zed $out/bin/zeditor  # home-manager expects the CLI binary to be here
345
346
347          install -D "crates/zed/resources/app-icon-nightly@2x.png" \
348            "$out/share/icons/hicolor/1024x1024@2x/apps/zed.png"
349          install -D crates/zed/resources/app-icon-nightly.png \
350            $out/share/icons/hicolor/512x512/apps/zed.png
351
352          # TODO: icons should probably be named "zed-nightly"
353          (
354            export DO_STARTUP_NOTIFY="true"
355            export APP_CLI="zed"
356            export APP_ICON="zed"
357            export APP_NAME="Zed Nightly"
358            export APP_ARGS="%U"
359            mkdir -p "$out/share/applications"
360            ${lib.getExe envsubst} < "crates/zed/resources/zed.desktop.in" > "$out/share/applications/dev.zed.Zed-Nightly.desktop"
361            chmod +x "$out/share/applications/dev.zed.Zed-Nightly.desktop"
362          )
363
364          runHook postInstall
365        '';
366
367    # TODO: why isn't this also done on macOS?
368    postFixup = lib.optionalString stdenv.hostPlatform.isLinux ''
369      wrapProgram $out/libexec/zed-editor --suffix PATH : ${lib.makeBinPath [ nodejs_22 ]}
370    '';
371
372    meta = {
373      description = "High-performance, multiplayer code editor from the creators of Atom and Tree-sitter";
374      homepage = "https://zed.dev";
375      changelog = "https://zed.dev/releases/preview";
376      license = lib.licenses.gpl3Only;
377      mainProgram = "zed";
378      platforms = lib.platforms.linux ++ lib.platforms.darwin;
379    };
380  }
381)