Replacement for ScalaCompile in gradle when switching to Kotlin - scala

I convert a project that was originally written in Scala into kotlin. It is a typical backend - frontend-design with the backend written in Scala and the Frontend in Angular (the code was written by a former co-worker who is no longer working in this project)
During the gradle-build, the e2e-tests are evaluated. For this purpose the backend is compiled in the following way:
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.scala.ScalaCompile
class ScalaCompilerPlugin implements Plugin<Project> {
void apply(Project project) {
project.configurations {
scalaCompilerPlugin
}
project.afterEvaluate {
project.tasks.withType(ScalaCompile) {
options.encoding = 'UTF-8'
if (scalaCompileOptions.additionalParameters == null) {
scalaCompileOptions.additionalParameters = []
}
scalaCompileOptions.additionalParameters <<
"-Xplugin:" + project.configurations.scalaCompilerPlugin.asPath
}
}
}
}
The 'scalaCompilerPlugin'-plugin is then added to the build.gradle-file in the backend.
plugins {
id 'scala'
id 'com.github.psxpaul.execfork' version '0.1.12'
}
apply plugin: ScalaCompilerPlugin
// ...
Since now the project is converted to Kotlin/Ktor I plan to use something like a KotlinCompiler-plugin for the same purpose, but in gradle 6.0.1, there seems to be no such plugin, and not being that firm with gradle I don't know how to add org.jetbrains.kotlin.gradle.tasks.KotlinCompile.
How can I achieve the same and start the backend during the gradle-build to run the e2e-tests during the test-runs? I am also open to suggestions to change this design.

Related

Eclipse, Buildship, Jigsaw: java.lang.module.FindException

Considering that Java 1.9 is now out for some time, i am still amazed how difficult it is to get a 1.9 project up and running with eclipse and buildship.
I now spend 2 days trying to get this flying and came very far but one issue is something i just get not off the ground:
"Right click on JUnit test case and 'Run as...'".
It always ends in a java.lang.module.FindException and i have to override the command line as described in Eclipse cannot find module even the module path is explicitly provided.
On top comes this whole "eclipse.classpath.file.whenMerged" stuff in the build.gradle file (my file see below).
Hence my question is two-fold:
Have i missed something that makes the jigsaw support with Gradle and buildship more easy?
What do I have to do to not patch the command line manually (as described in the link above)?
As I am currently evaluating the support of Eclipse/Gradle/Buildship for my team I need to have the second question fixed. Otherwise the developer in my team are going to get nuts. As such I am also contemplating to move away from Eclipse.
Side question: How good is the support in other IDE for modules and Gradle?
I am using
Eclipse 2020-12 (4.18.0.v20201202-1800)
Eclipse Plug-ins for Gradle 3.1.4.v20200326-1743
Java 12
Grade 6.7.1
Thank you
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java library project to get you started.
* For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
* User Manual available at https://docs.gradle.org/6.7.1/userguide/building_java_projects.html
*/
plugins {
// Apply the java-library plugin for API and implementation separation.
id 'java-library'
id 'eclipse'
id 'application'
}
sourceCompatibility = targetCompatibility = '1.12'
repositories {
// Use JCenter for resolving dependencies.
jcenter()
}
dependencies {
// Use JUnit test framework.
testImplementation 'junit:junit:4.13'
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
implementation 'org.postgresql:postgresql:42.2.18'
}
java {
modularity.inferModulePath = true
}
application {
mainModule = 'com.myproduct.mod.lib' // name defined in module-info.java
mainClass = 'com.myproduct.mod.lib.Library'
}
eclipse {
classpath {
file {
whenMerged {
//Define a module as being either a library or a project dependency.
//Test sources are excluded because eclipse wants them on the classpath for some reason (1)
entries.findAll { (it instanceof org.gradle.plugins.ide.eclipse.model.Library || it instanceof org.gradle.plugins.ide.eclipse.model.ProjectDependency) && !it.entryAttributes.get('gradle_used_by_scope').equals('test') }.each {
it.entryAttributes['module'] = 'true'
}
//Test-scoped stuff should set the appropriate flag
entries.findAll { (it.properties.kind.equals('src') || it.properties.kind.equals('lib')) && it.entryAttributes.get('gradle_used_by_scope').equals('test') }.each {
it.entryAttributes['test'] = 'true'
}
entries.findAll { isConGradle(it) }.each {
it.entryAttributes['module'] = 'true'
}
}
}
}
}
boolean isConGradle(entry) {
entry.kind == 'con' && entry.path == 'org.eclipse.buildship.core.gradleclasspathcontainer'
}

