181 lines
5.8 KiB
Common Lisp
181 lines
5.8 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 :alexandria)
|
|
(ql:quickload :md5)
|
|
|
|
(defvar testnames (make-hash-table :test 'equal))
|
|
|
|
(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 escape-filename (s)
|
|
(format
|
|
nil "~a~{~a~}"
|
|
(if (cl-ppcre:scan "^[a-zA-Z_]" s) "" "_")
|
|
(loop
|
|
for x in (map 'list 'identity s)
|
|
collect
|
|
(case x
|
|
(#\/ "_slash_")
|
|
(#\\ "_backslash_")
|
|
(#\_ "__")
|
|
(#\. "_dot_")
|
|
(t x)))))
|
|
|
|
(defun system-depends-on (system-name)
|
|
(labels
|
|
((decode (name)
|
|
(typecase name
|
|
(string
|
|
name)
|
|
(cons
|
|
(ecase (car name)
|
|
(:version (second name)))))))
|
|
(let* ((asdf-dependencies (asdf:system-depends-on (asdf:find-system system-name)))
|
|
(decoded-asdf-dependencies (mapcar #'decode asdf-dependencies))
|
|
(clean-asdf-dependencies (remove-if-not 'ql-dist:find-system decoded-asdf-dependencies))
|
|
(ql-dependencies (ql-dist:required-systems (ql-dist:find-system system-name)))
|
|
(all-dependencies (concatenate 'list clean-asdf-dependencies ql-dependencies))
|
|
(sorted-dependencies (sort all-dependencies #'string<))
|
|
(unique-dependencies (remove-duplicates sorted-dependencies :test #'equal)))
|
|
unique-dependencies)))
|
|
|
|
(defun system-data (system)
|
|
(let*
|
|
((asdf-system
|
|
(or
|
|
(ignore-errors (asdf:find-system system))
|
|
(progn
|
|
(ql:quickload system)
|
|
(asdf:find-system system))))
|
|
(ql-system (ql-dist:find-system system))
|
|
(ql-release (ql-dist:release ql-system))
|
|
(ql-sibling-systems (ql-dist:provided-systems ql-release))
|
|
(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 (system-depends-on system))
|
|
(name (string-downcase (format nil "~a" system)))
|
|
(ql-sibling-names
|
|
(remove name (mapcar 'ql-dist:name ql-sibling-systems)
|
|
:test 'equal))
|
|
(dependencies
|
|
(set-difference
|
|
(remove-duplicates
|
|
(remove-if-not 'ql-dist:find-system raw-dependencies)
|
|
:test 'equal)
|
|
ql-sibling-names
|
|
:test 'equal))
|
|
(deps (mapcar (lambda (x) (list :name x :filename (escape-filename x)))
|
|
dependencies))
|
|
(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
|
|
:testname (gethash name testnames)
|
|
:filename (escape-filename name)
|
|
:deps deps
|
|
:dependencies dependencies
|
|
:version version
|
|
:siblings ql-sibling-names)))
|
|
|
|
(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 old := (gethash next seen)
|
|
for data := (unless old (system-data next))
|
|
for deps := (getf data :dependencies)
|
|
for siblings := (getf data :siblings)
|
|
unless old 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)
|
|
(load (format nil "~a/quicklisp-to-nix-overrides.lisp" 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 (escape-filename 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)))
|