From 47e1bba41e7f10ff335e53799cacfd499119c0d9 Mon Sep 17 00:00:00 2001 From: Marien Zwart Date: Mon, 4 Mar 2024 01:29:47 +1100 Subject: [PATCH] Snapshot: all dependencies for doom-full build They don't all work, and the actual Doom integration isn't there yet... --- cli.el | 17 ++ flake.lock | 162 ++++++++++++++ flake.nix | 31 +++ org-contrib-ob-stata-ess-optional.patch | 66 ++++++ package.nix | 284 ++++++++++++++++++++++++ 5 files changed, 560 insertions(+) create mode 100755 cli.el create mode 100644 flake.lock create mode 100644 flake.nix create mode 100644 org-contrib-ob-stata-ess-optional.patch create mode 100644 package.nix diff --git a/cli.el b/cli.el new file mode 100755 index 0000000..e6da364 --- /dev/null +++ b/cli.el @@ -0,0 +1,17 @@ +;;; cli.el -*- lexical-binding: t; -*- + +(require 'json) +(doom-require 'doom-cli 'packages) + +(defcli! dump-for-nix-build + ((output-directory ("-o" dir) "Directory to dump into.") + (&flag full? ("--full"))) + "Dump intermediates for nix-doom-emacs-unstraightened." + (let* ((packages (doom-package-list full?)) + ;; For built-in packages, the :ignore property is the location of the + ;; built-in library, which is a Nix store path. We do not want that + ;; path to escape: avoid it by just filtering ignored packages here. + (packages (seq-remove (lambda (p) (plist-get (cdr p) :ignore)) packages)) + (json (json-encode packages)) + (json-path (expand-file-name "packages.json" output-directory))) + (write-region json nil json-path))) diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..d052a0c --- /dev/null +++ b/flake.lock @@ -0,0 +1,162 @@ +{ + "nodes": { + "doomemacs": { + "flake": false, + "locked": { + "lastModified": 1709171466, + "narHash": "sha256-Th+aCtv3XX2vnvsPgY9lcASZzQpvClsDu1U+z1WGpno=", + "owner": "doomemacs", + "repo": "doomemacs", + "rev": "35dc13632b3177b9efedad212f2180f69e756853", + "type": "github" + }, + "original": { + "owner": "doomemacs", + "repo": "doomemacs", + "type": "github" + } + }, + "emacs-overlay": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1709430482, + "narHash": "sha256-6DuA//PbOEMb3JjhbqYCMaLT6Y86r2TRvVEkKpDbU6Y=", + "owner": "nix-community", + "repo": "emacs-overlay", + "rev": "349cdc7ea8cbb285146a4ce42421ca15c956fb12", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "emacs-overlay", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1709336216, + "narHash": "sha256-Dt/wOWeW6Sqm11Yh+2+t0dfEWxoMxGBvv3JpIocFl9E=", + "path": "/nix/store/2hc9lg18zd6yabw9jqj0wy3s9kyvkzp0-source", + "rev": "f7b3c975cf067e56e7cda6cb098ebe3fb4d74ca2", + "type": "path" + }, + "original": { + "id": "flake-parts", + "type": "indirect" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1709126324, + "narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "d465f4819400de7c8d874d50b982301f28a84605", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1709237383, + "narHash": "sha256-cy6ArO4k5qTx+l5o+0mL9f5fa86tYUX3ozE1S+Txlds=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1536926ef5621b09bba54035ae2bb6d806d72ac8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "dir": "lib", + "lastModified": 1709237383, + "narHash": "sha256-cy6ArO4k5qTx+l5o+0mL9f5fa86tYUX3ozE1S+Txlds=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1536926ef5621b09bba54035ae2bb6d806d72ac8", + "type": "github" + }, + "original": { + "dir": "lib", + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1709309926, + "narHash": "sha256-VZFBtXGVD9LWTecGi6eXrE0hJ/mVB3zGUlHImUs2Qak=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "79baff8812a0d68e24a836df0a364c678089e2c7", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1709309926, + "narHash": "sha256-VZFBtXGVD9LWTecGi6eXrE0hJ/mVB3zGUlHImUs2Qak=", + "path": "/nix/store/3i3rncs75fid9hwai5p2nvwc4ngdnia7-source", + "rev": "79baff8812a0d68e24a836df0a364c678089e2c7", + "type": "path" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "doomemacs": "doomemacs", + "emacs-overlay": "emacs-overlay", + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs_2" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..8776e85 --- /dev/null +++ b/flake.nix @@ -0,0 +1,31 @@ +{ + inputs = { + flake-parts.url = "flake-parts"; + nixpkgs.url = "nixpkgs"; + doomemacs = { + url = "github:doomemacs/doomemacs"; + flake = false; + }; + emacs-overlay.url = "github:nix-community/emacs-overlay"; + }; + + outputs = inputs@{ self, doomemacs, nixpkgs, emacs-overlay, ... }: + inputs.flake-parts.lib.mkFlake { inherit inputs; } { + systems = [ "x86_64-linux" ]; + perSystem = { self', inputs', system, pkgs, lib, ... }: + let + common = { doomSource = doomemacs; emacs = pkgs.emacs29-pgtk; }; + in + { + # Current Doom + NixOS 23.11 requires emacs-overlay: Doom pins + # emacs-fish-completion, which moved from gitlab to github recently + # enough stable nixpkgs pulls it from the wrong source. + _module.args.pkgs = import nixpkgs { + inherit system; + overlays = [ emacs-overlay.overlays.package ]; + }; + packages.doom-minimal = pkgs.callPackage ./package.nix common; + packages.doom-full = pkgs.callPackage ./package.nix (common // { full = true; }); + }; + }; +} diff --git a/org-contrib-ob-stata-ess-optional.patch b/org-contrib-ob-stata-ess-optional.patch new file mode 100644 index 0000000..b8f25cf --- /dev/null +++ b/org-contrib-ob-stata-ess-optional.patch @@ -0,0 +1,66 @@ +From b2c5460598c7d91a66bd87ab9b6e9c0c442c9210 Mon Sep 17 00:00:00 2001 +From: Ihor Radchenko +Date: Sat, 13 Jan 2024 12:45:46 +0100 +Subject: [PATCH] lisp/ob-stata.el: Keep ESS optional to pacify compiler + +* lisp/ob-stata.el (ess-custom): Remove require. We cannot declare +ESS in common org-contrib dependencies. +(org-babel-stata-command): Change default value to nil, setting it to +`org-babel-stata-command' only after 'ess-custom is loaded. +(org-babel-stata-evaluate-external-process): Demand ess-custom during +runtime. +(ess-eval-visibly-p): Add variable declaration. +--- + lisp/ob-stata.el | 13 +++++++++++-- + 1 file changed, 11 insertions(+), 2 deletions(-) + +diff --git a/lisp/ob-stata.el b/lisp/ob-stata.el +index b7568e2..5956d2f 100644 +--- a/lisp/ob-stata.el ++++ b/lisp/ob-stata.el +@@ -42,7 +42,6 @@ + ;;; Code: + (require 'ob) + (require 'cl-lib) +-(require 'ess-custom) ; for `inferior-STA-program' + + (declare-function orgtbl-to-csv "org-table" (table params)) + (declare-function stata "ext:ess-stata" (&optional start-args)) +@@ -67,12 +66,20 @@ + ;; only ':results output' currently works, so make that the default + (defvar org-babel-default-header-args:stata '((:results . "output"))) + +-(defcustom org-babel-stata-command inferior-STA-program ++(defcustom org-babel-stata-command nil + "Name of command to use for executing stata code." + :group 'org-babel + :version "24.4" + :package-version '(Org . "8.3") + :type 'string) ++;; FIXME: Arrange the default value to be set without byte-compiler ++;; complaining. A proper fix would be putting this file into a ++;; separate package and adding ESS to package requires. Not possible ++;; while it is a part of org-contrib. ++(defvar inferior-STA-program) ++(eval-after-load 'ess-custom ++ (unless org-babel-stata-command ++ (setq org-babel-stata-command inferior-STA-program))) + + (defvar ess-local-process-name) ; dynamically scoped + (defun org-babel-edit-prep:stata (info) +@@ -242,6 +249,7 @@ current code buffer." + If RESULT-TYPE equals \\='output then return standard output as a + string. If RESULT-TYPE equals \\='value then return the value of the + last statement in BODY, as elisp." ++ (require 'ess-custom) + (cl-case result-type + (value + (let ((tmp-file (org-babel-temp-file "stata-"))) +@@ -258,6 +266,7 @@ last statement in BODY, as elisp." + column-names-p))) + (output (org-babel-eval org-babel-stata-command body)))) + ++(defvar ess-eval-visibly-p) + (defun org-babel-stata-evaluate-session + (session body result-type result-params column-names-p _row-names-p) + "Evaluate BODY in SESSION. diff --git a/package.nix b/package.nix new file mode 100644 index 0000000..f4c084a --- /dev/null +++ b/package.nix @@ -0,0 +1,284 @@ +{ + /* Your init.el. */ + doomInitFile ? null, + /* Your packages.el. */ + doomPrivateModule ? null, + /* Doom source tree. */ + doomSource, + /* Emacs package to build against. */ + emacs, + /* Whether to enable all default dependencies. Primarily useful for CI / + testing. */ + full ? false, + + autoreconfHook, + emacsPackagesFor, + fetchFromGitHub, + git, + lib, + linkFarm, + makeWrapper, + runCommand, + runtimeShell, + texliveBasic, +}: +let + inherit (lib) optional optionalAttrs optionalString; + + # Step 1: determine which Emacs packages to pull in. + # + # Inputs: unpatched Doom, a DOOMDIR with the provided init.el and packages.el. + # Outputs: + # - Packages Doom normally loads using Straight (as json) + # - modified packages.el that claims all packages are system-installed + # + # Uses Doom's CLI framework, which does not require anything else is installed + # (not even straight). + initialDoomDir = linkFarm "minimal-doom-dir" ( + [{ name = "cli.el"; path = ./cli.el; }] + ++ optional (doomInitFile != null) { name = "init.el"; path = doomInitFile; } + ++ optional (doomPrivateModule != null) { name = "packages.el"; path = doomPrivateModule; } + ); + # Set DOOMLOCALDIR somewhere harmless to stop Doom from trying to create it + # somewhere read-only. + + # (If this step breaks, add DEBUG=1 to make Doom more verbose.) + + # XXX this may need to be runCommandLocal just in case conditionals an init.el + # / packages.el evaluate differently on build systems. + doomIntermediates = runCommand "doom-intermediates" + { + env = { + EMACS = lib.getExe emacs; + DOOMDIR = initialDoomDir; + }; + } '' + mkdir $out + export DOOMLOCALDIR=$(mktemp -d) + ${runtimeShell} ${doomSource}/bin/doom dump-for-nix-build \ + ${optionalString full "--full"} -o $out + ''; + + doomPackageSet = lib.importJSON "${doomIntermediates}/packages.json"; + + # URLs for a few packages used by Doom that have straight recipes but are not + # in nixpkgs. + extraUrls = { + # Straight recipe from el-get + font-lock-ext = "https://github.com/sensorflo/font-lock-ext.git"; + sln-mode = "https://github.com/sensorflo/sln-mode.git"; + # Straight recipe from emacsmirror-mirror + nose = "https://github.com/emacsattic/nose.git"; + # In nixpkgs, but uses codeberg, for which nixpkgs uses fetchzip. + # TODO: consider parsing origEPkg.src.url instead. + spell-fu = "https://codeberg.org/ideasman42/emacs-spell-fu.git"; + tree-sitter-indent = "https://codeberg.org/FelipeLema/tree-sitter-indent.el.git"; + undo-fu = "https://codeberg.org/ideasman42/emacs-undo-fu.git"; + undo-fu-session = "https://codeberg.org/ideasman42/emacs-undo-fu-session.git"; + visual-fill-column = "https://codeberg.org/joostkremers/visual-fill-column.git"; + }; + + doomEmacsPackages = (emacsPackagesFor emacs).overrideScope ( + eself: esuper: + let + customPackages = { + # Doom uses using emacs-straight/auctex, which still contains parts of + # upstream's build system but does not contain all .in files, resulting + # in a failed build if we attempt to use upstream's configure.. + auctex = esuper.trivialBuild { + pname = "auctex"; + version = "1"; + meta = { + description = "build auctex from emacs-straight for Doom"; + }; + # TODO: figure out why this is necessary (there may be a better + # solution). + preBuild = '' + export HOME=$(mktemp -d) + ''; + }; + # Doom lets Straight provide org-autoloads.el as an alternative for + # org-loaddefs.el, and manually generates org-version.el. + # I currently run Org's build system. + # TODO: pass in ORGVERSION / GITVERSION (or provide a .git dir). + org = esuper.trivialBuild { + pname = "org"; + version = "1"; + meta = { + description = "build org-mode from emacs-straight repo for Doom"; + }; + buildInputs = [ ]; + nativeBuildInputs = [ emacs makeWrapper ]; + # XXX this sticks stuff in $out/emacs/etc/org (datadir in default.mk) + # that probably needs to go somewhere else. + # Possibly same for $out/share/info/. + configurePhase = '' + echo "prefix = $out" > local.mk + echo "lispdir = $out/share/emacs/site-lisp/org" >> local.mk + make config + ''; + buildPhase = '' + runHook preBuild + make + runHook postBuild + ''; + installPhase = '' + runHook preInstall + make install + runHook postInstall + ''; + }; + org-contrib = esuper.trivialBuild { + pname = "org-contrib"; + version = "1"; + meta = { + description = "build org-contrib from emacsmirror for Doom"; + }; + # Apply upstream fix for hard dependency on ess-custom. + # Straight just seems to ignore the byte-compilation failure(?). + patches = [ ./org-contrib-ob-stata-ess-optional.patch ]; + # HACK around sources being in lisp/, which trivialBuild does not + # handle. Setting sourceDir would probably be more sane, but we + # need the original one for a patch to apply. + postPatch = '' + cd lisp + ''; + }; + sln-mode = esuper.trivialBuild { + pname = "sln-mode"; + version = "1"; + meta = { + description = "build sln-mode for doom with manual dependencies"; + }; + # Straight uses a recipe from el-get that specifiecs the font-lock-ext + # dependency. + buildInputs = [ eself.font-lock-ext ]; + }; + # Straight checks for git's presence at import time. + # We could probably get by with feeding it /bin/true or similar, + # but it seems not worth the risk. + straight = esuper.trivialBuild { + pname = "straight"; + version = "1"; + meta = { + description = "build straight with Git dependency added for Doom"; + }; + nativeBuildInputs = [ git ]; + }; + # Nix uses a Melpa recipe that assumes the upstream CMake repo layout. + # Doom uses emacsmirror and sets :files (:defaults "*"). + cmake-mode = esuper.trivialBuild { + pname = "cmake-mode"; + version = "1"; + meta = { + description = "build cmake-mode from emacsmirror for Doom"; + }; + }; + }; + + makePackage = name: p: + assert lib.asserts.assertEachOneOf + "keys for ${name}" + (lib.attrNames p) + [ "modules" "recipe" "pin" "type" ]; + assert (p ? type) -> lib.asserts.assertOneOf + "type of ${name}" + p.type + [ "core" ]; + let + origEPkg = esuper.${name} or null; + # We have to specialcase ELPA packages pinned by Doom: Straight mirrors / + # repackages them. Doom's pins assume that mirror is used (so we have to + # use it), and replacing the source in nixpkgs's derivation will not work + # (it assumes it gets a tarball as input). + + # TODO: check notmuch works correctly without notmuch-version.el + + isElpa = origEPkg != null && (origEPkg == esuper.elpaPackages.${name} or null || origEPkg == esuper.nongnuPackages.${name} or null); + epkg = + customPackages.${name} + or (if origEPkg != null && !(p ? pin && isElpa) + then origEPkg + else + assert lib.assertMsg + (isElpa || (p ? recipe && p ? pin) || extraUrls ? ${name}) + "${name}: not in epkgs, not elpa, no recipe or not pinned"; + # Assume we can safely ignore (pre-)build unless we're actually + # building our own package. + assert lib.assertMsg (!(p ? recipe.pre-build)) "${name}: pre-build not supported"; + assert lib.assertMsg (!(p ? recipe.build)) "${name}: build not supported"; + # epkgs.trivialBuild takes an attrset, it does not support + # mkDerivation's fixed-point evaluation (`finalAttrs`). + # If it did, the buildInputs calculation should use it. + esuper.trivialBuild ({ + pname = name; + # src gets added below. + # version is required, but some other packages in nixpkgs just set 1. + version = "1"; + meta = { + description = "trivial build for doom-emacs"; + }; + buildInputs = map (name: eself.${name}) reqlist; + } // optionalAttrs (p ? recipe.files && p.recipe.files != { defaults = "*"; }) { + # HACK: files can contain :defaults, which splices in defaults. + # If files starts with :defaults, the entire thing gets + # misinterpreted as a proplist when exported to json. + # This currently only happens for `(:defaults "*")`, which we can + # safely ignore (skipping a few excludes). + postUnpack = '' + filteredSrc=$PWD/filteredSrc + mkdir $filteredSrc + pushd $sourceRoot + cp -r ${builtins.toString p.recipe.files} $filteredSrc + popd + sourceRoot=$filteredSrc + ''; + })); + url = + if (p.recipe.host or "") == "github" && p ? recipe.repo + then "https://github.com/${p.recipe.repo}" + else epkg.src.gitRepoUrl + or (if isElpa then "https://github.com/emacs-straight/${name}" + else extraUrls.${name} + or (throw "${name}: cannot derive url from recipe ${p.recipe or ""}")); + # Use builtins.fetchGit instead of nixpkgs's fetchFromGitHub because + # fetchGit allows fetching a specific git commit without a hash. + src = builtins.fetchGit ( + { + inherit url; + rev = p.pin; + submodules = !(p.recipe.nonrecursive or false); + # TODO: might need to pull ref from derivation.src if we're not pulling it from p.recipe? + # Note Doom does have packages with pin + branch (or nonrecursive) set, + # expecting to inherit the rest of the recipe from Straight. + } // optionalAttrs (p ? recipe.branch) { ref = p.recipe.branch; } + // optionalAttrs (p ? recipe.depth) { shallow = p.recipe.depth == 1; } + ); + # Ignore dependency extraction errors because it fails for repos not + # containing a "proper" package (no -pkg.el, no file with the right magic + # header). These seem common enough to be not worth allowlisting. + reqfile = runCommand "${name}-deps" { } '' + ${lib.getExe emacs} -Q --batch --eval \ + "(progn + (require 'package) + (with-temp-buffer + (setq default-directory \"${src}\") + (dired-mode) + (let ((reqs (with-demoted-errors \"Extracting dependencies: %s\" (package-desc-reqs (package-dir-info))))) + (princ (json-encode (mapcar #'car (seq-remove (lambda (p) (apply #'package-built-in-p p)) reqs)))))))" \ + > $out + ''; + reqjson = lib.importJSON reqfile; + # json-encode encodes the empty list as null (nil), not []. + reqlist = if reqjson == null then [ ] else reqjson; + in + if p ? pin + then epkg.overrideAttrs { inherit src; } + else epkg; + in + lib.mapAttrs makePackage doomPackageSet + ); + + emacsWithPackages = doomEmacsPackages.emacsWithPackages (epkgs: (map (p: epkgs.${p}) (builtins.attrNames doomPackageSet))); +in +emacsWithPackages