diff options
author | Steve Purcell <steve@sanityinc.com> | 2020-06-20 15:24:47 +1200 |
---|---|---|
committer | Steve Purcell <steve@sanityinc.com> | 2020-06-20 15:54:57 +1200 |
commit | 8439afbe1e1fca1b64c91aac302764d465212bca (patch) | |
tree | e6110f0278d5d5355ba08270c08cc652657d8625 | |
parent | 49597c22188e20dc05a7dcf2cfbe6f12162a7ed1 (diff) | |
download | nixlib-8439afbe1e1fca1b64c91aac302764d465212bca.tar nixlib-8439afbe1e1fca1b64c91aac302764d465212bca.tar.gz nixlib-8439afbe1e1fca1b64c91aac302764d465212bca.tar.bz2 nixlib-8439afbe1e1fca1b64c91aac302764d465212bca.tar.lz nixlib-8439afbe1e1fca1b64c91aac302764d465212bca.tar.xz nixlib-8439afbe1e1fca1b64c91aac302764d465212bca.tar.zst nixlib-8439afbe1e1fca1b64c91aac302764d465212bca.zip |
Add emacsWithPackagesFromPackageRequires
This provides a mechanism for creating an Emacs closure that contains the runtime dependencies for a given Emacs package source file, by inspecting its Package-Requires header.
-rw-r--r-- | README.org | 26 | ||||
-rw-r--r-- | default.nix | 3 | ||||
-rw-r--r-- | elisp.nix | 25 | ||||
-rw-r--r-- | packreq.nix | 26 | ||||
-rw-r--r-- | parse.nix | 61 |
5 files changed, 115 insertions, 26 deletions
diff --git a/README.org b/README.org index 49c83ea5bc59..4fc855a90ae8 100644 --- a/README.org +++ b/README.org @@ -28,8 +28,12 @@ We also provide two attributes named =emacsGit-nox= and =emacsUnstable-nox= if you wish to have Emacs built without X dependencies. ** Extra library functionality -This overlay comes with an extra function to generate an Emacs closure from =use-package= declarations. -This is an abstraction on top of =emacsWithPackages=. +This overlay comes with extra functions to generate an Emacs closure +from various types of dependency declaration. (These are abstractions +on top of =emacsWithPackages=.) + +For example, =emacsWithPackagesFromUsePackage= adds packages which are required in a user's config via =use-package=: + #+BEGIN_SRC nix { environment.systemPackages = [ @@ -52,6 +56,24 @@ This is an abstraction on top of =emacsWithPackages=. } #+END_SRC +Similarly, =emacsWithPackagesFromPackageRequires= adds packages which +are declared in a =.el= package file's =Package-Requires= header, which +can be handy for CI purposes: + +#+BEGIN_SRC nix +... +let + emacsForCI = pkgs.emacsWithPackagesFromPackageRequires { + packageFile = builtins.readFile ./flycheck.el; + extraEmacsPackages = epkgs: [ + epkgs.package-lint + ]; + }; +pkgs.mkShell { + buildInputs = [ emacsForCI ]; +} +#+END_SRC + ** Usage of the overlay *** Latest master each rebuild diff --git a/default.nix b/default.nix index de2afeb70158..e4acaa83e658 100644 --- a/default.nix +++ b/default.nix @@ -82,7 +82,6 @@ let buildInputs = old.buildInputs ++ [ self.libgccjit ]; }); - in { inherit emacsGit emacsUnstable; @@ -106,6 +105,8 @@ in { emacsWithPackagesFromUsePackage = import ./elisp.nix { pkgs = self; }; + emacsWithPackagesFromPackageRequires = import ./packreq.nix { pkgs = self; }; + emacsPackagesFor = emacs: ( (super.emacsPackagesFor emacs).overrideScope'(eself: esuper: let diff --git a/elisp.nix b/elisp.nix index 23a7e5a36bf4..d1edb2699f4d 100644 --- a/elisp.nix +++ b/elisp.nix @@ -6,35 +6,14 @@ use-package declarations. { pkgs }: let - isStrEmpty = s: (builtins.replaceStrings [" "] [""] s) == ""; - - splitString = _sep: _s: builtins.filter - (x: builtins.typeOf x == "string") - (builtins.split _sep _s); - - stripComments = dotEmacs: let - lines = splitString "\n" dotEmacs; - stripped = builtins.map (l: - builtins.elemAt (splitString ";;" l) 0) lines; - in builtins.concatStringsSep " " stripped; - - parsePackages = dotEmacs: let - strippedComments = stripComments dotEmacs; - tokens = builtins.filter (t: !(isStrEmpty t)) (builtins.map - (t: if builtins.typeOf t == "list" then builtins.elemAt t 0 else t) - (builtins.split "([\(\)])" strippedComments)); - matches = builtins.map (t: - builtins.match "^use-package[[:space:]]+([A-Za-z0-9_-]+).*" t) tokens; - in builtins.map (m: builtins.elemAt m 0) - (builtins.filter (m: m != null) matches); - + parse = pkgs.callPackage ./parse.nix {}; in { config, extraEmacsPackages ? epkgs: [], package ? pkgs.emacs, override ? (epkgs: epkgs) }: let - packages = parsePackages config; + packages = parse.parsePackagesFromUsePackage config; emacsPackages = pkgs.emacsPackagesGen package; emacsWithPackages = emacsPackages.emacsWithPackages; in emacsWithPackages (epkgs: let diff --git a/packreq.nix b/packreq.nix new file mode 100644 index 000000000000..5e432201a51a --- /dev/null +++ b/packreq.nix @@ -0,0 +1,26 @@ +/* +Parse an emacs package file to derive packages from +Package-Requires declarations. +*/ + +{ pkgs }: +let + parse = pkgs.callPackage ./parse.nix { }; +in +{ packageFile +, extraEmacsPackages ? epkgs: [ ] +, package ? pkgs.emacs +, override ? (epkgs: epkgs) +}: +let + packages = parse.parsePackagesFromPackageRequires packageFile; + emacsPackages = pkgs.emacsPackagesGen package; + emacsWithPackages = emacsPackages.emacsWithPackages; +in +emacsWithPackages (epkgs: + let + overriden = override epkgs; + usePkgs = builtins.map (name: overriden.${name}) packages; + extraPkgs = extraEmacsPackages overriden; + in + [ overriden.use-package ] ++ usePkgs ++ extraPkgs) diff --git a/parse.nix b/parse.nix new file mode 100644 index 000000000000..9fbe7c747389 --- /dev/null +++ b/parse.nix @@ -0,0 +1,61 @@ +{ lib }: +let + isStrEmpty = s: (builtins.replaceStrings [ " " ] [ "" ] s) == ""; + + splitString = _sep: _s: builtins.filter + (x: builtins.typeOf x == "string") + (builtins.split _sep _s); + + parsePackagesFromPackageRequires = packageFile: + let + lines = splitString "\r?\n" packageFile; + requires = + lib.concatMapStrings + (line: + let match = builtins.match "^;;;* *[pP]ackage-[rR]equires *: *\\((.*)\\)" line; + in if match == null then "" else builtins.head match) + lines; + parseReqList = s: + let matchAndRest = builtins.match " *\\(? *([^ \"\\)]+)( +\"[^\"]+\" *\\))?(.*)" s; + in + if isStrEmpty s then + [ ] + else + if matchAndRest == null then + throw "Failed to parse package requirements list: ${s}" + else + [ (builtins.head matchAndRest) ] ++ (parseReqList (builtins.elemAt matchAndRest 2)); + in + parseReqList requires; + + stripComments = dotEmacs: + let + lines = splitString "\n" dotEmacs; + stripped = builtins.map + (l: + builtins.elemAt (splitString ";;" l) 0) + lines; + in + builtins.concatStringsSep " " stripped; + + parsePackagesFromUsePackage = dotEmacs: + let + strippedComments = stripComments dotEmacs; + tokens = builtins.filter (t: !(isStrEmpty t)) (builtins.map + (t: if builtins.typeOf t == "list" then builtins.elemAt t 0 else t) + (builtins.split "([\(\)])" strippedComments) + ); + matches = builtins.map + (t: + builtins.match "^use-package[[:space:]]+([A-Za-z0-9_-]+).*" t) + tokens; + in + builtins.map + (m: builtins.elemAt m 0) + (builtins.filter (m: m != null) matches); + +in +{ + inherit parsePackagesFromPackageRequires; + inherit parsePackagesFromUsePackage; +} |