build.nix

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