I have the following .dir-locals:
((nil . ((eval . (let ((project-root-dir (expand-file-name (locate-dominating-file default-directory ".dir-locals.el"))))
(defun tokenize-set-strings ()
"Parse the contents of res/set/string.set and process them into src/core/string.hpp tokens."
(let ((string-set-file (format "%s/res/set/string.set" project-root-dir))
(current-file (buffer-file-name (current-buffer)))
(target-file (format "%s/src/core/sstring.hpp" project-root-dir))
(tokenizer-prog (format "%s/bin/util/stokenizer" project-root-dir)))
(if (string= string-set-file current-file)
(if (not (file-exists-p target-file))
(error (format "%s does not exist." target-file))
(if (not (file-exists-p tokenizer-prog))
(error (format "Could not find tokenizer at %s." tokenizer-prog))
(shell-command (format "%s %s %s" tokenizer-prog target-file string-set-file)))))))
(add-hook 'after-save-hook 'tokenize-set-strings))))))
The idea here is that whenever I edit a project-specific string.set file, it will then run an external program called stokenizer once the file is saved. I don't want this functionality anywhere else.
The function on its own works fine, but when it's called via the hook, Emacs complains:
let: Symbol’s value as variable is void: project-root-dir
I'm not sure why it's not working, and would appreciate any tips. In the mean time, I get around this issue by calling binding project-root-dir an additional time, this time with let*, inside the function.
;;; -*- lexical-binding: t -*-at the top of your.dir-locals.elfile.M-x toggle-debug-on-error, open a file that will trigger the function and see if there is a backtrace produced. If yes, add it to your question.hack-local-variablesis not applied and your suggestion does not work. Dir-local settings are read andeval-ed. Theevalis executed inhack-one-local-variablewithout thelexicalargument. So, it createslambdas and notclosures. Maybe, backquoting would be a solution. Another strategy would be to use(eval '(let (...) ...) t), i.e.,evalwith non-nillexical-argument.