GithubHelp home page GithubHelp logo

virgil's Introduction

Do you tarnish your Clojure with the occasional hint of Java? Have you become indescribably tired of reloading your REPL every time you change anything with a .java suffix? Look no further.

Virgil is a library for live-recompiling Java classes from the REPL. This can be done either manually or by starting a process that watches your source directories for changes in Java files and triggering recompilation when that happens.

Usage

Add virgil/virgil dependency to your project.clj or deps.edn. If you plan to use Virgil just as a devtime dependency, then you probably want to add it to a profile/alias which you enable only during development.

(require 'virgil)
;; To recompile once, manually:
(virgil/compile-java ["src"])

;; To recompile automatically when files change:
(virgil/watch-and-recompile ["src"])

The main argument to these functions is a list of directories where Java source files are located. Both functions can accept a list of string :options that is passed to Java compiler, e.g. :options ["-Xlint:all"] to print compilation warnings, and a :verbose flag to print all classnames that got compiled.

watch-and-recompile accepts an optional :post-hook function. You can use it to, e.g., trigger tools.namespace refresh after the classes get recompiled.

Check example directory for a sample project.

Happy tarnishing.

Can I use Virgil in production?

Virgil can compile Java classes at runtime in a production environment the same way as it does during the development, so the answer is yes. However, when you do a release build, it is advised to build real Java classes explicitly during your build step using javac task of your build tool. There are multiple arguments for it:

  • You get extra reliability and assurance that the compiled Java classes will be correctly discoverable by other code.
  • You get one fewer runtime dependency.
  • You won't have to rely on JDK-specific tools like javax.tools package that might not be available in your production environment (e.g., if it runs on JRE).

Migration from 0.1.9

From version 0.3.0, Virgil no longer provides lein-virgil plugin for Leiningen. Instead, you should add virgil as a regular dependency to your project and call its functions from the REPL.

Supported versions

Virgil makes sure to support Clojure 1.10+ and JDK 8, 11, 17, 21, 22 (see CI job). Supporting future versions of Java so far required only bumping ASM library dependency, so that shouldn't take long. Please, create an issue if you run into any compatibility problems.

License

Copyright © 2016-2019 Zachary Tellman, 2022-2024 Oleksandr Yakushev

Distributed under the MIT License

virgil's People

Contributors

agilecreativity avatar aiba avatar alexander-yakushev avatar jumarko avatar radicalzephyr avatar reify-tanner-stirrat avatar schmir avatar slipset avatar smee avatar ztellman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

virgil's Issues

Unsupported class file major version 59

Hi - I get the following error using both java 15 and java 8:

recompiling all files in ["/home/alan/expr/demo/src/java"]
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by virgil.compile$get_java_compiler to constructor com.sun.tools.javac.api.JavacTool()
WARNING: Please consider reporting this to the maintainers of virgil.compile$get_java_compiler
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

recompiling all files in ["/home/alan/expr/demo/src/java"]
Unsupported class file major version 59
Syntax error (IllegalArgumentException) compiling at (/tmp/form-init16056319481210472696.clj:1:74).
Unsupported class file major version 59

Full report at:
/tmp/clojure-5914386959178799319.edn
Tests failed.
( lein do clean, test; )  14.22s user 0.61s system 254% cpu 5.831 total



~/expr/demo > cat /tmp/clojure-5914386959178799319.edn
{:clojure.main/message
 "Syntax error (IllegalArgumentException) compiling at (/tmp/form-init16056319481210472696.clj:1:74).\nUnsupported class file major version 59\n",
 :clojure.main/triage
 {:clojure.error/phase :compile-syntax-check,
  :clojure.error/line 1,
  :clojure.error/column 74,
  :clojure.error/source "form-init16056319481210472696.clj",
  :clojure.error/path "/tmp/form-init16056319481210472696.clj",
  :clojure.error/class java.lang.IllegalArgumentException,
  :clojure.error/cause "Unsupported class file major version 59"},
 :clojure.main/trace
 {:via
  [{:type clojure.lang.Compiler$CompilerException,
    :message
    "Syntax error compiling at (/tmp/form-init16056319481210472696.clj:1:74).",
    :data
    {:clojure.error/phase :compile-syntax-check,
     :clojure.error/line 1,
     :clojure.error/column 74,
     :clojure.error/source "/tmp/form-init16056319481210472696.clj"},
    :at [clojure.lang.Compiler load "Compiler.java" 7652]}
   {:type java.lang.IllegalArgumentException,
    :message "Unsupported class file major version 59",
    :at
    [org.objectweb.asm.ClassReader <init> "ClassReader.java" 184]}],
  :trace
  [[org.objectweb.asm.ClassReader <init> "ClassReader.java" 184]
   [org.objectweb.asm.ClassReader <init> "ClassReader.java" 166]
   [org.objectweb.asm.ClassReader <init> "ClassReader.java" 152]
   [virgil.decompile$parents invokeStatic "decompile.clj" 12]
   [virgil.decompile$parents invoke "decompile.clj" 12]
   [virgil.decompile$rank_order$parents__825 invoke "decompile.clj" 24]
   [clojure.core$map$fn__5884 invoke "core.clj" 2759]
   [clojure.lang.LazySeq sval "LazySeq.java" 42]
   [clojure.lang.LazySeq seq "LazySeq.java" 51]
   [clojure.lang.RT seq "RT.java" 535]
   [clojure.core$seq__5419 invokeStatic "core.clj" 139]
   [clojure.core$zipmap invokeStatic "core.clj" 6570]
   [clojure.core$zipmap invoke "core.clj" 6570]
   [virgil.decompile$rank_order invokeStatic "decompile.clj" 25]
   [virgil.decompile$rank_order invoke "decompile.clj" 18]
   [virgil.compile$compile_java invokeStatic "compile.clj" 124]
   [virgil.compile$compile_java invoke "compile.clj" 119]
   [virgil.compile$compile_all_java invokeStatic "compile.clj" 161]
   [virgil.compile$compile_all_java invoke "compile.clj" 149]
   [virgil$watch$recompile__951 invoke "virgil.clj" 44]
   [virgil$watch invokeStatic "virgil.clj" 61]
   [virgil$watch doInvoke "virgil.clj" 41]
   [clojure.lang.RestFn invoke "RestFn.java" 408]
   [user$eval964 invokeStatic "form-init16056319481210472696.clj" 1]
   [user$eval964 invoke "form-init16056319481210472696.clj" 1]
   [clojure.lang.Compiler eval "Compiler.java" 7181]
   [clojure.lang.Compiler eval "Compiler.java" 7170]
   [clojure.lang.Compiler load "Compiler.java" 7640]
   [clojure.lang.Compiler loadFile "Compiler.java" 7578]
   [clojure.main$load_script invokeStatic "main.clj" 475]
   [clojure.main$init_opt invokeStatic "main.clj" 477]
   [clojure.main$init_opt invoke "main.clj" 477]
   [clojure.main$initialize invokeStatic "main.clj" 508]
   [clojure.main$null_opt invokeStatic "main.clj" 542]
   [clojure.main$null_opt invoke "main.clj" 539]
   [clojure.main$main invokeStatic "main.clj" 664]
   [clojure.main$main doInvoke "main.clj" 616]
   [clojure.lang.RestFn applyTo "RestFn.java" 137]
   [clojure.lang.Var applyTo "Var.java" 705]
   [clojure.main main "main.java" 40]],
  :cause "Unsupported class file major version 59",
  :phase :compile-syntax-check}}

Windows file separators causing URISyntaxException

I have a Java file called abc.java located at src/misc/abc.java and src is added to my :java-source-paths. When starting the REPL I receive:

Exception in thread "main" java.lang.IllegalArgumentException: Illegal character in path at index 10: string:///\misc\abc.java, compiling: ...
...
Caused by: java.lang.IllegalArgumentException: Illegal character in path at index 10: string:///\misc\abc.java
	at java.net.URI.create(URI.java:852)
	at virgil.compile$source_object.invokeStatic(compile.clj:33)
	at virgil.compile$source_object.invoke(compile.clj:30)
	at virgil.compile$source__GT_bytecode$fn__1816.invoke(compile.clj:68)```
...
Caused by: java.net.URISyntaxException: Illegal character in path at index 10: string:///\misc\abc.java
	at java.net.URI$Parser.fail(URI.java:2848)
	at java.net.URI$Parser.checkChars(URI.java:3021)
	at java.net.URI$Parser.parseHierarchical(URI.java:3105)
	at java.net.URI$Parser.parse(URI.java:3053)
	at java.net.URI.<init>(URI.java:588)
	at java.net.URI.create(URI.java:850)

The \ appears to be the issue. When I remove Virgil from the plugins the error disappears. I'm running Windows 10 and using Virgil 0.1.6.

defonce does not persist values across reloads

Once virgil notices a change on a .java file it recompiles everything on the folder and reloads every namespace.
Any value inside a defonce is lost.

I was wondering what's would be a good solution here?

Other than "fixing" the issue (I assume would require additional machinery), other options that can think;

  • Supplying a list of namespaces that shouldn't be reloaded when recompiling.
  • Supplying some sort of on-stop/on-start method per namespace (like shadow-cljs or figwheel do) such that I can clear/reset the value (in my case it is holding an atom for the PID of a server, the server should be stopped before the NS is reloaded).

Let me know your thoughts!

`lein with-profile` with lein-virgil 0.1.7 causes errors about unbound vars

Hi Zach,

We have a problem when using Virgil 0.1.7 with lein with-profile. In jsonista, when we do lein with-profile dev test, we get a bunch of errors that look like this:


recompiling all files in ["/Users/miikka/code/jsonista/src/java"]
:reloading (jsonista.core jsonista.test-utils jsonista.json-perf-test jsonista.core-test)

ERROR in () (Var.java:43)
expected: (= {"hello" "world"} (-> data jsonista/write-value-as-string jsonista/read-value))
  actual: java.lang.IllegalStateException: Attempting to call unbound fn: #'jsonista.core/write-value-as-string
 at clojure.lang.Var$Unbound.throwArity (Var.java:43)
    clojure.lang.AFn.invoke (AFn.java:32)
    jsonista.core_test$eval1863$fn__1864.invoke (core_test.clj:31)
    jsonista.core_test$eval1863.invokeStatic (core_test.clj:31)
    jsonista.core_test$eval1863.invoke (core_test.clj:30)
    ...

This works just fine with Virgil 0.1.6. I looked at the code changes since then, but nothing jumped out as especially suspicious. lein with-profile default test does seem to work.

To reproduce with jsonista
git clone https://github.com/metosin/jsonista.git
cd jsonista
git checkout 73e6d4d75c0a9469ee1cba78eaa78afe3f0cd516
lein with-profile dev test

virgil interferes with proxy

I posted on the clojure mailing list about a problem I was having with this minimal example, executed in a segmented namespace:

(definterface Interface (test []))
(def p (proxy [Object Interface] [] (test [] 1)))
(defn get-test [o] (.test ^Interface o))
(get-test p)

Someone reported that the problem was due to the lein-virgil plugin, which was causing Clojure to throw an error that p could not be cast to Interface. I have not confirmed for myself that virgil is the culprit, but am relaying the outcome of the discussion here.

Here's the thread:
https://www.mail-archive.com/[email protected]/msg104426.html

Compiler context isn't quite right

I'm not sure what's up here, but it's not #1. I'm seeing that when a class changes, it fails to resolve other unchanged classes unless they are first compiled by Virgil. As of right now (ox-lang/ox@455db6c) simply starting a fresh repl and adding a whitespace change to ox.lang.AObj will generate this:

2016-01-05-040437_392x108_scrot

however ox.lang.IMeta is a loaded class in the REPL, so something would seem to be screwy with the classloader or the handling of source paths in that extant depended classes aren't visible during recompilation.

Integration with clojure.tools.namespace.repl workflow

I write performance-sensitive clojure code, sometimes dropping into java, and virgil has been great for this. Thank you!

But frankly I don't want virgil to recompile on file saves. Would you want your clojure namespaces to recompile on save? For clojure I use clojure.tools.namespace.repl/refresh to manually trigger a refresh of necessary namespaces. I'd love for virgil to integrate with this.

The current story is to call virgil.compile/compile-all-java and then clojure.tools.namespace.repl/refresh-all as suggested by a user in #20. But recompiling all clojure namespaces is unecessarily slow (~30 seconds in my primary project). You really only need to recompile clojure namespaces that depend on changed java classes, and their dependents.

I think we can make this happen, and here's how it could work:

  • Keep some state containing:
    • The set of directories containing java code (analogous to clojure.tools.namespace.repl/refresh-dirs).
    • The result of the last java compilation.
  • Have a function virgil.repl/refresh that:
    • Calls virgil.compile/compile-all-java.
    • Checks the resultant bytecodes against the previous compilation to determine which classes have changed.
    • Scans clojure.tools.namespace.repl/refresh-dirs for clojure files that import any of the changed java classes.
    • Touches the dependent clojure files (changing their last-modified time) so that clojure.tools.namespace.repl/refresh will recompile them.

With this, anyone who wants to integrate java files into their refresh workflow could just call (virgil.repl/refresh) before calling (clojure.tools.namespace.repl/refresh).

I made a proof of concept and have been using it in my project successfully.

If you like the concept, I'd be happy to clean this up and submit it as a PR. This could just become a new namespace, virgil.repl. Virgil already depends on clojure.tools.namespace.

What do you think?

Dependency clash with org.clojure/tools.analyzer.jvm

tools.analyzer.jvm depends on [org.ow2.asm/asm-all "4.2"] and Virgil depends on [org.ow2.asm/asm "5.1"]. ASM 4.2 does not support Java 8, so if you depend on both and manage to get the libraries in the wrong order in your classpath, you'll get this stacktrace from Virgil:

Exception in thread "main" java.lang.IllegalArgumentException, compiling:(/private/var/folders/8s/w7wls65s2vvdml8ztk54jfch0000gn/T/form-init7439712748175071086.clj:1:125)
	at clojure.lang.Compiler.load(Compiler.java:7391)
	at clojure.lang.Compiler.loadFile(Compiler.java:7317)
	at clojure.main$load_script.invokeStatic(main.clj:275)
	at clojure.main$init_opt.invokeStatic(main.clj:277)
	at clojure.main$init_opt.invoke(main.clj:277)
	at clojure.main$initialize.invokeStatic(main.clj:308)
	at clojure.main$null_opt.invokeStatic(main.clj:342)
	at clojure.main$null_opt.invoke(main.clj:339)
	at clojure.main$main.invokeStatic(main.clj:421)
	at clojure.main$main.doInvoke(main.clj:384)
	at clojure.lang.RestFn.invoke(RestFn.java:421)
	at clojure.lang.Var.invoke(Var.java:383)
	at clojure.lang.AFn.applyToHelper(AFn.java:156)
	at clojure.lang.Var.applyTo(Var.java:700)
	at clojure.main.main(main.java:37)
Caused by: java.lang.IllegalArgumentException
	at org.objectweb.asm.ClassReader.<init>(Unknown Source)
	at org.objectweb.asm.ClassReader.<init>(Unknown Source)
	at virgil.decompile$parents.invokeStatic(decompile.clj:12)
	at virgil.decompile$parents.invoke(decompile.clj:12)
	at virgil.decompile$rank_order$parents__1212.invoke(decompile.clj:24)
	at clojure.core$map$fn__4785.invoke(core.clj:2646)
	at clojure.lang.LazySeq.sval(LazySeq.java:40)
	at clojure.lang.LazySeq.seq(LazySeq.java:49)
	at clojure.lang.RT.seq(RT.java:521)
	at clojure.core$seq__4357.invokeStatic(core.clj:137)
	at clojure.core$zipmap.invokeStatic(core.clj:2962)
	at clojure.core$zipmap.invoke(core.clj:2962)
	at virgil.decompile$rank_order.invokeStatic(decompile.clj:25)
	at virgil.decompile$rank_order.invoke(decompile.clj:18)
	at virgil.compile$compile_java.invokeStatic(compile.clj:86)
	at virgil.compile$compile_java.invoke(compile.clj:81)
	at virgil.compile$compile_all_java.invokeStatic(compile.clj:103)
	at virgil.compile$compile_all_java.invoke(compile.clj:102)
	at virgil$watch$recompile__1292.invoke(virgil.clj:15)
	at virgil$watch.invokeStatic(virgil.clj:26)
	at virgil$watch.doInvoke(virgil.clj:9)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at user$eval1309.invokeStatic(form-init7439712748175071086.clj:1)
	at user$eval1309.invoke(form-init7439712748175071086.clj:1)
	at clojure.lang.Compiler.eval(Compiler.java:6927)
	at clojure.lang.Compiler.eval(Compiler.java:6916)
	at clojure.lang.Compiler.load(Compiler.java:7379)
	... 14 more

The annoying thing is that because the artefact IDs do not match, lein deps :tree or :pedantic? :abort in project.clj won't complain about the problem. A possible workaround is to exclude asm-all and hope that it won't break anything in tools.analyzer.jvm.


I'm not sure if Virgil needs to do anything about this. Mostly I opened this issue to document the problem somewhere. Since tools.analyzer.jvm is a fairly common dependency, this might bite some other people as well. Took me a while to figure out what's going on.

#windows Illegal character in path at index...

source-object and class-object assumes "dot" character is the only type of delimiter of class-name.
on windows, that delimiter is "backslash" character, so I have to patch manually to consider both case.

diff --git a/src/virgil.clj b/src/virgil.clj
--- a/src/virgil.clj
+++ b/src/virgil.clj
@@ -24,7 +24,7 @@
   [class-name source]
   (proxy [SimpleJavaFileObject]
       [(java.net.URI/create (str "string:///"
-                                 (.replace ^String class-name \. \/)
+                                 (str/replace ^String class-name #"[\\|\.]" "/")
                                  (. JavaFileObject$Kind/SOURCE extension)))
        JavaFileObject$Kind/SOURCE]
       (getCharContent [_] source)))
@@ -34,7 +34,7 @@
   [class-name baos]
   (proxy [SimpleJavaFileObject]
       [(java.net.URI/create (str "string:///"
-                                 (.replace ^String class-name \. \/)
+                                 (str/replace ^String class-name #"[\\|\.]" "/")
                                  (. JavaFileObject$Kind/CLASS extension)))
        JavaFileObject$Kind/CLASS]
     (openOutputStream [] baos)))

This is virgil version 0.1.0. I tried 0.1.2 manually, but it won't work and I don't know why .
environment: os windows10, runtime clojure-1.8.0

Doesn't reload dependency-affected classes

First of all, I just want to thank you for your work on virgil! It's been really useful for a recent project of mine. It performs admirably as advertised — it does indeed auto-reload all the .java files in :java-source-paths, which is an extremely useful feature and has saved me quite a lot of time and sighing while waiting for the compiler.

However, that said, it doesn't reload dependency-affected classes. By that I mean that if class A imports or refers to in some way class B, and I make changes to class B's .java file, then B will reload/auto-compile, but A will not recognize that anything has changed (e.g. updated B code will not be called from A). In the description for virgil, you say that " all the namespaces that rely on those files will be reloaded", but for some reason this is not happening — at least not for me. Am I missing something?

Thanks for listening. I'm a big fan of your work with e.g. primitive-math and proteus (yes, I use proteus in production, if you can believe it haha).

Reloading namespaces in arbitrary order causes problems

I'm unable to use Virgil with Whidbey (or Ultra, which depends on Whidbey). I think the problem is that Virgil reloads Clojure namespaces in arbitrary order. It seems to break Whidbey:

First it [Virgil] reloads clojure.tools.nrepl.middleware.render-values which uses the old definition of Transport [protocol] and then it reloads clojure.tools.nrepl.transport, which creates a new definition for Transport.

I think this could be solved by making Virgil reload the namespaces in the dependency order. I quickly tried replacing Virgil's namespace reloading code with clojure.tools.namespace.repl/refresh-all and it fixed the problem. However, refresh-all is quite invasive: it unloads the namespaces before reloading them and loads all the Clojure files it can find, even if they weren't loaded in the first place. Would you be open to accepting such a patch?

virgil doesn't work under JDK 11

Everything is fine under openjdk version "1.8.0_191", however under openjdk version "11.0.1" 2018-10-16 I get:

Exception in thread "main" Syntax error compiling at (/tmp/form-init5179779871166635250.clj:1:73).
	at clojure.lang.Compiler.load(Compiler.java:7647)
	at clojure.lang.Compiler.loadFile(Compiler.java:7573)
	at clojure.main$load_script.invokeStatic(main.clj:452)
	at clojure.main$init_opt.invokeStatic(main.clj:454)
	at clojure.main$init_opt.invoke(main.clj:454)
	at clojure.main$initialize.invokeStatic(main.clj:485)
	at clojure.main$null_opt.invokeStatic(main.clj:519)
	at clojure.main$null_opt.invoke(main.clj:516)
	at clojure.main$main.invokeStatic(main.clj:598)
	at clojure.main$main.doInvoke(main.clj:561)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.lang.Var.applyTo(Var.java:705)
	at clojure.main.main(main.java:37)
Caused by: java.lang.IllegalArgumentException
	at org.objectweb.asm.ClassReader.<init>(ClassReader.java:160)
	at org.objectweb.asm.ClassReader.<init>(ClassReader.java:143)
	at virgil.decompile$parents.invokeStatic(decompile.clj:12)
	at virgil.decompile$parents.invoke(decompile.clj:12)
	at virgil.decompile$rank_order$parents__8541.invoke(decompile.clj:24)
	at clojure.core$map$fn__5851.invoke(core.clj:2755)
	at clojure.lang.LazySeq.sval(LazySeq.java:42)
	at clojure.lang.LazySeq.seq(LazySeq.java:51)
	at clojure.lang.RT.seq(RT.java:531)
	at clojure.core$seq__5387.invokeStatic(core.clj:137)
	at clojure.core$zipmap.invokeStatic(core.clj:3071)
	at clojure.core$zipmap.invoke(core.clj:3071)
	at virgil.decompile$rank_order.invokeStatic(decompile.clj:25)
	at virgil.decompile$rank_order.invoke(decompile.clj:18)
	at virgil.compile$compile_java.invokeStatic(compile.clj:124)
	at virgil.compile$compile_java.invoke(compile.clj:119)
	at virgil.compile$compile_all_java.invokeStatic(compile.clj:161)
	at virgil.compile$compile_all_java.invoke(compile.clj:149)
	at virgil$watch$recompile__8667.invoke(virgil.clj:44)
	at virgil$watch.invokeStatic(virgil.clj:61)
	at virgil$watch.doInvoke(virgil.clj:41)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at user$eval8680.invokeStatic(form-init5179779871166635250.clj:1)
	at user$eval8680.invoke(form-init5179779871166635250.clj:1)
	at clojure.lang.Compiler.eval(Compiler.java:7176)
	at clojure.lang.Compiler.eval(Compiler.java:7165)
	at clojure.lang.Compiler.load(Compiler.java:7635)
	... 12 more

cider-nrepl 0.16.0-SNAPSHOT causes "an exception has occured in the compiler"/IllegalAccessError

When using virgil with cider-nrepl 0.16.0-SNAPSHOT, the java compiler is not able to report errors in source files. Instead it reports:

An exception has occurred in the compiler (1.8.0_152). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program and the following diagnostic in your report. Thank you.

IllegalAccessError tried to access class com.sun.tools.javac.util.Log$2 from class com.sun.tools.javac.util.Log  com.sun.tools.javac.util.Log.getWriter (Log.java:372)

See https://github.com/schmir/cider-vs-javac for a repository demonstrating the error and the issue in cider-nrepl's repository: clojure-emacs/cider-nrepl#463

At the moment I'm not sure how to fix the problem, though there is a workaround and a short note in the readme may save the next person from spending way too much time on this issue.

The workaround is to add :injections in project.clj:

   :injections [(require 'cider.nrepl.middleware.util.java)]

Enable only for certain tasks

This plugin seems to break codox, is there anyway to enable it only for some certain task like repl? I'm sorry if this is supposed to be common knowledge.

Only recompile Java files that changed?

Is there a way for this to only recompile those java files that have actually changed and would need new class files? It’s currently recompiling lots of large Protobuf files which never change, and the delay to do all of this is killing me. Any suggestions?

Static method changes handled?

Thanks for making Virgil available, @ztellman!

I created a Java class in my Clojure project to try out Virgil via Leiningen. Instance method changes triggered a recompile/reload, but not (as far as I could tell) static method changes. Is this a known issue?

PR for separating compilation from watching code

Hey, I've been playing with doing something similar to this in Boot, and gotten things working to a large degree just by copy-paste-modify.

Now however I'd like to release an actual library with a new boot task and I thought perhaps I'd contribute back to virgil a bit and use it as a dependency.

So. My question is, if I were to submit a PR that separates out the java compiling code from the code for file-watching and reloading namespaces, would that be likely to get merged?

Also, thanks for your work on virgil!

IllegalAccessError when starting repl

I get the following error when starting lein repl. This happens with 0.1.7, but not with 0.1.6. So, I suspect one of my changes may have broken virgil.

I'm not sure what changed on my system. It used to run fine. I"ll have a look into it.

nREPL server started on port 40719 on host 127.0.0.1 - nrepl://127.0.0.1:40719

recompiling all files in ["/home/ralf/soka/repos/JavaRechenkern/src"]
An exception has occurred in the compiler (1.8.0_152). Please file a bug against the Java compiler via the Java bug reporting page (http://bugreport.java.com) after checking the Bug Database (http://bugs.java.com) for duplicates. Include your program and the following diagnostic in your report. Thank you.
java.lang.IllegalAccessError: tried to access class com.sun.tools.javac.comp.Lower$3 from class com.sun.tools.javac.comp.Lower
        at com.sun.tools.javac.comp.Lower.abstractLval(Lower.java:2325)
        at com.sun.tools.javac.comp.Lower.visitAssignop(Lower.java:3226)
        at com.sun.tools.javac.tree.JCTree$JCAssignOp.accept(JCTree.java:1716)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2371)
        at com.sun.tools.javac.tree.TreeTranslator.visitExec(TreeTranslator.java:245)
        at com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.java:1296)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2371)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70)
        at com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:162)
        at com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3561)
        at com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:909)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2371)
        at com.sun.tools.javac.comp.Lower.visitIf(Lower.java:2977)
        at com.sun.tools.javac.tree.JCTree$JCIf.accept(JCTree.java:1269)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2371)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70)
        at com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:162)
        at com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3561)
        at com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:909)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2371)
        at com.sun.tools.javac.comp.Lower.visitIf(Lower.java:2977)
        at com.sun.tools.javac.tree.JCTree$JCIf.accept(JCTree.java:1269)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2371)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70)
        at com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:162)
        at com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3561)
        at com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:909)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2371)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70)
        at com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:162)
        at com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3561)
        at com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:909)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2371)
        at com.sun.tools.javac.comp.Lower.visitForLoop(Lower.java:3582)
        at com.sun.tools.javac.tree.JCTree$JCForLoop.accept(JCTree.java:1000)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2371)
        at com.sun.tools.javac.comp.Lower.visitIterableForeachLoop(Lower.java:3528)
        at com.sun.tools.javac.comp.Lower.visitForeachLoop(Lower.java:3378)
        at com.sun.tools.javac.tree.JCTree$JCEnhancedForLoop.accept(JCTree.java:1035)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2371)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70)
        at com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:162)
        at com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3561)
        at com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:909)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2371)
        at com.sun.tools.javac.comp.Lower.visitForLoop(Lower.java:3582)
        at com.sun.tools.javac.tree.JCTree$JCForLoop.accept(JCTree.java:1000)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2371)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:70)
        at com.sun.tools.javac.tree.TreeTranslator.visitBlock(TreeTranslator.java:162)
        at com.sun.tools.javac.comp.Lower.visitBlock(Lower.java:3561)
        at com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:909)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2371)
        at com.sun.tools.javac.tree.TreeTranslator.visitMethodDef(TreeTranslator.java:145)
        at com.sun.tools.javac.comp.Lower.visitMethodDefInternal(Lower.java:2828)
        at com.sun.tools.javac.comp.Lower.visitMethodDef(Lower.java:2737)
        at com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:778)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2371)
        at com.sun.tools.javac.comp.Lower.visitClassDef(Lower.java:2508)
        at com.sun.tools.javac.tree.JCTree$JCClassDecl.accept(JCTree.java:693)
        at com.sun.tools.javac.tree.TreeTranslator.translate(TreeTranslator.java:58)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2371)
        at com.sun.tools.javac.comp.Lower.translate(Lower.java:2390)
        at com.sun.tools.javac.comp.Lower.translateTopLevelClass(Lower.java:3932)
        at com.sun.tools.javac.main.JavaCompiler.desugar(JavaCompiler.java:1512)
        at com.sun.tools.javac.main.JavaCompiler.desugar(JavaCompiler.java:1356)
        at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:901)
        at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:860)
        at com.sun.tools.javac.main.Main.compile(Main.java:523)
        at com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:129)
        at com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:138)
        at virgil.compile$source__GT_bytecode.invokeStatic(compile.clj:70)
        at virgil.compile$source__GT_bytecode.invoke(compile.clj:61)
        at virgil.compile$compile_java.invokeStatic(compile.clj:85)
        at virgil.compile$compile_java.invoke(compile.clj:81)
        at virgil.compile$compile_all_java.invokeStatic(compile.clj:110)
        at virgil.compile$compile_all_java.invoke(compile.clj:109)
        at virgil$watch$recompile__3523.invoke(virgil.clj:42)
        at virgil$consume_queue_and_callback_when_idle$fn__3512.invoke(virgil.clj:19)
        at virgil$consume_queue_and_callback_when_idle.invokeStatic(virgil.clj:18)
        at virgil$consume_queue_and_callback_when_idle.invoke(virgil.clj:10)
        at virgil$make_idle_callback$fn__3515$fn__3517.invoke(virgil.clj:31)
        at clojure.lang.AFn.run(AFn.java:22)
        at java.lang.Thread.run(Thread.java:748)
tried to access class com.sun.tools.javac.util.AbstractDiagnosticFormatter$2 from class com.sun.tools.javac.util.AbstractDiagnosticFormatter
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.8.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_152-b16
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

Can't def vars in REPL after Virgil reloads

I've come across a weird issue. To reproduce, create a new folder with the following files:

project.clj:

(defproject virgil-debug "0.1.0-SNAPSHOT"
  :dependencies [[org.clojure/clojure "1.9.0"]]
  :profiles {:dev {:plugins [[lein-virgil "0.1.7"]]
                   :source-paths ["env/dev"]
                   :java-source-paths ["java"]}})

env/dev/user.clj

(ns user)

java/Foo.java

package foo.bar;

public class Foo {
  // Whatever
}

Now, when you start a REPL:

Compiling 1 source files to /Users/<somepath>/clojure/virgil-debug/target/classes
nREPL server started on port 54024 on host 127.0.0.1 - nrepl://127.0.0.1:54024
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.9.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_92-b14

recompiling all files in ["/Users/<somepath>/clojure/virgil-debug/java"]

user=> :reloading (user)

user=> (do (def a 1) (class a))
#<Class@68e818cc clojure.lang.Var$Unbound>

To fix the issue, simply comment out (ns user) in user.clj

user=> (do (def a 1) (class a))
#<Class@3a079870 java.lang.Long>

I thought that this was some sort of race condition because the REPL inits in the user ns, but the issue remains even if you set :repl-options {:init-ns some.other.ns}. Probably related to #15 also.

java.lang.ArrayIndexOutOfBoundsException - Index ... out of bounds for length ...

Hi I keep getting this error when I try to compile the project :

recompiling all files in ["/home/ho0man/Projects/focus/Storm-Kepper/storm-virgil-failure/src/java"]
Index 28527 out of bounds for length 194
Exception in thread "main" Syntax error compiling at (/tmp/form-init7228090304527752464.clj:1:73).
        at clojure.lang.Compiler.load(Compiler.java:7647)
        at clojure.lang.Compiler.loadFile(Compiler.java:7573)
        at clojure.main$load_script.invokeStatic(main.clj:452)
        at clojure.main$init_opt.invokeStatic(main.clj:454)
        at clojure.main$init_opt.invoke(main.clj:454)
        at clojure.main$initialize.invokeStatic(main.clj:485)
        at clojure.main$null_opt.invokeStatic(main.clj:519)
        at clojure.main$null_opt.invoke(main.clj:516)
        at clojure.main$main.invokeStatic(main.clj:598)
        at clojure.main$main.doInvoke(main.clj:561)
        at clojure.lang.RestFn.applyTo(RestFn.java:137)
        at clojure.lang.Var.applyTo(Var.java:705)
        at clojure.main.main(main.java:37)
Caused by: java.lang.ArrayIndexOutOfBoundsException: Index 28527 out of bounds for length 194
        at org.objectweb.asm.ClassReader.getSuperName(Unknown Source)
        at virgil.decompile$parents.invokeStatic(decompile.clj:15)
        at virgil.decompile$parents.invoke(decompile.clj:12)
        at virgil.decompile$rank_order$parents__740.invoke(decompile.clj:24)
        at clojure.core$map$fn__5851.invoke(core.clj:2755)
        at clojure.lang.LazySeq.sval(LazySeq.java:42)
        at clojure.lang.LazySeq.seq(LazySeq.java:51)
        at clojure.lang.Cons.next(Cons.java:39)
        at clojure.lang.RT.next(RT.java:709)
        at clojure.core$next__5371.invokeStatic(core.clj:64)
        at clojure.core$zipmap.invokeStatic(core.clj:3079)
        at clojure.core$zipmap.invoke(core.clj:3071)
        at virgil.decompile$rank_order.invokeStatic(decompile.clj:25)
        at virgil.decompile$rank_order.invoke(decompile.clj:18)
        at virgil.compile$compile_java.invokeStatic(compile.clj:124)
        at virgil.compile$compile_java.invoke(compile.clj:119)
        at virgil.compile$compile_all_java.invokeStatic(compile.clj:161)
        at virgil.compile$compile_all_java.invoke(compile.clj:149)
        at virgil$watch$recompile__866.invoke(virgil.clj:44)
        at virgil$watch.invokeStatic(virgil.clj:61)
        at virgil$watch.doInvoke(virgil.clj:41)
        at clojure.lang.RestFn.invoke(RestFn.java:408)
        at user$eval879.invokeStatic(form-init7228090304527752464.clj:1)
        at user$eval879.invoke(form-init7228090304527752464.clj:1)
        at clojure.lang.Compiler.eval(Compiler.java:7176)
        at clojure.lang.Compiler.eval(Compiler.java:7165)
        at clojure.lang.Compiler.load(Compiler.java:7635)
        ... 12 more
Compilation failed: Subprocess failed (exit code: 1)

I have put the code in this public repo if anyone wants more info on this : https://github.com/clov0/storm-virgil-failure

Test failure

Hi,

When I run lein test in the virgil directory I get the following error (multiple times):

java.lang.IllegalAccessException: class virgil.compile$get_java_compiler cannot access class com.sun.tools.javac.api.JavacTool (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.api to unnamed module @74ac5242
This is on:

java --version
openjdk 17.0.11 2024-04-16
OpenJDK Runtime Environment Homebrew (build 17.0.11+0)
OpenJDK 64-Bit Server VM Homebrew (build 17.0.11+0, mixed mode, sharing)

I suspect this has to do with changes on the JDK side of things. I'll try an newer version and see what happens.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.