Why does the gradle-clojure plugin skip the compileClojure task? - gradle

I'm trying to write a plugin for Intellij in Clojure. To that end I want to implement some extension endpoints with Clojure's :gen-class functionality. I've added the gradle-clojure plugin and placed some Clojure code in src/main/clojure. But when I build the project it says
> Task :compileClojure SKIPPED
Why is that?
Also, on a related note: If I add the expression (throw (Exception. "abort")) to the Clojure code on the top level, I can crash the build. This doesn't make sense to me. Why would Clojure code get executed during the build?

In Clojure, pre-compilation is not required. The source code can be compiled when running for the first time, as long as the source is bundled in the .jar file.
For gradle-clojure specifically, the default build task will run checkClojure, which will call the Clojure load function on each source directory, which loads all the namespaces. When you load a namespace, its expressions are executed in order. Normally you'd only put def or defn which would just define global variables. This is done to ensure there's no compiler errors before bundling in the .jar.
The gradle-clojure compileClojure task will only compile the namespaces that are configured with the aotNamespaces or all of them if using aotAll(). In that case it will call the Clojure compile on each namespace. See the gradle-clojure documentation for more info.
For more detail about Clojure compilation, see this documentation

Related

IntelliJ + Gradle Multiproject: How to tell how the target source set is called?

we are currently checking, whether we can switch from Eclipse to IntelliJ as an IDE.
In this project we are using gradle multi projects whose structure looks something like this:
Project
|-ProjectA
|-ProjectAImpl
|-main*
|-ProjectATest
|-test*
|-ProjectB
|-ProjectBImpl
|-main*
|-ProjectBTest
|-test*
= Source set or in IntelliJ it seems a module.
The ProjectBTest has a dependency to ProjectATest, which is configured as
compile project(":ProjectA:ProjectATest")
This always worked properly with Eclipse but in IntelliJ I'm having the problem, that the ProjectBTest is configured such, that it is looking for a module named "Project.ProjectA.ProjectATest.main", instead of "Project.ProjectA.ProjectATest.test"
This module can obviously not be found, leading to a lot of compiler errors.
Can maybe somebody give me a hint how I can tell IntelliJ or gradle here to take the proper module?
Thank you very much.
This is standard Gradle functionality. Unless you have other Gradle customizations (like feature variants or changing the source directories for a source set), project dependencies will naturally target the main source set.
There are several ways to solve this, but two primary ones that stand out to me:
Use Gradle's Java test fixtures.
The "test" source set is not naturally inheritable in any way in Gradle. There is no built-in consumable configuration that provides test classes to downstream projects. However, Java test fixtures allow you to use a separate testFixtures source set which is shareable. To do this, you would do the following:
Add the java-test-fixtures plugin to all projects which need to produce shared test sources
Move your shared test sources to <project directory>/src/testFixtures (ideally this would include as few actual test classes as possible, but rather just shareable test logic instead)
Change your dependency references to point to the upstream project(s)' test fixtures artifact: testImplementation(testFixtures(project(":ProjectA:ProjectATest"))
Register a tests configuration which includes the test classes as an output.
project.configurations.register("tests") {
extendsFrom(project.configurations[JavaPlugin.TEST_RUNTIME_CONFIGURATION_NAME])
}
tasks.register("testJar", Jar::class) {
classifier.set("test-classes")
from(project.the<SourceSetContainer>()[SourceSet.TEST_SOURCE_SET_NAME].output)
}
project.artifacts.add("tests", project.tasks.named("testJar"))
Downstream projects:
dependencies {
compile(project(":ProjectA:ProjectATest", "tests"))
}
None of the above code is tested. It may require some adjustments.
Java test fixtures are a supported way to produce shareable test sources, so they should be preferred, but the tests configuration may be quicker to implement, depending on your use case.

How to build an ANTLR grammar with Gradle in a multi-platform project?

So I have been struggling all afternoon with getting some Gradle build to work for a Kotlin multiplatform project that involves an ANTLR grammar. What I'm trying to do is to have a parser generated by ANTLR from a shared grammar for both a Kotlin (or Java if that doesn't work) and JavaScript target. Based on this I'd like to write some library around the parser that can be used from the JVM and JavaScript.
So I have set up a Kotlin multi-platform project because that seemed like a nice way of killing two birds with one stone (here is a repo https://github.com/derkork/project.txt). I created a source set commonAntlr where I placed the grammar file under commonAntlr/antlr/project_txt.g4. According to the documentation of the ANTLR plugin this is how stuff should be set up. I also apply the antlr plugin at the top (here is the build.gradle.kts - https://github.com/derkork/project.txt/blob/master/build.gradle.kts).
Now I run gradle build in the hopes that the ANTLR plugin will at least try to generate some nice Java code for me from the grammar using the default settings. Alas, it does not. The ANTLR plugin does not even get started, which is what I can tell from the output. The build later fails with some obscure JavaScript problem, but that looks unrelated and I'd like to skip over it for now.
Now my Gradle-foo isn't exactly strong (I have only used it in extremely simple projects, most of my experience is in Maven), and I have the distinct feeling I'm missing something here. However the documentation of the plugin just says
To use the ANTLR plugin, include the following in your build script:
plugins {
antlr
}
Which I did. Since I get zero output, I have a feeling that I need to do a bit more for this to work. I have read a lot of the Gradle documentation up and down to find out how plugins work in general and I found that they add tasks to the build and also add some dependencies so that tasks are invoked when you try to build certain things. However I don't really understand how plugins work together with source sets and how you can tell Gradle "would you please run the generateGrammarSource task for this source set" (or if it even works like this).
So if some of the Gradle gods could enlighten me on this, this would be much appreciated :)
I have met a similar issue: https://gitlab.com/pika-lab/tuprolog/2p-in-kotlin/tree/feature/parser
My solution -- which is still a work in progress -- consists in decomposing the problem.
I reasonable solution in my opinion is to create a Kotlin/JVM project (say parser-jvm) where to put the generated Java code + any JVM specific facility, and a Kotlin/JS project where to put the generated JS code + any JS specific facility (say parser-js). The next step is to create a Kotlin/MPP (say parser-common) project whose JVM implementation depends on parser-jvm and whose JS implementation depends on parser-js.
My approach is actually working for JVM while I'm experiencing some issues for JS, which are mostly caused by this issue.
The main drawback of this approach is that some Gradle coding is required to setup ANTLR with Kotlin/JS. I already faced this problem in my build.gradle and I'm quite satisfied with the result and the overall architecture of the project. However, I believe that my proposal is far less troublesome than configuring a Kotlin/MPP project to work with ANTLR.

