;;; nix-shell.el -- run nix commands in Emacs -*- lexical-binding: t -*-
|
|
|
|
;; Author: Matthew Bauer <mjbauer95@gmail.com>
|
|
;; Homepage: https://github.com/NixOS/nix-mode
|
|
;; Keywords: nix
|
|
;; Version: 1.4.0
|
|
|
|
;; This file is NOT part of GNU Emacs.
|
|
|
|
;;; Commentary:
|
|
|
|
;; To use this just run:
|
|
|
|
;; M-x RET nix-shell RET
|
|
|
|
;; This will give you some
|
|
|
|
;;; Code:
|
|
|
|
(require 'nix)
|
|
(require 'nix-instantiate)
|
|
(require 'nix-store)
|
|
|
|
;; Tell the byte compiler these are dynamically bound
|
|
(defvar woman-manpath)
|
|
(defvar Man-header-file-path)
|
|
(defvar irony-additional-clang-options)
|
|
(defvar eshell-path-env)
|
|
(defvar ffap-c-path)
|
|
|
|
(defgroup nix-shell nil
|
|
"All nix-shell options."
|
|
:group 'nix)
|
|
|
|
(defcustom nix-shell-inputs '(depsBuildBuild
|
|
depsBuildBuildPropagated
|
|
nativeBuildInputs
|
|
propagatedNativeBuildInputs
|
|
depsBuildTarget
|
|
depsBuildTargetPropagated)
|
|
"List of inputs to collect for nix-shell."
|
|
:type 'list
|
|
:group 'nix-shell)
|
|
|
|
(defcustom nix-shell-clear-environment nil
|
|
"Whether to clear the old ‘exec-path’ & environment.
|
|
Similar to ‘--pure’ argument in command line nix-shell."
|
|
:type 'boolean
|
|
:group 'nix-shell)
|
|
|
|
(defcustom nix-shell-auto-realise t
|
|
"Whether we can realise paths in the built .drv file."
|
|
:type 'boolean
|
|
:group 'nix-shell)
|
|
|
|
(defcustom nix-file nil
|
|
"Nix file to build expressions from.
|
|
Should only be set in dir-locals.el file."
|
|
:type 'stringp
|
|
:group 'nix-shell)
|
|
|
|
(defcustom nix-attr nil
|
|
"Nix attribute path to use.
|
|
Should only be set in dir-locals.el file."
|
|
:type 'stringp
|
|
:group 'nix-shell)
|
|
|
|
;;;###autoload
|
|
(defun nix-shell-unpack (file attr)
|
|
"Run Nix’s unpackPhase.
|
|
FILE is the file to unpack from.
|
|
ATTR is the attribute to unpack."
|
|
(interactive (list (nix-read-file) nil))
|
|
(unless attr (setq attr (nix-read-attr file)))
|
|
|
|
(nix-shell--run-phase "unpack" file attr))
|
|
|
|
(defun nix-read-attr (_)
|
|
"Get nix attribute from user."
|
|
(read-string "Nix attr: "))
|
|
|
|
(defun nix-read-file ()
|
|
"Get nix file from user."
|
|
(cond
|
|
(nix-file nix-file)
|
|
((file-exists-p "shell.nix") "shell.nix")
|
|
((file-exists-p "default.nix") "default.nix")
|
|
(t (read-file-name "Nix file: " nil "<nixpkgs>"))))
|
|
|
|
;;;###autoload
|
|
(defun nix-shell-configure (file attr)
|
|
"Run Nix’s configurePhase.
|
|
FILE is the file to configure from.
|
|
ATTR is the attribute to configure."
|
|
(interactive (list (nix-read-file) nil))
|
|
(unless attr (setq attr (nix-read-attr file)))
|
|
|
|
(nix-shell--run-phase "configure" file attr))
|
|
|
|
;;;###autoload
|
|
(defun nix-shell-build (file attr)
|
|
"Run Nix’s buildPhase.
|
|
FILE is the file to build from.
|
|
ATTR is the attribute to build."
|
|
(interactive (list (nix-read-file) nil))
|
|
(unless attr (setq attr (nix-read-attr file)))
|
|
|
|
(nix-shell--run-phase "build" file attr))
|
|
|
|
(defun nix-shell--run-phase (phase file attr)
|
|
"Get source from a Nix derivation.
|
|
PHASE phase to run.
|
|
FILE used for base of Nix expresions.
|
|
ATTR from NIX-FILE to get Nix expressions from."
|
|
(shell-command
|
|
(format "%s '%s' -A '%s' --run 'if [ -z \"$%sPhase\" ]; then eval %sPhase; else eval \"$%sPhase\"; fi' &"
|
|
nix-shell-executable
|
|
file attr phase phase phase)))
|
|
|
|
(declare-function flycheck-buffer "flycheck")
|
|
|
|
(defun nix-shell--callback (buffer drv)
|
|
"Run the nix-shell callback to setup the buffer.
|
|
The BUFFER to run in.
|
|
The DRV file to use."
|
|
(let* ((env (alist-get 'env drv))
|
|
(stdenv (alist-get 'stdenv env))
|
|
(system (alist-get 'system env))
|
|
(inputs (remove nil
|
|
(apply 'append
|
|
(mapcar (lambda (prop)
|
|
(split-string (alist-get prop env)))
|
|
nix-shell-inputs)))))
|
|
|
|
;; Prevent accidentally rebuilding the world.
|
|
(unless (file-directory-p stdenv)
|
|
(error
|
|
"Your stdenv at %s has not been built. Please run: nix-store -r %s"
|
|
stdenv stdenv))
|
|
|
|
;; Make sure this .drv file can actually be built here.
|
|
(unless (string= system (nix-system))
|
|
(error
|
|
"Your system (%s) does not match .drv’s build system (%s)"
|
|
(nix-system) system))
|
|
|
|
(with-current-buffer buffer
|
|
(when nix-shell-clear-environment
|
|
(setq-local exec-path nil)
|
|
(setq-local eshell-path-env "")
|
|
;; (setq-local process-environment nil)
|
|
)
|
|
|
|
(dolist (input inputs)
|
|
(when (and (not (file-directory-p input))
|
|
nix-shell-auto-realise)
|
|
(nix-store-realise input))
|
|
|
|
(let ((bin (expand-file-name "bin" input))
|
|
(man (expand-file-name "share/man" input))
|
|
(include (expand-file-name "include" input)))
|
|
(add-to-list 'exec-path bin)
|
|
(setq-local eshell-path-env
|
|
(format "%s:%s" bin eshell-path-env))
|
|
(add-to-list 'woman-manpath man)
|
|
(add-to-list 'ffap-c-path include)
|
|
(add-to-list 'Man-header-file-path include)
|
|
(add-to-list 'irony-additional-clang-options
|
|
(format "-I%s" include))))
|
|
|
|
(when (bound-and-true-p flycheck-mode)
|
|
(flycheck-buffer))
|
|
)))
|
|
|
|
(defun nix-shell-with-packages (packages &optional pkgs-file)
|
|
"Create a nix shell environment from the listed package.
|
|
PACKAGES a list of packages to use.
|
|
PKGS-FILE the Nix file to get the packages from."
|
|
(nix-instantiate-async (apply-partially 'nix-shell--callback
|
|
(current-buffer))
|
|
(nix-shell--with-packages-file packages pkgs-file)
|
|
))
|
|
|
|
(defun nix-shell--with-packages-file (packages &optional pkgs-file)
|
|
"Get a .nix file from the packages list.
|
|
PACKAGES to put in the .nix file.
|
|
PKGS-FILE package set to pull from."
|
|
(unless pkgs-file (setq pkgs-file "<nixpkgs>"))
|
|
(let ((nix-file (make-temp-file "nix-shell" nil ".nix")))
|
|
(with-temp-file nix-file
|
|
(insert (format "with import %s { };\n" pkgs-file))
|
|
(insert "runCommandCC \"shell\" {\n")
|
|
(insert " nativeBuildInputs = [\n")
|
|
(mapc (lambda (x) (insert (format " %s\n" x))) packages)
|
|
(insert " ];\n")
|
|
(insert "} \"\"\n"))
|
|
nix-file))
|
|
|
|
(defun nix-eshell-with-packages (packages &optional pkgs-file)
|
|
"Create an Eshell buffer that has the shell environment in it.
|
|
PACKAGES a list of packages to pull in.
|
|
PKGS-FILE a file to use to get the packages."
|
|
(let ((buffer (generate-new-buffer "*nix-eshell*")))
|
|
(pop-to-buffer-same-window buffer)
|
|
|
|
(setq-local nix-shell-clear-environment t)
|
|
|
|
(nix-shell--callback
|
|
(current-buffer)
|
|
(nix-instantiate
|
|
(nix-shell--with-packages-file packages pkgs-file) nil t))
|
|
|
|
(eshell-mode)
|
|
buffer))
|
|
|
|
(defun nix-eshell (file &optional attr)
|
|
"Create an Eshell buffer that has the shell environment in it.
|
|
FILE the .nix expression to create a shell for.
|
|
ATTR attribute to instantiate in NIX-FILE."
|
|
(interactive (list (nix-read-file) nil))
|
|
(unless attr (setq attr (nix-read-attr nix-file)))
|
|
|
|
(let ((buffer (generate-new-buffer "*nix-eshell*")))
|
|
(pop-to-buffer-same-window buffer)
|
|
|
|
(setq-local nix-shell-clear-environment t)
|
|
|
|
(nix-shell--callback
|
|
(current-buffer)
|
|
(nix-instantiate file attr t))
|
|
|
|
(eshell-mode)
|
|
buffer))
|
|
|
|
;;;###autoload
|
|
(defun nix-shell-with-string (string)
|
|
"A nix-shell emulator in Emacs from a string.
|
|
STRING the nix expression to use."
|
|
(let ((file (make-temp-file "nix-shell" nil ".nix")))
|
|
(with-temp-file file (insert string))
|
|
(nix-instantiate-async (apply-partially 'nix-shell--callback
|
|
(current-buffer))
|
|
file)))
|
|
|
|
;;;###autoload
|
|
(defun nix-shell (file &optional attr)
|
|
"A nix-shell emulator in Emacs.
|
|
FILE the file to instantiate.
|
|
ATTR an attribute of the Nix file to use."
|
|
(interactive (list (nix-read-file) nil))
|
|
(unless attr (setq attr (nix-read-attr file)))
|
|
|
|
(nix-instantiate-async (apply-partially 'nix-shell--callback
|
|
(current-buffer))
|
|
file attr))
|
|
|
|
(provide 'nix-shell)
|
|
;;; nix-shell.el ends here
|