Skip to content
Browse files

Add nrepl-ritz-reload-project

This command will reload the project.clj file, compute a new classpath,
and reset the REPL to run with the new classpath. Creates a new
nrepl session, and clears all user namespaces.
  • Loading branch information...
1 parent 851ee17 commit d0646a679ea0bdc69b29f0787b554c7950d1b581 @hugoduncan hugoduncan committed
View
146 lein-ritz/src/leiningen/ritz_nrepl.clj
@@ -1,19 +1,13 @@
(ns leiningen.ritz-nrepl
"Start a nrepl session either with the current project or standalone."
- (:require clojure.main
- clojure.set
- [clojure.string :as string]
- [reply.main :as reply]
- [clojure.java.io :as io]
- [leiningen.core.eval :as eval]
- [leiningen.core.project :as project]
- [leiningen.trampoline :as trampoline]
- [clojure.tools.nrepl.ack :as nrepl.ack]
- [clojure.tools.nrepl.server :as nrepl.server]
- [leiningen.core.user :as user]
- [leiningen.core.classpath :as classpath]
- [leiningen.core.main :as main])
+ (:require
+ [clojure.string :as string]
+ [clojure.java.io :as io]
+ [leiningen.core.eval :as eval]
+ [leiningen.core.project :as project]
+ [clojure.tools.nrepl.server :as nrepl.server])
(:use
+ [clojure.tools.cli :only [cli]]
[leiningen.core.classpath :only [get-classpath]]))
(def nrepl-version "0.2.0-beta9")
@@ -29,39 +23,42 @@
(def ritz-profile {:dependencies '[[ritz/ritz-nrepl "0.4.0-SNAPSHOT"
:exclusions [org.clojure/clojure]]]})
-(def lein-project-profile {:dependencies '[[leiningen "2.0.0-preview8"]]})
+(def lein-project-profile {:dependencies '[[leiningen "2.0.0-preview10"]]})
-(def trampoline-profile {:dependencies '[[reply "0.1.0-beta9"
- :exclusions [org.clojure/clojure]]]})
-
-;; (def ^{:private true} jvm-jdwp-opts
-;; "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n")
-;;(update-in [:jvm-opts] conj jvm-jdwp-opts)
+(def classlojure-profile {:dependencies '[[classlojure "0.6.6"]]})
(defn- start-jpda-server
"Start the JPDA nrepl server. The JPDA nrepl server will start the user
project server."
[{{:keys [nrepl-middleware]} :repl-options :as project}
- host port ack-port {:keys [headless? debug?]}]
+ host port ack-port {:keys [log-level]}]
{:pre [project]}
(let [jpda-project (->
project
(project/merge-profiles
[ritz-profile lein-project-profile :jpda]))
+ vm-project (->
+ project
+ (dissoc :test-paths :source-paths :dependencies)
+ (project/merge-profiles
+ [ritz-profile classlojure-profile]))
user-project (->
project
- (project/merge-profiles [ritz-profile])
- ;; ritz.nrepl.exec should be separated
- )
+ (project/merge-profiles [ritz-profile]))
+ vm-classpath (vec (get-classpath vm-project))
user-classpath (vec (get-classpath user-project))
_ (require 'leiningen.ritz) ;; for add-hooks
server-starting-form
`(do
(ritz.nrepl/start-jpda-server
- ~host ~port ~ack-port
- ~(str (io/file (:target-path project) "repl-port"))
- ~(string/join ":" user-classpath)
- ~(vec (map #(list 'quote %) nrepl-middleware))))]
+ {:host ~host
+ :port ~port
+ :ack-port ~ack-port
+ :repl-port-path ~(str (io/file (:target-path project) "repl-port"))
+ :classpath ~(vec user-classpath)
+ :vm-classpath ~(vec vm-classpath)
+ :middleware ~(vec (map #(list 'quote %) nrepl-middleware))
+ :log-level ~log-level}))]
(eval/eval-in-project
jpda-project
server-starting-form
@@ -78,89 +75,22 @@ project server."
(-> project :repl-options :host)
"localhost"))
-(def lein-repl-server
- (delay (nrepl.server/start-server
- :host (repl-host nil)
- :handler (nrepl.ack/handle-ack nrepl.server/unknown-op))))
-
(defn- ack-port [project]
(when-let [p (or (System/getenv "LEIN_REPL_ACK_PORT")
(-> project :repl-options :ack-port))]
(Integer. p)))
-(defn options-for-reply [project & {:keys [attach port]}]
- (let [repl-options (:repl-options project)]
- (clojure.set/rename-keys
- (merge
- repl-options
- {:init (if-let [init-ns (or (:init-ns repl-options) (:main project))]
- `(do (require '~init-ns) (in-ns '~init-ns)
- ~(:init repl-options))
- (:init repl-options))}
- (cond
- attach
- {:attach (if-let [host (repl-host project)]
- (str host ":" attach)
- (str attach))}
- port
- {:port (str port)}
- :else
- {}))
- {:prompt :custom-prompt
- :init :custom-init})))
-
-(defn- trampoline-repl [project]
- (let [options (options-for-reply project :port (repl-port project))
- profiles [(:repl (user/profiles) profile) trampoline-profile]]
- (eval/eval-in-project
- (project/merge-profiles project profiles)
- `(reply.main/launch-nrepl ~options)
- '(require 'reply.main 'clojure.tools.nrepl.server 'complete.core))))
-
(defn ^:no-project-needed ritz-nrepl
- "Start a ritz repl session either with the current project or standalone.
-
-USAGE: lein ritz-nrepl
-This will launch an nREPL server behind the scenes
-that reply will connect to. If a :port key is present in
-the :repl-options map in project.clj, that port will be used for the
-server, otherwise it is chosen randomly. If you run this command
-inside of a project, it will be run in the context of that classpath.
-If the command is run outside of a project, it'll be standalone and
-the classpath will be that of Leiningen.
-
-USAGE: lein repl :headless
-This will launch an nREPL server and wait, rather than connecting reply to it.
-
-USAGE: lein repl :connect [host:]port
-Connects to the nREPL server running at the given host (defaults to localhost)
-and port."
- ([project]
- (if trampoline/*trampoline?*
- (trampoline-repl project)
- (let [prepped (promise)]
- (nrepl.ack/reset-ack-port!)
- (.start
- (Thread.
- (bound-fn []
- (start-jpda-server
- (and project (vary-meta project assoc :prepped prepped))
- (repl-host project) (repl-port project)
- (-> @lein-repl-server deref :ss .getLocalPort)
- {}))))
- (when project @prepped)
- (if-let [repl-port (nrepl.ack/wait-for-ack (-> project
- :repl-options
- (:timeout 30000)))]
- (reply/launch-nrepl (options-for-reply project :attach repl-port))
- (println "REPL server launch timed out.")))))
- ([project flag & opts]
- (case flag
- ":headless" (start-jpda-server
- project
- (repl-host project) (repl-port project)
- (ack-port project)
- {:headless? true :debug? true})
- ":connect" (do (require 'cemerick.drawbridge.client)
- (reply/launch-nrepl {:attach (first opts)}))
- (main/abort "Unrecognized flag:" flag))))
+ "Start a ritz repl session either with the current project or standalone."
+ [project & args]
+ (let [[{:keys [debug] :as opts} [port host]]
+ (cli args
+ ["-l" "--log-level" :default nil]
+ ["-f" "--port-file"])
+ opts (update-in opts [:log-level] #(when % (keyword %)))]
+ (start-jpda-server
+ project
+ (or host (repl-host project))
+ (or port (repl-port project))
+ (ack-port project)
+ opts)))
View
27 nrepl/elisp/nrepl-ritz.el
@@ -533,6 +533,33 @@ are supported:
(define-key
nrepl-interaction-mode-map (kbd "C-c C-c") 'nrepl-ritz-compile-expression)
+;;; Reload project.clj
+(defun nrepl-ritz-recreate-session-handler ()
+ (lambda (response)
+ (message "Requesting new session completed")
+ (nrepl-dbind-response response (id new-session)
+ (cond (new-session
+ (message "Reloaded.")
+ (setq nrepl-session new-session))))))
+
+(defun nrepl-ritz-recreate-session ()
+ (message "Requesting new session")
+ (nrepl-create-client-session (nrepl-ritz-recreate-session-handler)))
+
+(defun nrepl-ritz-reload-project ()
+ "Reload project.clj."
+ (interactive)
+ (nrepl-ritz-send-op-strings
+ "reload-project"
+ (nrepl-make-response-handler
+ (current-buffer)
+ (lambda (buffer description)
+ (message description))
+ (lambda (buffer out) (message out))
+ (lambda (buffer err) (message err))
+ (lambda (buffer) (with-current-buffer buffer
+ (nrepl-ritz-recreate-session))))
+ `()))
;;; # Minibuffer
(defvar nrepl-ritz-minibuffer-map
View
20 nrepl/project.clj
@@ -6,22 +6,4 @@
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/tools.nrepl "0.2.0-beta9"
:exclusions [org.clojure/clojure]]
- [ritz/ritz-debugger "0.4.2-SNAPSHOT"]]
- ;; :dummy [~(require 'clojure.tools.nrepl.server)
- ;; ~(alter-var-root
- ;; (ns-resolve 'clojure.tools.nrepl.server 'default-middlewares)
- ;; (constantly
- ;; [(ns-resolve 'clojure.tools.nrepl.middleware 'wrap-describe)
- ;; ;; (ns-resolve 'clojure.tools.nrepl.middleware.interruptible-eval
- ;; ;; 'interruptible-eval)
- ;; (ns-resolve
- ;; 'clojure.tools.nrepl.middleware.load-file 'wrap-load-file)
- ;; (ns-resolve 'clojure.tools.nrepl.middleware.session 'add-stdin)
- ;; (ns-resolve 'clojure.tools.nrepl.middleware.session 'session)]))]
- ;; :profiles
- ;; {:dev {:repl-options
- ;; {:nrepl-middleware
- ;; [ritz.nrepl.middleware.tracking-eval/wrap-source-forms
- ;; ritz.nrepl.middleware.tracking-eval/tracking-eval
- ;; ritz.nrepl.middleware.simple-complete/wrap-simple-complete]}}}
- )
+ [ritz/ritz-debugger "0.4.2-SNAPSHOT"]])
View
66 nrepl/src/ritz/nrepl.clj
@@ -7,6 +7,7 @@ store some extra info. We also unify the session id across the jpda and user
processes."
(:use
[clojure.stacktrace :only [print-cause-trace]]
+ [clojure.string :only [join]]
[clojure.tools.nrepl.server :only [start-server unknown-op]]
[clojure.tools.nrepl.middleware.interruptible-eval
:only [interruptible-eval]]
@@ -34,7 +35,7 @@ processes."
:only [add-pending-connection connection-for-session
promote-pending-connection rename-connection]]
[ritz.nrepl.debug-eval :only [debug-eval]]
- [ritz.nrepl.exec :only [read-msg]]
+ [ritz.nrepl.exec :only [read-msg-using-classloader]]
[ritz.nrepl.middleware.tracking-eval :only [wrap-source-forms]]
[ritz.nrepl.rexec :only [rexec]]
[ritz.nrepl.simple-eval :only [simple-eval]])
@@ -44,8 +45,6 @@ processes."
[ritz.jpda.jdi-clj :as jdi-clj]
[ritz.nrepl.pr-values :as pr-values]))
-;; (set-level :trace)
-
(add-connection-for-event-fn!
ritz.nrepl.connections/connection-for-event)
@@ -83,6 +82,15 @@ reference."
(trace "Message %s" msg)
(handler msg)))
+;;; ## block on startup
+(def server-ready (promise))
+
+(defn block-until-ready
+ [handler]
+ (fn [{:keys [id session] :as msg}]
+ @server-ready
+ (handler msg)))
+
;;; ## Connection middleware
;;; This looks up a connection map based on session id. If the session id is not
@@ -160,7 +168,7 @@ reference."
pr-values (pr-values/pr-values #{"jpda"})]
(-> unknown-op
rexec wrap-source-forms jpda-eval debug-eval pr-values connection
- log-message)))
+ log-message block-until-ready)))
;;; # Reply pump
;;; The reply pump takes all nREPL replies sent from the user process, and
@@ -203,10 +211,18 @@ connection is found in the connections map based on the session id."
[vm thread]
(loop []
(try
- (process-reply
- (jdi-clj/eval
- vm thread invoke-single-threaded
- `(read-msg)))
+ (let [reply (jdi-clj/eval
+ vm thread invoke-single-threaded
+ `(read-msg-using-classloader))]
+ (cond
+ (nil? reply) (do
+ (trace "reply-pump returned nil message")
+ (Thread/sleep 1000))
+
+ (= "ritz/release-read-msg" (:op reply))
+ (trace "reply-pump released")
+
+ :else (process-reply reply)))
(catch Exception e
(trace "Unexpected exception in reply-pump %s" e)
(clojure.stacktrace/print-cause-trace e)))
@@ -236,31 +252,33 @@ generate a name for the thread."
context (start-control-thread-body msg-thread-name))))))
(defn start-jpda-server
- [host port ack-port repl-port-path classpath middleware]
+ [{:keys [host port ack-port repl-port-path classpath vm-classpath
+ middleware log-level]}]
+ (when log-level
+ (set-level log-level))
(let [server (start-server
:bind "localhost" :port 0 :ack-port ack-port
:handler (debug-handler host port))
- vm (launch-vm classpath `@(promise))
+ vm (launch-vm (join ":" vm-classpath) `@(promise))
msg-thread (start-remote-thread vm "msg-pump")
vm (assoc vm :msg-pump-thread msg-thread)
_ (set-vm vm)
port (-> server deref ^java.net.ServerSocket (:ss) .getLocalPort)]
(add-exception-event-request vm)
(vm-resume vm)
- (start-reply-pump server vm)
(ritz.jpda.jdi-clj/control-eval
- vm
- `(do
- (require 'ritz.nrepl.handler 'ritz.nrepl.exec)
- (doseq [ns# ~(vec
- (map #(list `quote (symbol (namespace %))) middleware))]
- (require ns#))
- nil))
+ vm `(require 'ritz.nrepl.exec 'ritz.logging))
+ (when log-level
+ (ritz.jpda.jdi-clj/control-eval vm `(ritz.logging/set-level ~log-level)))
(ritz.jpda.jdi-clj/control-eval
- vm
- `(do
- (ritz.nrepl.exec/set-handler!
- (ritz.nrepl.handler/default-handler ~@middleware))
- nil))
+ vm `(ritz.nrepl.exec/set-middleware! ~(vec middleware)))
+ (ritz.jpda.jdi-clj/control-eval
+ vm `(ritz.nrepl.exec/set-classpath! ~(vec classpath)))
+ (when log-level
+ (ritz.jpda.jdi-clj/control-eval
+ vm `(ritz.nrepl.exec/set-log-level ~log-level)))
+ (start-reply-pump server vm)
+ (deliver server-ready nil)
(println "nREPL server started on port" port)
- (spit repl-port-path port)))
+ (when repl-port-path
+ (spit repl-port-path port))))
View
10 nrepl/src/ritz/nrepl/debug_eval.clj
@@ -14,7 +14,8 @@
[clojure.tools.nrepl.middleware.interruptible-eval :only [*msg*]]
[ritz.debugger.connection :only [bindings bindings-assoc!]]
[ritz.logging :only [trace]]
- [ritz.nrepl.middleware :only [args-for-map read-when]]))
+ [ritz.nrepl.middleware :only [args-for-map read-when]]
+ [ritz.nrepl.project :only [reload]]))
(defn evaluate
[{:keys [op code ns session transport] :as msg}]
@@ -125,6 +126,13 @@
(transport/send transport (response-for msg :value (args-for-map v)))
(transport/send transport (response-for msg :status :done)))
+ (= "reload-project" op)
+ (let [f #(transport/send transport (response-for msg :status :done))]
+ (reload connection)
+ (trace "reload-project done")
+ (f))
+
+
:else (handler msg))))
View
136 nrepl/src/ritz/nrepl/exec.clj
@@ -1,20 +1,144 @@
(ns ritz.nrepl.exec
"Execute commands using nrepl"
(:use
- [clojure.tools.nrepl.server :only [default-handler handle*]]
- [ritz.nrepl.transport :only [make-transport read-sent]]))
+ [clojure.tools.nrepl.server :only [handle*]]
+ [ritz.nrepl.handler :only [default-handler]]
+ [ritz.nrepl.transport :only [make-transport read-sent release-queue]]
+ [ritz.repl-utils.classloader
+ :only [configurable-classpath? eval-clojure has-classloader?]]
+ [ritz.repl-utils.clojure :only [feature-cond]]
+ [ritz.repl-utils.namespaces :only [namespaces-reset namespace-state]]
+ [ritz.logging :only [set-level trace]]))
-(defonce transport (make-transport {}))
-(defonce handler (atom (default-handler)))
+(defonce transport (atom nil)) ;(make-transport {})
+(defonce middleware (atom nil)) ;[]
+(defonce handler (atom nil)) ; (default-handler)
+(defonce namespaces (atom nil))
+(def wait-for-reinit (atom nil))
(defn set-handler!
[handler-fn]
(reset! handler handler-fn))
+(defn set-transport!
+ [t]
+ (reset! transport t))
+
+(defn current-transport
+ [] @transport)
+
(defn exec
[msg]
- (handle* msg @handler transport))
+ (trace "exec handle %s" msg)
+ (handle* msg @handler @transport))
(defn read-msg
[]
- (read-sent transport))
+ (if @transport
+ (read-sent @transport)
+ (Thread/sleep 1000)))
+
+(defn msg->cl
+ [msg]
+ (let [cl-kw (fn [kw] (keyword (.getName kw)))]
+ (zipmap (map cl-kw (keys msg)) (vals msg))))
+
+(defn exec-using-classloader
+ "Execute a msg using the classloader specified classpath if possible."
+ [msg]
+ (feature-cond
+ (configurable-classpath?)
+ (if (has-classloader?)
+ (eval-clojure
+ `(fn [msg#] (exec (msg->cl msg#))) msg)
+ (do
+ (trace "exec-using-classloader No classloader yet")
+ (Thread/sleep 1000)))
+ :else (exec msg)))
+
+(defn read-msg-using-classloader
+ "Read a message using the classloader specified classpath if possible."
+ []
+ (feature-cond
+ (configurable-classpath?)
+ (if (has-classloader?)
+ (do
+ (when-let [p @wait-for-reinit]
+ (trace "read-msg waiting for re-init")
+ @p
+ (trace "read-msg re-init received")
+ (reset! wait-for-reinit nil)) ; this helps debugging but intros a race
+ (eval-clojure `(try
+ (read-msg)
+ (catch Exception e#
+ (trace "Exception in read-msg %s" e#)))))
+ (do
+ (trace "read-msg-using-classloader No classloader yet")
+ (Thread/sleep 1000)))
+ :else (read-msg)))
+
+(defn eval-using-classloader
+ "Eval a form using the classloader specified classpath if possible."
+ [form]
+ (feature-cond
+ (configurable-classpath?)
+ (if (has-classloader?)
+ (eval-clojure form)
+ (do
+ (trace "eval-using-classloader No classloader yet")
+ (Thread/sleep 1000)))
+ :else (eval form)))
+
+(defn set-middleware!
+ [mw]
+ (reset! middleware mw))
+
+(defn reset-middleware!
+ []
+ (if (configurable-classpath?)
+ (do
+ (eval-clojure
+ `(do
+ (require 'ritz.nrepl.handler 'ritz.nrepl.exec)
+ (doseq [ns# ~(vec
+ (map
+ #(list `quote (symbol (namespace %)))
+ @middleware))]
+ (require ns#))
+ nil))
+ (eval-clojure
+ `(do
+ (set-handler! (default-handler ~@@middleware))
+ nil)))
+ (do
+ (require 'ritz.nrepl.handler 'ritz.nrepl.exec)
+ (doseq [ns (map (comp ns-name namespace) @middleware)]
+ (require ns))
+ (set-handler! (apply default-handler (map resolve @middleware)))
+ nil)))
+
+(defn set-classpath!
+ [files]
+ (when (configurable-classpath?)
+ (trace "set-classpath!/release queue")
+ (reset! wait-for-reinit (promise))
+ (when (has-classloader?)
+ (eval-clojure `(when (current-transport)
+ (release-queue (current-transport)))))
+ (trace "set-classpath!/reset namespace")
+ (if @namespaces
+ (namespaces-reset @namespaces)
+ (reset! namespaces (namespace-state)))
+ (trace "set-classpath!/set classpath")
+ (ritz.repl-utils.classloader/set-classpath! files)
+ (eval-clojure '(require 'ritz.nrepl.exec 'ritz.logging))
+ (trace "set-classpath!/set middleware")
+ (reset-middleware!)
+ (trace "set-classpath!/set transport")
+ (eval-clojure `(set-transport! (make-transport {})))
+ (deliver @wait-for-reinit nil)))
+
+(defn set-log-level
+ [level]
+ (set-level level)
+ (eval-clojure `(set-level ~level)))
View
23 nrepl/src/ritz/nrepl/project.clj
@@ -0,0 +1,23 @@
+(ns ritz.nrepl.project
+ "Project.clj functions"
+ (:require
+ [leiningen.core.project :as project])
+ (:use
+ [leiningen.core.classpath :only [get-classpath]]
+ [ritz.debugger.connection :only [vm-context]]
+ [ritz.logging :only [trace]]))
+
+
+(defn set-classpath!
+ [vm classpath]
+ (ritz.jpda.jdi-clj/control-eval
+ vm `(ritz.nrepl.exec/set-classpath! ~(vec classpath))))
+
+(defn reload
+ [connection]
+ (let [project (project/read)
+ classpath (get-classpath project)]
+ (trace "Resetting classpath to %s" (vec classpath))
+ (set-classpath!
+ (vm-context connection)
+ classpath)))
View
4 nrepl/src/ritz/nrepl/rexec.clj
@@ -3,7 +3,7 @@
(:use
[clojure.tools.nrepl.misc :only [response-for]]
[ritz.logging :only [trace]]
- [ritz.nrepl.exec :only [exec read-msg]])
+ [ritz.nrepl.exec :only [exec-using-classloader]])
(:require
[clojure.tools.nrepl.transport :as transport]
[ritz.jpda.jdi-clj :as jdi-clj]))
@@ -17,7 +17,7 @@
`(require 'ritz.nrepl.exec))
(jdi-clj/control-eval
context
- `(exec ~(dissoc msg :transport :ritz.nrepl/connection)))
+ `(exec-using-classloader ~(dissoc msg :transport :ritz.nrepl/connection)))
(catch Exception e
(let [root-ex (#'clojure.main/root-cause e)]
(when-not (instance? ThreadDeath root-ex)
View
7 nrepl/src/ritz/nrepl/transport.clj
@@ -7,7 +7,8 @@
(defprotocol ReadSent
"Adds a read-sent function for retrieving queued messages"
- (read-sent [_] "Return a sent message, or block"))
+ (read-sent [_] "Return a sent message, or block")
+ (release-queue [_] "Return a release message, as the queue is disappearing"))
(defrecord JpdaTransport [^LinkedBlockingQueue queue]
@@ -18,7 +19,9 @@
(.put queue msg))
ReadSent
(read-sent [this]
- (.take queue)))
+ (.take queue))
+ (release-queue [this]
+ (.offer queue {:op "ritz/release-read-msg"})))
(defn make-transport
View
2 repl-utils/dev-resources/ritz/repl_utils/test_namespace.clj
@@ -0,0 +1,2 @@
+(ns ritz.repl-utils.test-namespace
+ "A namespace used for testing")
View
60 repl-utils/src/ritz/repl_utils/classloader.clj
@@ -0,0 +1,60 @@
+(ns ritz.repl-utils.classloader
+ "Provide a classloader for a project and functions to use it if available.
+This depends on having classlojure on the classpath."
+ (:use
+ [clojure.java.io :only [file]]
+ [ritz.logging :only [trace]]
+ [ritz.repl-utils.clojure :only [feature-cond]]))
+
+
+(def classlojure-on-classpath true)
+
+(try
+ (use '[classlojure.core :only [eval-in eval-in* ext-classloader]])
+ (catch Exception e
+ (alter-var-root #'classlojure-on-classpath (constantly false))))
+
+(def ^{:private true :doc "The classloader"} cl (atom nil))
+
+(defn configurable-classpath?
+ []
+ classlojure-on-classpath)
+
+(defn has-classloader?
+ []
+ @cl)
+
+(defn absolute-filename [filename]
+ (.getPath (file filename)))
+
+(defn filename-to-url-string
+ [filename]
+ (str (.toURL (file filename))))
+
+(defn set-classpath!
+ "Set the ClassLoader to the specified files."
+ [files]
+ (feature-cond
+ classlojure-on-classpath
+ (let [loader (#'classlojure.core/url-classloader
+ (->>
+ files
+ (map absolute-filename)
+ (map filename-to-url-string))
+ ext-classloader)]
+ (.loadClass loader "clojure.lang.RT")
+ (eval-in* loader '(require 'clojure.main))
+ (reset! cl loader))
+ :else (throw (Exception. "Can not configure classpath"))))
+
+(defn classpath
+ []
+ (when-let [cl @cl] (.getURLs cl)))
+
+(defn eval-clojure
+ "Evaluate a closure form in a classloader with the specified paths."
+ [form & args]
+ (trace "eval-clojure %s %s" form (vec args))
+ (feature-cond
+ classlojure-on-classpath (apply eval-in @cl form args)
+ :else (throw (Exception. "Can not eval in configurable classpath"))))

0 comments on commit d0646a6

Please sign in to comment.
Something went wrong with that request. Please try again.