# Created 2024-07-24 mer. 14:07
#+title: 7d.nz
#+author: Phil. Estival
* • [2024-07-11 jeu.] ob-tangle to multiple destinations                :org:
In org-mode, exporting a code block to a source file
calls a "tangle processor"[fn::knuth-1984] (see org#Extracting Source Code).

The ability to tangle to multiple destinations is a
very convenient way to manage cluster configurations,
with central configurations dispatched to multiple
remote systems.  There are many ways to approach this
problem, and this one works quite well.

The tangle process is a rather long and complex
operation: in short, there are noweb references,
variables, langages-specific hooks and possible
evaluations. Altogether they open a wide range
of possibilities.

The :tangle parameter is either global to the
composition of the document, gathering every blocks in
order to build one output file, or local to the
processed block.  That's this second case we're
interested in, with many configuration files described
in blocks next to comments and explanations.

Here is a modification of org-babel-tangle
introducing the argument :tangle-directory
that can accept a list.

It's displayed here as a diff not with the intent to be
applied as a patch, but to show the very little
differences required in order to get it working.

#+begin_src diff
  diff --git a/lisp/ob-tangle.el b/lisp/ob-tangle.el
  index c89763efa..c494571dc 100644
  --- a/lisp/ob-tangle.el
  +++ b/lisp/ob-tangle.el
  @@ -269,11 +269,20 @@ matching a regular expression."
         (when (equal arg '(16))
           (or (cdr (assq :tangle (nth 2 (org-babel-get-src-block-info 'no-eval))))
         (user-error "Point is not in a source code block"))))
  +            (dirs (cdr (assq :tangle-directory (nth 2 (org-babel-get-src-block-info)))))
        path-collector
  -            (source-file buffer-file-name))
  -     (mapc ;; map over file-names
  +      (source-file buffer-file-name))
  +
  +        (setq dirs (cl-case (type-of dirs)
  +                     (string (list dirs))
  +                     (cons dirs)
  +                     (symbol '(nil))))
  +
  +     (dolist (dir dirs) ; iterate the n-tangle group
  +          (progn
  +     (mapc ; map over directories
     (lambda (by-fn)
  -        (let ((file-name (car by-fn)))
  +        (let ((file-name (concat dir (car by-fn))))
         (when file-name
                  (let ((lspecs (cdr by-fn))
           (fnd (file-name-directory file-name))
  @@ -354,6 +363,7 @@ matching a regular expression."
     (if (equal arg '(4))
         (org-babel-tangle-single-block 1 t)
       (org-babel-tangle-collect-blocks lang-re tangle-file)))
  +        ))
    (message "Tangled %d code block%s from %s" block-counter
       (if (= block-counter 1) "" "s")
       (file-name-nondirectory
#+end_src

Calling org-babel-tangle with the universal argument
runs the tangle processor, not on the entire file, but
for the current block. The tangled output goes into the
designated group.

#+begin_example
  ,#+begin_src elisp :tangle-directory '("/tmp" "/-:cadiz:/tmp") :tangle /x/y :mkdirp t
    (org-babel-n-tangle '(4))
  ,#+end_src
#+end_example

In the above example the tangled outputs goes locally
to /tmp/x/y and to a remote host, to cadiz:/tmp/x/y using a default protocol.

Additionally, here is an alternative to
org-babel-detangle that can be used to start from
existing files.

#+begin_src elisp
  (defun org-babel-detangle-block ()
    "detangle current block"
    (interactive)
    (save-excursion
      (let ((source-code-file
             (cdr (assoc :tangle (nth 2 (org-babel-get-src-block-info))))))
        (when source-code-file
          (org-babel-update-block-body
           (with-temp-buffer
             (insert-file-contents source-code-file)
             (buffer-string)))))))
#+end_src

* Bibliography
** Knuth, D. E. - Literate Programming
  :PROPERTIES:...