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