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