Using sbt Avrohugger plugin in gradle

I am using https://github.com/julianpeeters/avrohugger sbt plugin to generate Scala case classes for Avro .avsc files. How can I use the same plugin in a Gradle project?
I created gradle plugin for generating Scala case classes from Avro schema and it internally uses avrohugger library.
So now it's enough to add this plugin to your project:
plugins {
id 'com.zlad.gradle.avrohugger' version '0.2.1'
}
When you want to compile Scala sources you need Scala library dependency too:
plugins {
id 'scala'
id 'com.zlad.gradle.avrohugger' version '0.2.1'
}
repositories {
mavenCentral()
}
dependencies {
compile 'org.scala-lang:scala-library:2.12.8'
}
Scala classes will be automatically generated during build before compileScala task. By default avro schema should be in src/main/avro and generated sources will be in build/generated-src/avro
You can call generateAvroScala to invoke Scala sources generation manually.
You can find all details and configuration options on gradle-avrohugger-plugin github page.
I ended up using the avrohugger-tools library in Gradle to autogenerate Scala case classes whenever my schemas were updated. Your mileage may vary, but this finally worked for me:
build.gradle.kts
import java.io.File
plugins {
scala
id("com.github.maiflai.scalatest") version "0.19"
}
version = "1.0"
configurations {
register("avrohugger-tools")
}
dependencies {
// Scala std-libs
implementation(Dependencies.Libs.Scala.library)
// AvroHugger tools JAR
"avrohugger-tools"("com.julianpeeters:avrohugger-tools_2.12:1.0.0-RC14")
// testing
testImplementation(gradleTestKit())
testImplementation("junit:junit:4.12")
testImplementation("org.scalatest:scalatest_2.12:3.0.5")
testRuntime("org.pegdown:pegdown:1.4.2")
}
fun normalizeFileSeparator(path: String) = path.replace('/', File.separatorChar)
val genAvro = task<JavaExec>("genAvro") {
val sourceAvroPath = normalizeFileSeparator("src/main/avro/")
val sourceAvroFiles = fileTree(mapOf(
"dir" to sourceAvroPath, "include" to listOf("**/*.avsc")
))
val targetPath = normalizeFileSeparator("src/gen/")
doFirst {
delete(targetPath)
}
classpath = configurations["avrohugger-tools"]
main = "avrohugger.tool.Main"
args(
"generate", "schema",
sourceAvroFiles.files.joinToString(separator=" "),
targetPath
)
inputs.files(sourceAvroFiles)
outputs.files(targetPath)
}
the<JavaPluginConvention>().sourceSets.getByName(
SourceSet.MAIN_SOURCE_SET_NAME
).java.srcDir("gen")
tasks.withType<ScalaCompile> {
dependsOn(genAvro)
}
inline fun <reified T : Task> TaskContainer.existing() = existing(T::class)
inline fun <reified T : Task> TaskContainer.register(name: String, configuration: Action<in T>) = register(name, T::class, configuration)
Note that I'm also building/test a Scala source tree in this package, so some of the Scala-specific pieces can be presumably elided.
Really hope this helps!

fail to load a native library using activator (Play Framework)

I'm trying to load a native library in my Play 2.4.x application. I have written a simple test that works fine both in the IDE (IntelliJ) and in SBT. In both case I'm setting the java.library.path to get the tests to run.
In the IDE, I set -Djava.library.path=$USER_HOME$/dev/lindoapi/bin/linux64 in the test run configuration.
As per the sbt documentation, my build.sbt is forking the JVM and setting the java.library.path.
javaOptions += "-Djava.library.path=/home/aczerwon/dev/lindoapi/bin/linux64"
fork := true
The following test passes just fine in both the IDE and from activator test.
class LindoApiSpec extends Specification {
System.loadLibrary("lindojni")
"The Lindo API" should {
"have a valid license" in {
val lindo = new LindoEnvironment()
lindo.apiVerion() must beSuccessfulTry.withValue("LINDO API Version 9.0.2120.225")
}
}
When outside of the testing context, I load the native library in Play's startup lifecycle.
object Global extends GlobalSettings {
override def beforeStart(app: Application) = {
System.loadLibrary("lindojni")
}
}
When I call that same method from the webapi (activator ~run), I'm getting an UnsatisfiedLinkError error.
1) Error injecting constructor, java.lang.UnsatisfiedLinkError: no lindojni in java.library.path
at play.api.GlobalPlugin.<init>(GlobalSettings.scala:262)
at play.api.GlobalPlugin.class(GlobalSettings.scala:262)
while locating play.api.GlobalPlugin
The web api looks like this:
class OptimizationApi extends Controller {
def version() = Action {
val lindo = new LindoEnvironment()
lindo.apiVerion() match {
case Success(version) => Ok(version)
case Failure(e) => BadRequest(e.getMessage)
}
}
}
I assumed that my build.sbt would fork the JVM and set the java.library.path for both test and run contexts. Any clues as to what I'm doing wrong?
New Information
When I start activator -Djava.library.path=$USER_HOME$/dev/lindoapi/bin/linux64 or set JAVA_OPTS, the call to System.loadLibrary(...) in the startup lifecycle passes. I still get the UnsatisfiedLinkError, but it happens later when I make a call to the native library via JNI. Very strange.
I found a solution to the issue here.
The native library and its java counterpart must be in the same class loader.
Create a class similar to:
public final class PlayNativeLibraryLoader {
public static void load(String libraryPath) {
System.load(libraryPath);
}
}
And now you can use it in the Play startup lifecycle.
object Global extends GlobalSettings {
override def beforeStart(app: Application) = {
PlayNativeLibraryLoader.load(app.getFile("./lib/lindoapi/linux64/liblindojni.so").getPath)
Logger.info("Lindo native library loaded")
}
}

