# -*- mode: org -*- #+PROPERTY: header-args :noweb-prefix no :noweb no-export #+PROPERTY: header-args:cfg :eval yes :cache yes :exports code :wrap "src scheme :noweb-ref guix-file" :results replace * Intro [[file:./literate.txt][This file]] is a literate configuration of a Guix and Emacs system. Tangling it (=C-c C-v t=) produces =home.scm= , which could be then installed as usual. Using this template you'll be able to structure a =home-environment= definition in a way that is convenient to you. Besides regular =noweb= features, a new source code block language is implemented, allowing templated configuration files (see [[*templated files][example]] below). Emacs configuration is included in the =home-environment= as a =package= (see the [[*config package][definition]]), which is built by tangling this org file. A couple settings to ease the use in emacs: - set tangle parameters - eval and wrap =cfg= blocks automatically ** Header #+begin_src org ,#+PROPERTY: header-args :noweb-prefix no :noweb yes :eval no ,#+PROPERTY: header-args:elisp :tangle config.el ,#+PROPERTY: header-args:cfg :eval yes :cache yes :wrap "src text :noweb-ref guix-file" #+end_src ** Footer #+begin_src org # Local Variables: # eval: (add-hook 'after-save-hook (lambda () (org-babel-tangle nil nil "scheme")) 0 t) # eval: (add-hook 'org-babel-pre-tangle-hook #'org-babel-execute-buffer 0 t) # End: #+end_src * Template Main template which tangles to =home.scm=. #+begin_src org ,#+begin_src scheme :tangle home.scm #+end_src #+begin_src scheme :tangle home.scm (use-modules (gnu) (gnu services) (gnu home services) (gnu home services desktop) (guix gexp) (guix git-download) (guix build-system copy) (guix packages) (ice-9 receive)) (define (pkg x) (if (string? x) (receive (package out) (specification->package+output x) (if (string= "out" out) package (list package out))) x)) (home-environment (services (cons* (simple-service 'files home-files-service-type `((".guile" ,%default-dotguile) (".config/guix/channels.scm" ,(plain-file "channels.scm" (simple-format #f "~s" '(list <>)))) <>)) <> %base-home-services)) (packages (map pkg (list <>)))) #+end_src * Channels ** guix #+begin_src org ,#+begin_src scheme :noweb-ref guix-channel #+end_src #+begin_src scheme :noweb-ref guix-channel (channel (name 'guix) (url "https://git.guix.gnu.org/guix.git") (branch "master") (introduction (make-channel-introduction "9edb3f66fd807b096b48283debdcddccfea34bad" (openpgp-fingerprint "BBB0 2DDF 2CEA F6A8 0D1D E643 A2A0 6DF2 A33A 54FA")))) #+end_src * Example content ** package #+begin_src org ,#+begin_src scheme :noweb-ref guix-pkg #+end_src #+begin_src scheme :noweb-ref guix-pkg "emacs-next" "emacs-org" #+end_src ** service #+begin_src org ,#+begin_src scheme :noweb-ref guix-srv #+end_src #+begin_src scheme :noweb-ref guix-srv (service home-dbus-service-type) #+end_src ** simple files #+begin_src org ,#+begin_src scheme :noweb-ref guix-file #+end_src #+begin_src scheme :noweb-ref guix-file ("plain" ,(plain-file "file" "hello, world")) ("local" ,(local-file "." "file" #:recursive? #t)) #+end_src ** templated files A =cfg= source block with =:dest= parameter will produce a src_scheme{(<:dest> ,(mixed-text-file "cfg-file" ))} result, which when wrapped using =:wrap "src scheme :noweb-ref guix-file"=, would be weaved automatically in the main template. #+begin_src org ,#+begin_src cfg :dest "tmpl" #+end_src #+begin_src cfg :exports both :dest "tmpl" this template gets converted to mixed-text-file ANT unicode characters mark scheme code some-program = 🐜 (pkg "msmtp") 🐜/bin/msmtp #+end_src #+RESULTS[3caf69bcded9804564f98db6dc37ec85388f9459]: #+begin_src text :noweb-ref guix-file ("tmpl" ,(mixed-text-file "cfg-file" "this template gets converted to mixed-text-file\n" "ANT unicode characters mark scheme code\n" "some-program = " (pkg "msmtp") "/bin/msmtp")) #+end_src * Emacs config ** ob-cfg.el A helper script that renders =cfg= source code blocks. #+begin_src elisp :tangle ob-cfg.el ;; -*- lexical-binding: t; -*- (require 'rx) (defun org-babel-execute:cfg (body params) "Wrap BODY in a (mixed-text-file). If PARAMS contains :file, use its contents instead of BODY. If PARAMS contain :chmod, additionally wrap with (chmod-computed-file)." (let-alist params (when .:file (with-temp-buffer (insert-file-contents-literally .:file nil) (setf body (buffer-string)))) (concat (if .:dest (format "(%S\n," .:dest) "") (if .:chmod "(chmod-computed-file " "") "(mixed-text-file\n \"cfg-file\"\n" ;; split string into parts 'string-part🐜scheme-part🐜' ;; - output each line of 'string-part' quoted. ;; - output 'scheme-part' as is ;; - note: add an empty 'scheme-part' at the end of input string ;; so that the last 'string-part' is matched (replace-regexp-in-string (rx (minimal-match (group (zero-or-more anything)) ?🐜 (group (zero-or-more anything)) ?🐜)) (lambda (e) (let ((string-part (match-string 1 e)) (scheme-part (match-string 2 e))) (concat (if (string-empty-p string-part) "" (string-join (mapcar (lambda (l) (prin1-to-string l nil '((escape-newlines . t)))) (string-lines string-part nil t)) "\n")) scheme-part))) (concat body "🐜🐜") t t) ")" (if .:chmod (format " #o%o)" (org-babel-interpret-file-mode .:chmod)) "") (if .:dest ")" "")))) #+end_src ** config package #+name: self #+begin_src elisp :tangle no :exports none ;(file-name-nondirectory (buffer-file-name)) "literate.org" #+end_src #+begin_src org ,#+begin_src scheme :noweb-ref emacs-config :noweb yes #+end_src #+begin_src scheme :noweb-ref emacs-config :noweb yes (package (name "emacs-config") (version "0") (source (local-file "." "src" #:recursive? #t)) ;; you really should put the files in git and enable filtering ;; #:select? (git-predicate ".") (build-system copy-build-system) (arguments (list #:install-plan ''(("." "" #:include (".el"))) #:phases #~(modify-phases %standard-phases (add-before 'install 'tangle (lambda* (#:key inputs #:allow-other-keys) (substitute* "<>" (("\\(guix[/]pkg '([^ )]+)" _ pkg) (format #f "(concat ~s" (or (assoc-ref inputs pkg) (error (format #f "~a is required for emacs config but not present in inputs" pkg)))))) (invoke "emacs" "-Q" "-l" "ob-cfg.el" "--batch" "--eval" (simple-format #f "~s" '(progn (require 'ob-tangle) (setq org-confirm-babel-evaluate nil) (with-current-buffer (find-file-noselect "<>") (org-babel-tangle nil "early-init.el" "elisp"))))) (delete-file "<>")))))) (inputs (map pkg '(<>))) (native-inputs (map pkg '("emacs-org" "emacs-next-minimal"))) (description "emacs config") (home-page #f) (synopsis #f) (license #f)) #+end_src #+begin_src org ,#+begin_src scheme :noweb-ref guix-file #+end_src #+begin_src scheme :noweb-ref guix-file (".config/emacs" ,<>) #+end_src ** config parts #+begin_src org ,#+begin_src text :noweb-ref emacs-dep #+end_src #+begin_src text :noweb-ref emacs-dep "msmtp" #+end_src #+begin_src org ,#+begin_src elisp #+end_src #+begin_src elisp (load (concat user-emacs-directory "ob-cfg.el")) (require 'sendmail) (setq sendmail-program (guix/pkg 'msmtp "/bin/msmtp")) #+end_src