Spotbugs on a single file?

I am using Spotbugs plugin within Eclipse IDE. I can run the Spotbugs over a whole project, which gives me the impression that the tool needs to build the project to present its analysis report.
But the documentation says that it's a static analysis tool.
So, I was curious if it requires to build the project, then can we call it a Static Analysis Tool?
And if it doesn't require to build the project, can we run Spotbugs on single .java files?
The meaning of static analysis is that it analyses your project files "at rest", as opposed to monitoring a running application. https://en.wikipedia.org/wiki/Static_program_analysis
Analyzing bytecode has both strengths and weaknesses compared to analysing source code. It's faster, and better suited to deep analysis of program flow, but won't pick up mistakes that get compiled away, like unnecessary imports and inconsistent-but-legal whitespace.
You can't properly run it on a single file, even if you compiled that file, because there are detectors that take multiple files into consideration, eg detecting when you try to pass null to a method whose parameters are annotated as non-null, or when you've defined a method as public and then never called it from outside the class.
Yes, since SpotBugs analyzes bytecode (.class files), you must first build the project (at least the part you want to analyze).
After that, you can analyze just a single file, for example in IntelliJ IDEA (still FindBugs plugin, but SpotBugs can do all that FindBugs could, same code base):

Do I need cljsbuild plugin if I am just writing clojurescript utility functions?

I am a beginner in Clojurescript and I had a doubt. Right now I am simply writing a bunch of utility functions in CLJS files which will be used in other CLJS projects.
So do I still need to compile my CLJS files to JS using cljsbuild plugin? If no, how do I still compile my CLJS source files to check for errors and all?
To get started, I would recommend getting a CLJS REPL running. One option is to use boot cljs. Instruction to set that up here https://github.com/adzerk-oss/boot-cljs-repl
Or, if your functions are simple or pure, and does not have external dependencies, you can try a browser REPL such as http://clojurescript.io No setup required.
Either way, you can just paste the function in the REPL and test them. You will get instant feedback. That should get you started. Later on, you can add testing infrastructures to automate your tests. Have fun!
When a ClojureScript project depends on ClojureScript code in a library, it depends on the library's ClojureScript source code directly. (There is no need to compile that library's code to JavaScript for dependency purposes, and in fact, that model is not really supported.)
How does the ClojureScript project depend on the ClojureScript library's code? Typically the library's code is place in a JAR and that JAR is placed on the Java classpath of the project. (Oftentimes such JARs are deployed to Clojars or Maven central.)
A newer, more flexible alternative, using deps.edn is to have the ClojureScript project depend either on a JAR (as described in the previous paragraph), or to depend directly on the library's local path on disk (using :local/root) or the library's GitHub source (using :git/url and :sha).
To compile and test your code locally you could use cljs.main to load your code in a REPL and exercise it. This can often be as simple as setting up deps.edn and then running
clj -m cljs.main
followed by using (require my-ns.core) to load your namespace into the REPL. The Quick Start guide is very much worth a read regarding this.
You can use cljs.main to run its cljs.test-based tests. An example of running test like this using Node is here.
There are other, more sophisticated and feature-rich options out there for compiling and testing ClojureScript library code; the above covers some of the simpler options that ship with ClojureScript itself.

removing extra jars dependencies from java project

I am working on migrating multi module java project into maven. Now for most of them i migrated to maven.
Finally i am aware my project have lot of unnecessary jars included, and i want to clean them up.
I know maven has plugin command, mvn dependency:analyze. Which works very well.
dependency:analyze analyzes the dependencies of this project and determines which are: used and declared; used and undeclared; unused and declared. based on static code analysis.
Now my question is that, how can i remove reported unused and declared dependency for cleanup purpose. It could be possible those jars were getting used at runtime and my code will compile perfectly fine after removing but blow up at runtime.
An example: mycode compile with one of opensource library antisamy.jar but it require batik.jar at runtime. And mvn dependency:analyze reports me to remove batik.jar.
IS my understanding correct or i need expert inputs here.
Your understanding seems to be correct.
But I'm not sure why you'd think that there is a tool that could cover all the bases.
Yes, if you use stuff by reflection, no tool can reliably detect the fact that you depend on this class or the other.
For example consider this snippet:
String myClassName = "com." + "example." + "SomeClass";
Class.forName(myClassName);
I don't think you can build a tool that can crawl through the code and extract all such references.
I'd use a try-and-fail approach instead, which would consist of:
remove all dependencies that dependency:analyze says are superfluous
whenever you find one that was actually used, you just add it back
This could work well because I expect that the number of dependencies that are actually used by reflection to be extremely small.

Resources