Below you find an :override
advice for org-babel-tangle-collect-blocks
of org-mode 9.1.6 that allows lists of tangle file names instead of just one tangle file name.
The code of the advice is largely a copy of org-babel-tangle-collect-blocks
. Only the lines marked with Tobias
are modified or added.
That would make the modification a good pull request if someone is willing to submit it to org-mode.
The elisp code also contains an :around
advice for org-babel-tangle-single-src-block
that takes over if you tangle only the source block at point by calling org-babel-tangle
with prefix arg.
That advice is not so appropriate to derive a pull-request from it. Therefore, I give a short description here what it does:
The advice for org-babel-tangle-single-src-block
gets the source block info for the source block at point.
If the :tangle
header argument of the source block is not a list the old version of org-babel-tangle-single-src-block
is called.
Otherwise for each tangle file in the :tangle
list org-babel-get-src-block-info
is modified such that it returns that file as value of the :tangle
argument and org-babel-tangle-single-src-block
is called.
(defun org-babel-tangle-collect-blocks-handle-tangle-list (&optional language tangle-file)
"Can be used as :override advice for `org-babel-tangle-collect-blocks'.
Handles lists of :tangle files."
(let ((counter 0) last-heading-pos blocks)
(org-babel-map-src-blocks (buffer-file-name)
(let ((current-heading-pos
(org-with-wide-buffer
(org-with-limited-levels (outline-previous-heading)))))
(if (eq last-heading-pos current-heading-pos) (cl-incf counter)
(setq counter 1)
(setq last-heading-pos current-heading-pos)))
(unless (org-in-commented-heading-p)
(let* ((info (org-babel-get-src-block-info 'light))
(src-lang (nth 0 info))
(src-tfiles (cdr (assq :tangle (nth 2 info))))) ; Tobias: accept list for :tangle
(unless (consp src-tfiles) ; Tobias: unify handling of strings and lists for :tangle
(setq src-tfiles (list src-tfiles))) ; Tobias: unify handling
(dolist (src-tfile src-tfiles) ; Tobias: iterate over list
(unless (or (string= src-tfile "no")
(and tangle-file (not (equal tangle-file src-tfile)))
(and language (not (string= language src-lang))))
;; Add the spec for this block to blocks under its
;; language.
(let ((by-lang (assoc src-lang blocks))
(block (org-babel-tangle-single-block counter)))
(setcdr (assoc :tangle (nth 4 block)) src-tfile) ; Tobias:
(if by-lang (setcdr by-lang (cons block (cdr by-lang)))
(push (cons src-lang (list block)) blocks)))))))) ; Tobias: just ()
;; Ensure blocks are in the correct order.
(mapcar (lambda (b) (cons (car b) (nreverse (cdr b)))) blocks)))
(defun org-babel-tangle-single-block-handle-tangle-list (oldfun block-counter &optional only-this-block)
"Can be used as :around advice for `org-babel-tangle-single-block'.
If the :tangle header arg is a list of files. Handle all files"
(let* ((info (org-babel-get-src-block-info))
(params (nth 2 info))
(tfiles (cdr (assoc :tangle params))))
(if (null (and only-this-block (consp tfiles)))
(funcall oldfun block-counter only-this-block)
(cl-assert (listp tfiles) nil
":tangle only allows a tangle file name or a list of tangle file names")
(let ((ret (mapcar
(lambda (tfile)
(let (old-get-info)
(cl-letf* (((symbol-function 'old-get-info) (symbol-function 'org-babel-get-src-block-info))
((symbol-function 'org-babel-get-src-block-info)
`(lambda (&rest get-info-args)
(let* ((info (apply 'old-get-info get-info-args))
(params (nth 2 info))
(tfile-cons (assoc :tangle params)))
(setcdr tfile-cons ,tfile)
info))))
(funcall oldfun block-counter only-this-block))))
tfiles)))
(if only-this-block
(list (cons (cl-caaar ret) (mapcar #'cadar ret)))
ret)))))
(advice-add 'org-babel-tangle-collect-blocks :override #'org-babel-tangle-collect-blocks-handle-tangle-list)
(advice-add 'org-babel-tangle-single-block :around #'org-babel-tangle-single-block-handle-tangle-list)
The code is tested with `org-version` 9.1.6, `emacs-version` 25.1.50.2, and the following org-file.
The test case is also an example for setting up a list of tangle files for one source block.
#+BEGIN_SRC emacs-lisp :tangle '("/tmp/first.el" "/tmp/third.el")
(message "Some code to be tangled into first and third")
#+END_SRC
#+BEGIN_SRC emacs-lisp :tangle "/tmp/first.el"
(message "I am the first file.")
#+END_SRC
#+BEGIN_SRC emacs-lisp :tangle '("/tmp/first.el" "/tmp/second.el")
(message "I am the first or the second file.")
#+END_SRC
#+BEGIN_SRC emacs-lisp :tangle "/tmp/third.el"
(message "I am the third file.")
#+END_SRC
org-babel-tangle
. But maybe the program to be configured does not allow includes.