13007957e5
The idea is to have an almost-automatic conversion from QuickLisp, the definitive Common Lisp package repository, to Nix. The benefit over just using lispPackages.quicklisp is automatic installation of non-Lisp dependencies from NixPkgs (and integration with Nix package management). The benefit over lispPackages for normal Lisp packages is packaging just a snapshot of QuickLisp which is known to be tested for version compatibility between libraries. There are some packages in lispPackages that are not from QuickLisp (for example, the installable wrapper of QuickLisp itself). My hope is to replace the rest with the expressions converted from QuickLisp. Note that the current commit is a mere addition.
125 lines
3.9 KiB
Common Lisp
125 lines
3.9 KiB
Common Lisp
; QuickLisp-to-Nix export
|
|
; Requires QuickLisp to be loaded
|
|
; Installs the QuickLisp version of all the packages processed (in the
|
|
; QuickLisp instance it uses)
|
|
|
|
(ql:quickload :cl-emb)
|
|
(ql:quickload :external-program)
|
|
(ql:quickload :cl-ppcre)
|
|
(ql:quickload :md5)
|
|
(ql:quickload :alexandria)
|
|
|
|
(defun nix-prefetch-url (url)
|
|
(let*
|
|
((stdout nil)
|
|
(stderr nil))
|
|
(setf
|
|
stdout
|
|
(with-output-to-string (so)
|
|
(setf
|
|
stderr
|
|
(with-output-to-string (se)
|
|
(external-program:run
|
|
"nix-prefetch-url"
|
|
(list url)
|
|
:search t :output so :error se)))))
|
|
(let*
|
|
((path-line (first (last (cl-ppcre:split (format nil "~%") stderr))))
|
|
(path (cl-ppcre:regex-replace-all "path is .(.*)." path-line "\\1")))
|
|
(list
|
|
:sha256 (first (cl-ppcre:split (format nil "~%") stdout))
|
|
:path path
|
|
:md5 (string-downcase
|
|
(format nil "~{~16,2,'0r~}"
|
|
(map 'list 'identity (md5:md5sum-file path))))))))
|
|
|
|
(defun system-data (system)
|
|
(ql:quickload system)
|
|
(let*
|
|
((asdf-system (asdf:find-system system))
|
|
(ql-system (ql-dist:find-system system))
|
|
(ql-release (ql-dist:release ql-system))
|
|
(url (ql-dist:archive-url ql-release))
|
|
(local-archive (ql-dist:local-archive-file ql-release))
|
|
(local-url (format nil "file://~a" (pathname local-archive)))
|
|
(archive-data
|
|
(progn
|
|
(ql-dist:ensure-local-archive-file ql-release)
|
|
(nix-prefetch-url local-url)))
|
|
(ideal-md5 (ql-dist:archive-md5 ql-release))
|
|
(file-md5 (getf archive-data :md5))
|
|
(raw-dependencies (asdf:system-depends-on asdf-system))
|
|
(dependencies (remove-if-not 'ql-dist:find-system raw-dependencies))
|
|
(deps (mapcar (lambda (x) (list :name x)) dependencies))
|
|
(name (string-downcase (format nil "~a" system)))
|
|
(description (asdf:system-description asdf-system))
|
|
(release-name (ql-dist:short-description ql-release))
|
|
(version (cl-ppcre:regex-replace-all
|
|
(format nil "~a-" name) release-name "")))
|
|
(assert (equal ideal-md5 file-md5))
|
|
(list
|
|
:system system
|
|
:description description
|
|
:sha256 (getf archive-data :sha256)
|
|
:url url
|
|
:md5 file-md5
|
|
:name name
|
|
:deps deps
|
|
:dependencies dependencies
|
|
:version version)))
|
|
|
|
(defmacro this-file ()
|
|
(or *compile-file-truename*
|
|
*load-truename*))
|
|
|
|
(defun nix-expression (system)
|
|
(cl-emb:execute-emb
|
|
(merge-pathnames #p"nix-package.emb" (this-file))
|
|
:env (system-data system)))
|
|
(defun nix-invocation (system)
|
|
(cl-emb:execute-emb
|
|
(merge-pathnames #p"invocation.emb" (this-file))
|
|
:env (system-data system)))
|
|
|
|
(defun systems-closure (systems)
|
|
(let*
|
|
((seen (make-hash-table :test 'equal)))
|
|
(loop
|
|
with queue := systems
|
|
with res := nil
|
|
while queue
|
|
for next := (pop queue)
|
|
for deps := (getf (system-data next) :dependencies)
|
|
unless (gethash next seen) do
|
|
(progn
|
|
(push next res)
|
|
(setf queue (append queue deps)))
|
|
do (setf (gethash next seen) t)
|
|
finally (return res))))
|
|
|
|
(defun ql-to-nix (target-directory)
|
|
(let*
|
|
((systems
|
|
(cl-ppcre:split
|
|
(format nil "~%")
|
|
(alexandria:read-file-into-string
|
|
(format nil "~a/quicklisp-to-nix-systems.txt" target-directory))))
|
|
(closure (systems-closure systems))
|
|
(invocations
|
|
(loop for s in closure
|
|
collect (list :code (nix-invocation s)))))
|
|
(loop
|
|
for s in closure
|
|
do (alexandria:write-string-into-file
|
|
(nix-expression s)
|
|
(format nil "~a/quicklisp-to-nix-output/~a.nix" target-directory s)
|
|
:if-exists :supersede))
|
|
(alexandria:write-string-into-file
|
|
(cl-emb:execute-emb
|
|
(merge-pathnames
|
|
#p"top-package.emb"
|
|
(this-file))
|
|
:env (list :invocations invocations))
|
|
(format nil "~a/quicklisp-to-nix.nix" target-directory)
|
|
:if-exists :supersede)))
|