Gradle plugin development error passing arguments

I am trying to create a custom gradle plugin following the example here: http://www.javacodegeeks.com/2012/08/gradle-custom-plugin.html. Everything works fine as long as I dont try to provide arguments to the task, however when I attempt to add arguments, I receive the following error:
Error:(26, 0) Task of type 'com.jwoolston.finalizer.gradle.FinalizerTask_Decorated' has been instantiated directly which is not supported. Tasks can only be created using the DSL.
I've googled it but I don't seem to be getting any results related to my situation (at least not that I understand.
The plugin short id is declared in the generated jar's manifest as: finalizer-plugin.
I have the following files:
FinalizerPlugin.groovy
class FinalizerArgumentExtension {
String path = ''
}
class FinalizerPlugin implements Plugin<Project> {
#Override
void apply(Project project) {
project.extensions.create('finalizeArgs', FinalizerArgumentExtension)
project.task('finalizeTask', type: FinalizerTask)
}
}
FinalizerTask.groovy
class FinalizerTask extends DefaultTask {
#TaskAction
def executeTask() {
println "------------executeTask-------------------"
println "Source Directory : ${project.finalizeArgs.path}"
}
}
Plugin related exerpt of build.gradle of the utilizing project:
apply plugin: 'java'
apply plugin: 'maven'
version = '1.0.0'
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.11'
}
buildscript {
repositories {
maven {
url uri("file://C:\\Users\\ideal\\.m2\\repository")
}
}
dependencies {
classpath group: 'com.jwoolston.finalizer',
name: 'gradle',
version: '1.0.1-SNAPSHOT'
}
}
apply plugin: 'finalizer-plugin'
finalizeArgs {
path = "src/main/java"
}
In short, I don't understand the error. Near as I can tell, everything matches the tutorial. I am trying to simply pass in a string argument to the plugin's task.
Plugin class needs to be fixed as follows:
class FinalizerPlugin implements Plugin<Project> {
#Override
void apply(Project project) { ... }
}
Groovy only supports String interpolation for double-quoted Strings:
println "Source Directory : ${project.finalizeArgs.path}"

Use Gradle Project Extension Properties In Configuration Phase

When writing a custom Gradle plugin, how is it possible to access the extension properties defined in the consuming build.gradle in the custom plugin’s configuration phase?
Please see the following MWE.
build.gradle
apply plugin: 'codechecks'
codechecks {
checkstyleConfig = '/home/user/checkstyle.xml'
}
CodechecksPlugin.groovy
class CodechecksPlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create('codechecks', CodechecksPluginExtension)
project.apply( [ plugin: 'checkstyle' ] )
project.checkstyle {
configFile = project.codechecks.checkstyleConfig
}
}
}
CodechecksPluginExtension.groovy
class CodechecksPluginExtension {
def checkstyleConfig = 'config/checkstyle/checkstyle.xml'
}
Wanted behavior: The checkstyle plugin uses the configuration file defined in the build.gradle codechecks extension.
Actual behavior: The default value in the CodechecksPluginExtension is being used because the build.gradle codechecks extension is not yet evaluated.
I already tried putting all uses of the codechecks extension in the plugins into closures but they won’t expand correctly due to class casting issues at execution phase.
Thanks for your help!
project.afterEvaluate works for me.
Try:
project.afterEvaluate {
project.checkstyle {
configFile = project.codechecks.checkstyleConfig
}
}