I'm working on a pandoc filter that executes clojure within Code and CodeBlock ASTs (gated and in-line "verbatim" parts of the pandoc markdown). Its going pretty OK, but now I'd like to load any dependencies from the 'deps.edn' (should it exist) in the directory from which pandoc was called. I'm struggling to take contextless, repl-less clojure code and load-up deps.edn. any advice?
Carsten is right, the clojure cli tool loads the deps.edn file from the folder it's executed in. You'll control the deps.edn file by controlling that folder. If you want to change it, you'll need a script as your pandoc filter, which then calls the clojure clifrom your folder of choice.
I assume that "pandoc" calls "clojure xxx", correct ? If so it need to so setting the working directory to the dir where "deps.edn" is. Is it like that ?
that's right
in my examples, I can have deps.edn and the pandoc call in the same directory, so I don't think the problem is that I can't find deps.edn, but that I don't know what to do with deps.edn if/when I have it
"you" do nothing with deps.edn, the clojure CLI uses it to construc teh classpath (if exiting in 'current dir', which at the end he "caller of 'clojure' controls)
I wonder if clj-mcp-light might be of help here. I have used it with success before with pandoc filter system. https://github.com/bhauman/clojure-mcp-light
I can run these in my CodeBlocks
(require '[clojure.repl :refer :all]
'[clojure.string :as str]
)
but not this:
(require '[some.randomthingfromdeps :as harder])
What exactly did you do and what was the error?
Never worked with Pandoc, so no idea. But what does (-> (System/getProperty "java.class.path") (clojure.string/split #":")) output?
["../ clojure_pandoc_filter/projects/rcf/target/rcf.jar"]
And (System/getProperty "user.dir")?
/home/don/Desktop/git/delivery_kick_v_brooksherthis is the directory containing the deps.edn I'd like to recognize and sync
Oh, wait, brain farts - 1 AM here.
Looking up deps.edn is separate from the classpath. When you launch a REPL via clj and there's no cache, it's done in two steps:
1. Find deps.edn and compute the classpath
2. Launch the actual process with that classpath
So unless there's that step (1), nothing will compute the classpath.
So either you have to wrap pandoc with something that does (1) and then launches pandoc with the right classpath (assuming it's even possible), or you have to inject things into the classpath dynamically.
You might have some luck with clojure.repl.deps/sync-deps, but I don't know how it looks up deps.edn. I'd guess that it will work just as is, without you having to do anything. Just try calling (clojure.rep.deps/sync-deps).
thanks, I'll try sync-deps
I get a macro expanding error with sync-deps. I was hacking at the https://github.com/clojure/tools.deps/blob/master/src/main/clojure/clojure/tools/deps.clj earlier, but don't understand it well enough to get it working. I recognize "compute the classpath" from that. I was able to find a deps.edn, resolve the dependencies, get a map that looks like all the right stuff (maven paths, ect), but I don't know what to do with it...what's the end-game for making a dependency "here"
Thanks for the help. I did finally figure this out, I think. This lets me send arbitrary code to my pandoc filter (a jar file, who's ClassLoader is not dynamic).
(ns com.raw-digital.transformer.dynamic-deps
(:require
[clojure.tools.deps :as deps]
[clojure.tools.deps.edn :as dedn]
[ :as io]
[clojure.repl :refer [dir doc]]
[clojure.tools.deps.util.maven :as mvn]))
(defn resolve-arbitrary-deps!
"use standard maven repos to fetch any deps we don't already have"
[deps-edn-path]
(let [my-deps (dedn/read-deps deps-edn-path)]
(deps/resolve-deps (assoc my-deps :mvn/repos mvn/standard-repos) nil)))
(defn get-loader []
(let [current-loader (.. Thread currentThread getContextClassLoader)]
(if (instance? clojure.lang.DynamicClassLoader current-loader)
current-loader
(let [dyn (clojure.lang.DynamicClassLoader. )]
(.setContextClassLoader (Thread/currentThread) dyn)
dyn))))
(defn load-arbitrary-deps!
"load any deps.edn file into the current thread's classpath, as if it had been launched with that deps.edn"
[deps-edn-path]
(let [cp-roots (:classpath-roots (deps/calc-basis (dedn/read-deps deps-edn-path)))
loader (get-loader)]
(doseq [path cp-roots]
(.addURL loader (-> path io/as-file .toURI .toURL)))))
(defn use-deps! [deps-edn-path]
(do (resolve-arbitrary-deps! deps-edn-path)
(load-arbitrary-deps! deps-edn-path)))