From 1d453785e5ac06e595a27255187c971649276220 Mon Sep 17 00:00:00 2001 From: Christopher Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Tue, 4 Mar 2025 11:23:28 -0500 Subject: [PATCH] Clean up Nix Flake & make it easier to customize (#12831) --- default.nix | 84 ++++++++++++++++++++-- flake.lock | 28 ++------ flake.nix | 195 +++++++++++---------------------------------------- grammars.nix | 32 +++++---- 4 files changed, 142 insertions(+), 197 deletions(-) diff --git a/default.nix b/default.nix index d2c51ec3a..0efa75bb4 100644 --- a/default.nix +++ b/default.nix @@ -1,8 +1,78 @@ -# Flake's default package for non-flake-enabled nix instances -let - compat = builtins.fetchTarball { - url = "https://github.com/edolstra/flake-compat/archive/b4a34015c698c7793d592d66adbab377907a2be8.tar.gz"; - sha256 = "sha256:1qc703yg0babixi6wshn5wm2kgl5y1drcswgszh4xxzbrwkk9sv7"; - }; +{ + lib, + rustPlatform, + callPackage, + runCommand, + installShellFiles, + git, + ... +}: let + fs = lib.fileset; + + src = fs.difference (fs.gitTracked ./.) (fs.unions [ + ./.envrc + ./rustfmt.toml + ./screenshot.png + ./book + ./docs + ./flake.lock + (fs.fileFilter (file: lib.strings.hasInfix ".git" file.name) ./.) + (fs.fileFilter (file: file.hasExt "svg") ./.) + (fs.fileFilter (file: file.hasExt "md") ./.) + (fs.fileFilter (file: file.hasExt "nix") ./.) + ]); + + # Next we actually need to build the grammars and the runtime directory + # that they reside in. It is built by calling the derivation in the + # grammars.nix file, then taking the runtime directory in the git repo + # and hooking symlinks up to it. + grammars = callPackage ./grammars.nix {}; + runtimeDir = runCommand "helix-runtime" {} '' + mkdir -p $out + ln -s ${./runtime}/* $out + rm -r $out/grammars + ln -s ${grammars} $out/grammars + ''; in - (import compat {src = ./.;}).defaultNix + # Currently rustPlatform.buildRustPackage doesn't have the finalAttrs pattern + # hooked up. To get around this while having good customization, mkDerivation is + # used instead. + rustPlatform.buildRustPackage (self: { + cargoLock.lockFile = ./Cargo.lock; + + nativeBuildInputs = [ + installShellFiles + git + ]; + + buildType = "release"; + + name = with builtins; (fromTOML (readFile ./helix-term/Cargo.toml)).package.name; + src = fs.toSource { + root = ./.; + fileset = src; + }; + + # Helix attempts to reach out to the network and get the grammars. Nix doesn't allow this. + HELIX_DISABLE_AUTO_GRAMMAR_BUILD = "1"; + + # So Helix knows what rev it is. + HELIX_NIX_BUILD_REV = self.rev or self.dirtyRev or null; + + doCheck = false; + strictDeps = true; + + # Sets the Helix runtimedir to the grammars + env.HELIX_DEFAULT_RUNTIME = "${runtimeDir}"; + + # Get all the application stuff in the output directory. + postInstall = '' + mkdir -p $out/lib + installShellCompletion ${./contrib/completion}/hx.{bash,fish,zsh} + mkdir -p $out/share/{applications,icons/hicolor/256x256/apps} + cp ${./contrib/Helix.desktop} $out/share/applications + cp ${./contrib/helix.png} $out/share/icons/hicolor/256x256/apps + ''; + + meta.mainProgram = "hx"; + }) diff --git a/flake.lock b/flake.lock index 62ff34477..7e3d5bd3c 100644 --- a/flake.lock +++ b/flake.lock @@ -1,20 +1,5 @@ { "nodes": { - "crane": { - "locked": { - "lastModified": 1737563566, - "narHash": "sha256-GLJvkOG29XCynQm8XWPyykMRqIhxKcBARVu7Ydrz02M=", - "owner": "ipetkov", - "repo": "crane", - "rev": "849376434956794ebc7a6b487d31aace395392ba", - "type": "github" - }, - "original": { - "owner": "ipetkov", - "repo": "crane", - "type": "github" - } - }, "flake-utils": { "inputs": { "systems": "systems" @@ -35,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1728018373, - "narHash": "sha256-NOiTvBbRLIOe5F6RbHaAh6++BNjsb149fGZd1T4+KBg=", + "lastModified": 1740560979, + "narHash": "sha256-Vr3Qi346M+8CjedtbyUevIGDZW8LcA1fTG0ugPY/Hic=", "owner": "nixos", "repo": "nixpkgs", - "rev": "bc947f541ae55e999ffdb4013441347d83b00feb", + "rev": "5135c59491985879812717f4c9fea69604e7f26f", "type": "github" }, "original": { @@ -51,7 +36,6 @@ }, "root": { "inputs": { - "crane": "crane", "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", "rust-overlay": "rust-overlay" @@ -64,11 +48,11 @@ ] }, "locked": { - "lastModified": 1737599167, - "narHash": "sha256-S2rHCrQWCDVp63XxL/AQbGr1g5M8Zx14C7Jooa4oM8o=", + "lastModified": 1740623427, + "narHash": "sha256-3SdPQrZoa4odlScFDUHd4CUPQ/R1gtH4Mq9u8CBiK8M=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "38374302ae9edf819eac666d1f276d62c712dd06", + "rev": "d342e8b5fd88421ff982f383c853f0fc78a847ab", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 1fda3fa94..7d176cb01 100644 --- a/flake.nix +++ b/flake.nix @@ -8,13 +8,11 @@ url = "github:oxalica/rust-overlay"; inputs.nixpkgs.follows = "nixpkgs"; }; - crane.url = "github:ipetkov/crane"; }; outputs = { self, nixpkgs, - crane, flake-utils, rust-overlay, ... @@ -24,167 +22,56 @@ inherit system; overlays = [(import rust-overlay)]; }; - mkRootPath = rel: - builtins.path { - path = "${toString ./.}/${rel}"; - name = rel; - }; - filteredSource = let - pathsToIgnore = [ - ".envrc" - ".ignore" - ".github" - ".gitignore" - "logo_dark.svg" - "logo_light.svg" - "rust-toolchain.toml" - "rustfmt.toml" - "runtime" - "screenshot.png" - "book" - "docs" - "README.md" - "CHANGELOG.md" - "shell.nix" - "default.nix" - "grammars.nix" - "flake.nix" - "flake.lock" - ]; - ignorePaths = path: type: let - inherit (nixpkgs) lib; - # split the nix store path into its components - components = lib.splitString "/" path; - # drop off the `/nix/hash-source` section from the path - relPathComponents = lib.drop 4 components; - # reassemble the path components - relPath = lib.concatStringsSep "/" relPathComponents; - in - lib.all (p: ! (lib.hasPrefix p relPath)) pathsToIgnore; - in - builtins.path { - name = "helix-source"; - path = toString ./.; - # filter out unnecessary paths - filter = ignorePaths; - }; - makeOverridableHelix = old: config: let - grammars = pkgs.callPackage ./grammars.nix config; - runtimeDir = pkgs.runCommand "helix-runtime" {} '' - mkdir -p $out - ln -s ${mkRootPath "runtime"}/* $out - rm -r $out/grammars - ln -s ${grammars} $out/grammars - ''; - helix-wrapped = - pkgs.runCommand - old.name - { - inherit (old) pname version; - meta = old.meta or {}; - passthru = - (old.passthru or {}) - // { - unwrapped = old; - }; - nativeBuildInputs = [pkgs.makeWrapper]; - makeWrapperArgs = config.makeWrapperArgs or []; - } - '' - cp -rs --no-preserve=mode,ownership ${old} $out - wrapProgram "$out/bin/hx" ''${makeWrapperArgs[@]} --set HELIX_RUNTIME "${runtimeDir}" - ''; - in - helix-wrapped - // { - override = makeOverridableHelix old; - passthru = - helix-wrapped.passthru - // { - wrapper = old: makeOverridableHelix old config; - }; - }; - stdenv = - if pkgs.stdenv.isLinux - then pkgs.stdenv - else pkgs.clangStdenv; - rustFlagsEnv = pkgs.lib.optionalString stdenv.isLinux "-C link-arg=-fuse-ld=lld -C target-cpu=native -Clink-arg=-Wl,--no-rosegment --cfg tokio_unstable"; - rustToolchain = pkgs.pkgsBuildHost.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; - craneLibMSRV = (crane.mkLib pkgs).overrideToolchain rustToolchain; - craneLibStable = (crane.mkLib pkgs).overrideToolchain pkgs.pkgsBuildHost.rust-bin.stable.latest.default; - commonArgs = { - inherit stdenv; - inherit (craneLibMSRV.crateNameFromCargoToml {cargoToml = ./helix-term/Cargo.toml;}) pname; - inherit (craneLibMSRV.crateNameFromCargoToml {cargoToml = ./Cargo.toml;}) version; - src = filteredSource; - # disable fetching and building of tree-sitter grammars in the helix-term build.rs - HELIX_DISABLE_AUTO_GRAMMAR_BUILD = "1"; - buildInputs = [stdenv.cc.cc.lib]; - nativeBuildInputs = [pkgs.installShellFiles]; - # disable tests - doCheck = false; - meta.mainProgram = "hx"; + + # Get Helix's MSRV toolchain to build with by default. + msrvToolchain = pkgs.pkgsBuildHost.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml; + msrvPlatform = pkgs.makeRustPlatform { + cargo = msrvToolchain; + rustc = msrvToolchain; }; - cargoArtifacts = craneLibMSRV.buildDepsOnly commonArgs; in { - packages = { - helix-unwrapped = craneLibStable.buildPackage (commonArgs - // { - cargoArtifacts = craneLibStable.buildDepsOnly commonArgs; - postInstall = '' - mkdir -p $out/share/applications $out/share/icons/hicolor/scalable/apps $out/share/icons/hicolor/256x256/apps - cp contrib/Helix.desktop $out/share/applications - cp logo.svg $out/share/icons/hicolor/scalable/apps/helix.svg - cp contrib/helix.png $out/share/icons/hicolor/256x256/apps - installShellCompletion contrib/completion/hx.{bash,fish,zsh} - ''; - # set git revision for nix flake builds, see 'git_hash' in helix-loader/build.rs - HELIX_NIX_BUILD_REV = self.rev or self.dirtyRev or null; - }); - helix = makeOverridableHelix self.packages.${system}.helix-unwrapped {}; - default = self.packages.${system}.helix; + packages = rec { + helix = pkgs.callPackage ./default.nix {}; + + # The default Helix build. Uses the latest stable Rust toolchain, and unstable + # nixpkgs. + # + # This can be overridden though to add Cargo Features, flags, and different toolchains with + # packages.${system}.default.override { ... }; + default = helix; }; - checks = { - # Build the crate itself - inherit (self.packages.${system}) helix; - - clippy = craneLibMSRV.cargoClippy (commonArgs - // { - inherit cargoArtifacts; - cargoClippyExtraArgs = "--all-targets -- --deny warnings"; - }); - - fmt = craneLibMSRV.cargoFmt commonArgs; - - doc = craneLibMSRV.cargoDoc (commonArgs - // { - inherit cargoArtifacts; - }); - - test = craneLibMSRV.cargoTest (commonArgs - // { - inherit cargoArtifacts; - }); + checks.helix = self.outputs.packages.${system}.helix.override { + buildType = "debug"; + rustPlatform = msrvPlatform; }; - devShells.default = pkgs.mkShell { - inputsFrom = builtins.attrValues self.checks.${system}; - nativeBuildInputs = with pkgs; - [lld_13 cargo-flamegraph rust-analyzer] - ++ (lib.optional (stdenv.isx86_64 && stdenv.isLinux) pkgs.cargo-tarpaulin) - ++ (lib.optional stdenv.isLinux pkgs.lldb) - ++ (lib.optional stdenv.isDarwin pkgs.darwin.apple_sdk.frameworks.CoreFoundation); - shellHook = '' - export HELIX_RUNTIME="$PWD/runtime" - export RUST_BACKTRACE="1" - export RUSTFLAGS="''${RUSTFLAGS:-""} ${rustFlagsEnv}" - ''; - }; + # Devshell behavior is preserved. + devShells.default = let + rustFlagsEnv = pkgs.lib.optionalString pkgs.stdenv.isLinux "-C link-arg=-fuse-ld=lld -C target-cpu=native -Clink-arg=-Wl,--no-rosegment --cfg tokio_unstable"; + in + pkgs.mkShell + { + inputsFrom = [self.checks.${system}.helix]; + nativeBuildInputs = with pkgs; + [ + lld_13 + cargo-flamegraph + rust-bin.nightly.latest.rust-analyzer + ] + ++ (lib.optional (stdenv.isx86_64 && stdenv.isLinux) cargo-tarpaulin) + ++ (lib.optional stdenv.isLinux lldb) + ++ (lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.CoreFoundation); + shellHook = '' + export HELIX_RUNTIME="$PWD/runtime" + export RUST_BACKTRACE="1" + export RUSTFLAGS="''${RUSTFLAGS:-""} ${rustFlagsEnv}" + ''; + }; }) // { overlays.default = final: prev: { - inherit (self.packages.${final.system}) helix; + helix = final.callPackage ./default.nix {}; }; }; diff --git a/grammars.nix b/grammars.nix index 967b1b136..bc99d21df 100644 --- a/grammars.nix +++ b/grammars.nix @@ -32,10 +32,10 @@ # If `use-grammars.except` is set, use all other grammars. # Otherwise use all grammars. useGrammar = grammar: - if languagesConfig?use-grammars.only then - builtins.elem grammar.name languagesConfig.use-grammars.only - else if languagesConfig?use-grammars.except then - !(builtins.elem grammar.name languagesConfig.use-grammars.except) + if languagesConfig ? use-grammars.only + then builtins.elem grammar.name languagesConfig.use-grammars.only + else if languagesConfig ? use-grammars.except + then !(builtins.elem grammar.name languagesConfig.use-grammars.except) else true; grammarsToUse = builtins.filter useGrammar languagesConfig.grammar; gitGrammars = builtins.filter isGitGrammar grammarsToUse; @@ -66,10 +66,10 @@ version = grammar.source.rev; src = source; - sourceRoot = if builtins.hasAttr "subpath" grammar.source then - "source/${grammar.source.subpath}" - else - "source"; + sourceRoot = + if builtins.hasAttr "subpath" grammar.source + then "source/${grammar.source.subpath}" + else "source"; dontConfigure = true; @@ -116,15 +116,19 @@ ''; }; grammarsToBuild = builtins.filter includeGrammarIf gitGrammars; - builtGrammars = builtins.map (grammar: { - inherit (grammar) name; - value = buildGrammar grammar; - }) grammarsToBuild; + builtGrammars = + builtins.map (grammar: { + inherit (grammar) name; + value = buildGrammar grammar; + }) + grammarsToBuild; extensibleGrammars = lib.makeExtensible (self: builtins.listToAttrs builtGrammars); - overlaidGrammars = lib.pipe extensibleGrammars + overlaidGrammars = + lib.pipe extensibleGrammars (builtins.map (overlay: grammar: grammar.extend overlay) grammarOverlays); - grammarLinks = lib.mapAttrsToList + grammarLinks = + lib.mapAttrsToList (name: artifact: "ln -s ${artifact}/${name}.so $out/${name}.so") (lib.filterAttrs (n: v: lib.isDerivation v) overlaidGrammars); in