During my quest to draw battle diagrams for a certain novel, I stumbled across this LaTeX problem. Suppose we have a file directory in this form:
$ROOT
├ .latexmkrc
├ cv.tex
├ application.cls
├ applications
│ ├ grant-application.tex
│ └ fellowship-application.tex
└ build
and we want all the PDF output files to be in the build/
directory. If we want
to compile grant-application.tex
, we need the command
$ latexmk applications/grant-application.tex
However, the default behaviour in tex.el
is that it executes latexmk
in the
directory of the “master file”, i.e. applications/
. This is a problem since
this file does not contain application.cls
which we want to reference.
Moreover latexmk
sometimes cannot find .latexmkrc
when executed in
applications/
.
In order to solve this issue, we modify the 'TeX-master-file
and 'TeX-master-directory
functions before entering the 'TeX-command-run-all
function. This makes use of
function advising.
(defun custom/TeX-compile-in-root ()
"Execute ~TeX-command-run-all~ in the nearest ~.latexmkrc~ directory or
projectile project root."
(interactive)
(let*
((dir-latexmkrc
(projectile-locate-dominating-file default-directory ".latexmkrc"))
(dir-projectile (projectile-project-root))
(is-latexmk-child-of-projectile (string-match-p (regexp-quote dir-projectile) dir-latexmkrc))
(dir-work (cond
((and dir-latexmkrc is-latexmk-child-of-projectile) dir-latexmkrc)
(dir-projectile dir-projectile)
(t default-directory)))
(dir-current-rel (string-remove-prefix dir-work default-directory))
(file-current-rel (concat dir-current-rel (TeX-master-file))))
; Execute in shadowed ~default-directory~
(cl-labels ((replace-pwd () dir-work) (replace-file (&optional extension nondirectory ask) file-current-rel))
(advice-add 'TeX-master-directory :override #'replace-pwd)
(advice-add 'TeX-master-file :override #'replace-file)
(TeX-command-run-all nil)
(advice-remove 'TeX-master-file #'replace-file)
(advice-remove 'TeX-master-directory #'replace-pwd)
)))
When executed on grant-application.tex
, this replaces
TeX-master-file: grant-application
TeX-master-directory: $ROOT/applications
with
TeX-master-file: applications/grant-application
TeX-master-directory: $ROOT
and the problem is solved.
A yet more elegant way is to globally advice 'TeX-master-file
and 'TeX-master-directory'
. We make use of the Advice Combinators:
(defun custom/TeX-root-directory (start)
(let* (
(dir-latexmkrc (projectile-locate-dominating-file start ".latexmkrc"))
(dir-projectile (projectile-project-root))
(is-latexmk-child-of-projectile (and dir-projectile dir-latexmkrc
(string-match-p (regexp-quote dir-projectile) dir-latexmkrc))))
(cond
((and dir-latexmkrc is-latexmk-child-of-projectile) dir-latexmkrc)
(dir-projectile dir-projectile)
(t start))))
(defun custom/TeX-master-directory-filter (dir-master)
(custom/TeX-root-directory dir-master))
(defun custom/TeX-master-file-filter (file-master)
(concat
(string-remove-prefix (custom/TeX-root-directory default-directory) default-directory)
file-master
))
(advice-add 'TeX-master-directory :filter-return #'custom/TeX-master-directory-filter)
(advice-add 'TeX-master-file :filter-return #'custom/TeX-master-file-filter)
One caveat which could happen is if the subdirectory contains a .tex
file with the same name as the parent directory:
$ROOT
├ ...
├ cv.tex
├ applications
│ ├ cv.tex
│ └ ...
└ build
which would result in clashing PDF names in build/
.