#one development setup with guix and emacs project skeleton
Here is one development setup using guix as the package manager of everything.
Assumption:
The setup:
git repo where you put all your package definitionsput in your project repo the following skeleton:
.
|- guix.scm
|- <module.scm> -> /path/to/channel/and/module.scm
|- .envrc
|- .gitignore
|- .guix
|- modules -> /path/to/channel
and in guix.scm
(use-modules (guix)
(guix packages)
(<module the package is in>))
(package
(inherit <the package>)
(source (local-file (getcwd) #:recursive? #t)))
in .envrc
eval $(guix shell --search-paths -D -f guix.scm -L .guix/modules)
in .gitignore
lisp-xyz.scm
.envrc
.guix
and have the real package definition in <modules.scm>
;; ...... other stuff in the module
(define-public my-other-guix
(package
(name "my-other-guix")
(version "0.0.0")
(source
;; stub: overwritten in guix.scm for dev shell
;; here to make this evaluate
(local-file (getcwd) #:recursive? #t)
;; after publishing to online git hositing
;; (origin
;; (method git-fetch)
;; (uri (git-reference
;; (url "")
;; (commit version)))
;; (file-name (git-file-name name version))
;; (sha256
;; ;; guix download -g URL --commit version
;; ;; for the sha256 in base32
;; (base32 ""))
;; )
)
(build-system asdf-build-system/sbcl)
(inputs (list ))
(synopsis "")
(description "")
(home-page "")
(license expat)))
direnv allow this directory, and you should land in your developmentguix shell -D -f guix.scm -L .guix/modules should setup your dev environment for you just fine
And here is a small elisp bit that helps you make this setup
It is an interactive function, so hopefully it is intuitive enough:
the project is going to be at parent-dir/name, and you’ll select which module to put your package in and link to.
;; a skeleton is a list of entries;
;; each entry could be:
;; (dir "name" [entries])
;; (symlink "source" "path")
;; (file "name" "content")
;; (magit [entries]) initialize the following under dir, and then make dir a git repo with magit,
;; which would landing you in there.
(defun project-skeleton-realize (dir skeleton)
;; when the dir don't exist, create it.
(unless (f-exists? dir) (mkdir dir t))
(cl-flet ((file-name-concat (dir &rest components)
(if (or (s-prefix? "/" (car components))
(s-prefix? "~" (car components)))
(apply 'file-name-concat components)
(apply 'file-name-concat dir components))))
(cond
((consp (car skeleton))
(mapcar (lambda (skeleton) (project-skeleton-realize dir skeleton ))
skeleton))
((eq (car skeleton) 'dir)
(let ((newdir (file-name-concat dir (cadr skeleton))))
(mkdir newdir t)
(project-skeleton-realize newdir (cddr skeleton))))
((eq (car skeleton) 'file)
(with-temp-file (file-name-concat dir (cadr skeleton))
(insert (or (caddr skeleton) ""))))
((eq (car skeleton) 'file-append)
(f-append-text
(or (caddr skeleton) "")
'utf-8
(file-name-concat dir (cadr skeleton))))
((eq (car skeleton) 'symlink)
(f-symlink (f-expand (file-name-concat dir (cadr skeleton)))
(file-name-concat dir (caddr skeleton))))
((eq (car skeleton) 'magit)
(project-skeleton-realize dir (cdr skeleton))
(magit-init dir))
(t nil))))
(defcustom project-skeleton-guix-default-channel "~/Project/hermanhel-guix-channel/" "default central local guix channel")
(defcustom project-skeleton-default-parent-dir "~/Project/" "default project to put the project dir")
(defun project-skeleton-guix (parent-dir name namespace)
"create dir NAME under PARENT-DIR which contains the code for guix package NAME in
NAMESPACE (guile module) in your default channel"
(interactive (list (read-file-name "parent-dir: " project-skeleton-default-parent-dir)
(file-name-nondirectory (directory-file-name (read-file-name "project name: " project-skeleton-default-parent-dir)))
(->> (f-relative
(read-file-name "guile module for guix package: " project-skeleton-guix-default-channel)
project-skeleton-guix-default-channel)
(string-remove-suffix ".scm")
(string-remove-prefix ".") ;; in case it shows; in my test f-relative don't have .
(string-replace "/" " "))))
(let ((dir (file-name-concat parent-dir name)))
;; FEAT: overwriting existing dir; removing the original
(when (f-exists? dir)
(pcase (read-char-choice (format "directory %s exists; [o]verwrite, [s]uperimpose, [a]bort" dir) '("o" "s" "a") )
(?\o (delete-directory dir t))
(?\a (error "Aborting project skeleton realization: dir exists"))))
(let* ((namespace (or namespace ""))
(guix-package-str
(mapconcat #'identity
(list
(format "(define-public %s" name)
" (package"
(format " (name \"%s\")" name)
" (version \"0.0.0\")"
" (source"
" ;; stub: overwritten in guix.scm for dev shell"
" ;; here to make this evaluate"
" (local-file (getcwd) #:recursive? #t)"
" ;; after publishing to online git hositing"
" ;; (origin"
" ;; (method git-fetch)"
" ;; (uri (git-reference"
" ;; (url \"\")"
" ;; (commit version)))"
" ;; (file-name (git-file-name name version))"
" ;; (sha256"
" ;; ;; guix download -g URL --commit version"
" ;; ;; for the sha256 in base32"
" ;; (base32 \"\"))"
" ;; )"
" (build-system asdf-build-system/sbcl)"
" (inputs (list ))"
" (synopsis \"\")"
" (description \"\")"
" (home-page \"\")"
" (license expat)))")
"\n"))
(module-path (format "%s.scm"
(apply 'file-name-concat project-skeleton-guix-default-channel
(mapcar #'symbol-name (read (format "(%s)" namespace))))))
(module-exists? (f-exists? module-path)))
(project-skeleton-realize
dir
`(magit
(file
"guix.scm"
,(mapconcat #'identity
(list
"(use-modules (guix)"
" (guix packages)"
(format " (%s))" namespace)
"(package"
(format " (inherit %s)" name)
" (source (local-file (getcwd) #:recursive? #t)))")
"\n"))
,(if module-exists?
`(file-append
;; the real package def at module-path in the channel
,module-path
,guix-package-str
)
`(file
;; the real package def at module-path in the channel
,module-path
,(mapconcat #'identity
(list
(format "(define-module (%s)" namespace)
" #:use-module (guix packages)"
" #:use-module (guix git-download)"
" #:use-module (guix build-system asdf)"
" #:use-module (guix licenses)"
" #:use-module (gnu packages gl)"
" #:use-module (gnu packages lisp)"
" #:use-module (gnu packages lisp-xyz))"
guix-package-str)
"\n")))
(dir ".guix" (symlink ,project-skeleton-guix-default-channel "modules" ))
;; link the file here for ease of edit
(symlink ,module-path ,(file-name-nondirectory module-path))
(file ".envrc" "eval $(guix shell --search-paths -D -f guix.scm -L .guix/modules)")
(file ".gitignore" ,(mapconcat #'identity
(list (file-name-nondirectory module-path)
".envrc"
".guix")
"\n")))))))