3

I am writing a report in latex, many of which contain code blocks which will be rendered with asymptote.

Since all the scripts will be tangled into the same subdirectory asyscripts, I don't want to have to write code like this

#+BEGIN_SRC asy :tangle asyscripts/test.asy
write("Hello World!");
#+END_SRC

Specifying asyscripts every time is just too tedious. Any way I can specify globally which directory all the code blocks must be tangled into? (Each code block will be in general tangled into a different script. )

smilingbuddha
  • 1,131
  • 10
  • 26

2 Answers2

1

I could not find any header args to do this (I thought that :dir would be the one, but it does not make any difference in tangling). But you can use the post-tangle hook, org-babel-post-tangle-hook, to manipulate the tangled files after they are produced. Here is a test file:

#+PROPERTY: dest ./tangled/

* Link

* TODO foo

#+BEGIN_SRC asy :tangle test.asy
write("Hello World!");
#+END_SRC


#+BEGIN_SRC asy :tangle test2.asy
write("Good-bye World!");
#+END_SRC


* Code                                                                                                        :noexport:

#+begin_src emacs-lisp
  (setq ndk/tangle-dir (org-entry-get nil "dest" t))

  (defun ndk/org-babel-tangle-rename ()
    (let ((tangledir ndk/tangle-dir)
          (tanglefile (buffer-file-name)))
        (rename-file tanglefile tangledir t)))

  (add-hook 'org-babel-post-tangle-hook #'ndk/org-babel-tangle-rename)
#+end_src

The code consists of three things:

  • the setting of a global variable ndk/tangle-dir to the directory path where you want the tangled files to end up. You could hardwire this into the function, but I made it a bit more flexible by defining a global #+PROPERTY called dest whose value is the path (N.B. The path MUST end with a slash). We retrieve the value of the dest property with org-entry-get and set ndk/tangle-dir to that value.

  • a function that will be called through the hook on every file that is tangled. The function is run in a buffer that contains the tangled output. We retrieve the file name from the buffer, and then we call rename-file to move that file to the destination directory. The t argument means it's OK to move it there even if a file with the same name exists in that directory already.

  • adding the function above to the org-babel-post-tangle-hook.

If you create an Org mode file with the contents above, and create the destination directory, you should be able to open the Org mode file, C-c C-c on the source code block, C-c C-v C-t to tangle the two blocks in the foo section and then check out the contents of the destination directory: the two tangled files should be there.

You probably want to remove the function from the hook afterwards, otherwise all files you tangle will end up in that directory:

(remove-hook 'org-babel-post-tangle-hook #'ndk/org-babel-tangle-rename)

For another example of the use of this hook, see this recent question. As you can see, it's pretty flexible!

NickD
  • 27,023
  • 3
  • 23
  • 42
1

Have you tried adding in this Header property at the top of your org file? This is what I add to most of my org files that I want tangled to a specific directory.

#+PROPERTY: header-args:emacs-lisp :noweb yes :tangle emacs.d/init.el

just replace the emacs.d/init.el with the location of where you want your tangle files to be located. You can drop the ":emacs-lisp" and the ":noweb yes" if you don't need them. So to just recap, you can try just adding this to the top of your file:

#+PROPERTY: header-args  :tangle "asyscripts/test.asy"

and it should tangle your source blocks to that location. (I'm using emacs 25.3.1 and Org mode version 9.2.3 (9.2.3-13-g727c3f-elpa if that helps, I've been using this tag back from org mode version 9.1.10)

sarcastyx
  • 11
  • 3