How to create sourceset in custom plugin - gradle

I'm writing custom plugin which should create number of sourcesets depends on plugin extension.
How can I do it in apply method?
Here's my code snippets (both don't work), labels - list from extension:
1.
project.sourceSets {
labels.each { info ->
"${info.lower}Src" {
java.srcDirs = ['src'] + info.srcPostfix.collect { postfix -> "src_custom/${postfix}" }
}
}
main { java.srcDirs = ['src'] + labels.collect { info -> "src_custom/${info.lower}" } }
test { java.srcDirs = ['test'] + labels.collect { info -> "test_custom/${info.lower}" } }
}
2.
labels.each { info ->
SourceSet modelSrc = project.sourceSets.create("${info.lower}Src")
modelSrc.getJava().setSrcDirs(['src'] + info.srcPostfix.collect { postfix -> "src_custom/${postfix}" })
}
SourceSetContainer sourceSets = project.convention.getPlugin(JavaPluginConvention).sourceSets
SourceSet mainSourceSet = sourceSets.getByName(MAIN_SOURCE_SET_NAME)
mainSourceSet.getJava().setSrcDirs(['src'] + labels.collect { info -> "src_custom/${info.lower}" })
SourceSet testSourceSet = sourceSets.getByName(TEST_SOURCE_SET_NAME)
testSourceSet.getJava().setSrcDirs(['test'] + labels.collect { info -> "test_custom/${info.lower}" })

I know this is old, but here's what I ran into. I needed to create a plugin that would create a sourceSet. What I had before was this in my build.gradle
sourceSets {
myNewSet {
groovy.srcDir file("${project.myNewSet_src}")
resources.srcDir file("${project.myNewSet_resources}")
}
}
So when I created my plugin, in the apply method, I had this:
project.getSourceSets().create("myNewSet", {
groovy.srcDir new File("${project.getProjectDir()}/${project.myNewSet_src}")
resources.srcDir new File("${project.getProjectDir()}/${project.myNewSet_resources}")
});
Hope this helps someone.

Here is an example what does it look like in Kotlin Gradle DSL, configuring Spring Boot's BootRun task project-wide in multiproject build:
tasks {
withType<BootRun> {
val createGitRevisionReport: GitRevisionReportTask by this#tasks
dependsOn(createGitRevisionReport)
val sourceSets = project.convention.getPlugin(JavaPluginConvention::class.java).sourceSets
val additionalSourceSet = sourceSets.create("bootRunAdditionalSourceSet") {
resources.srcDir(createGitRevisionReport.gitRevisionReportDir)
}
sourceResources(additionalSourceSet)
}
}

Related

Kotlin multiplatform publish with dokka and sources

I am struggling to publish a Kotlin multiplatform project properly to maven (for now mavenLocal). I can add the dependency to another multiplatform project and use the code but I don't get any documentation and I am not sure if I am doing something wrong or if that is simply not possible at the moment.
From what I understand you cannot use the normal javadoc because it is bound to Java which does not make sense in a multiplatform environment. I read somewhere that in that case you should use the html version of dokka. I can see that I get a "javadoc.jar" with content to my mavenLocal but still in the IDE in an example project where I add my KMP library as a dependency, I don't see any documentation.
Also, the code decompiling seems to be weird. I guess somehow the sources are also not properly resolved.
According to the documentation everything should automatically and perfectly work by simply adding the maven-publish and the dokka plugin. But it seems like this is not the case and actually nothing is working as I'd expect it :D
Does anyone know, how to properly set that up?
My gradle file looks like this:
plugins {
kotlin("multiplatform") version "1.6.21"
id("org.jetbrains.kotlinx.benchmark") version "0.4.2"
id("org.jetbrains.dokka") version "1.6.21"
`maven-publish`
signing
}
group = "io.github.quillraven.fleks"
version = "1.4-KMP-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_1_8
repositories {
mavenCentral()
}
kotlin {
targets {
jvm {
compilations {
all {
kotlinOptions {
jvmTarget = "1.8"
}
}
val main by getting { }
// custom benchmark compilation
val benchmarks by compilations.creating {
defaultSourceSet {
dependencies {
// Compile against the main compilation's compile classpath and outputs:
implementation(main.compileDependencyFiles + main.output.classesDirs)
}
}
}
}
withJava()
testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
}
}
js(BOTH) {
browser { }
}
val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" -> macosX64("native")
hostOs == "Linux" -> linuxX64("native")
isMingwX64 -> mingwX64("native")
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}
sourceSets {
val commonMain by getting { }
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val jvmMain by getting
val jvmTest by getting
val jvmBenchmarks by getting {
dependsOn(commonMain)
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.2")
implementation("com.badlogicgames.ashley:ashley:1.7.4")
implementation("net.onedaybeard.artemis:artemis-odb:2.3.0")
}
}
val jsMain by getting
val jsTest by getting
val nativeMain by getting
val nativeTest by getting
}
}
benchmark {
targets {
register("jvmBenchmarks")
}
}
val javadocJar by tasks.registering(Jar::class) {
archiveClassifier.set("javadoc")
from(tasks.dokkaHtml)
}
publishing {
repositories {
maven {
url = if (project.version.toString().endsWith("SNAPSHOT")) {
uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
} else {
uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
}
credentials {
username = System.getenv("OSSRH_USERNAME")
password = System.getenv("OSSRH_TOKEN")
}
}
}
publications {
val kotlinMultiplatform by getting(MavenPublication::class) {
version = project.version.toString()
groupId = project.group.toString()
artifactId = "Fleks"
artifact(javadocJar)
pom {
name.set("Fleks")
description.set("A lightweight entity component system written in Kotlin.")
url.set("https://github.com/Quillraven/Fleks")
scm {
connection.set("scm:git:git#github.com:quillraven/fleks.git")
developerConnection.set("scm:git:git#github.com:quillraven/fleks.git")
url.set("https://github.com/quillraven/fleks/")
}
licenses {
license {
name.set("MIT License")
url.set("https://opensource.org/licenses/MIT")
}
}
developers {
developer {
id.set("Quillraven")
name.set("Simon Klausner")
email.set("quillraven#gmail.com")
}
}
}
}
signing {
useInMemoryPgpKeys(System.getenv("SIGNING_KEY"), System.getenv("SIGNING_PASSWORD"))
sign(kotlinMultiplatform)
}
}
}
// only sign if version is not a SNAPSHOT release.
// this makes it easier to publish to mavenLocal and test the packed version.
tasks.withType<Sign>().configureEach {
onlyIf { !project.version.toString().endsWith("SNAPSHOT") }
}
When I run the publishToMavenLocal gradle task then I get following directories in my .m2 folder:
When I then create an example project and add it as a dependency, then I don't see any quick documentation and also the decompiling is not working properly:
I was finally able to publish to mavenCentral. Not sure if this is the best way and correct way to do it, but it seems to work. Here is my build.gradle.kts. The important part is in the publications sections. For whatever reason it is not sufficient to have the "root" folder setup properly. I also had to adjust the pom, javadoc and signing of every single publication, too.
#file:Suppress("UNUSED_VARIABLE")
plugins {
kotlin("multiplatform") version "1.6.21"
id("org.jetbrains.kotlinx.benchmark") version "0.4.2"
id("org.jetbrains.dokka") version "1.6.21"
`maven-publish`
signing
}
group = "io.github.quillraven.fleks"
version = "1.4-KMP-RC1"
java.sourceCompatibility = JavaVersion.VERSION_1_8
repositories {
mavenCentral()
}
kotlin {
targets {
jvm {
compilations {
all {
kotlinOptions {
jvmTarget = "1.8"
}
}
val main by getting { }
// custom benchmark compilation
val benchmarks by compilations.creating {
defaultSourceSet {
dependencies {
// Compile against the main compilation's compile classpath and outputs:
implementation(main.compileDependencyFiles + main.output.classesDirs)
}
}
}
}
withJava()
testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
}
}
js(BOTH) {
browser { }
}
val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" -> macosX64("native")
hostOs == "Linux" -> linuxX64("native")
isMingwX64 -> mingwX64("native")
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}
sourceSets {
val commonMain by getting { }
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val jvmMain by getting
val jvmTest by getting
val jvmBenchmarks by getting {
dependsOn(commonMain)
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-benchmark-runtime:0.4.2")
implementation("com.badlogicgames.ashley:ashley:1.7.4")
implementation("net.onedaybeard.artemis:artemis-odb:2.3.0")
}
}
val jsMain by getting
val jsTest by getting
val nativeMain by getting
val nativeTest by getting
}
}
benchmark {
targets {
register("jvmBenchmarks")
}
}
val javadocJar by tasks.registering(Jar::class) {
archiveClassifier.set("javadoc")
from(tasks.dokkaHtml)
}
publishing {
repositories {
maven {
url = if (project.version.toString().endsWith("SNAPSHOT")) {
uri("https://s01.oss.sonatype.org/content/repositories/snapshots/")
} else {
uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/")
}
credentials {
username = System.getenv("OSSRH_USERNAME")
password = System.getenv("OSSRH_TOKEN")
}
}
}
publications {
val kotlinMultiplatform by getting(MavenPublication::class) {
// we need to keep this block up here because
// otherwise the different target folders like js/jvm/native are not created
version = project.version.toString()
groupId = project.group.toString()
artifactId = "Fleks"
}
}
publications.forEach {
if (it !is MavenPublication) {
return#forEach
}
// We need to add the javadocJar to every publication
// because otherwise maven is complaining.
// It is not sufficient to only have it in the "root" folder.
it.artifact(javadocJar)
// pom information needs to be specified per publication
// because otherwise maven will complain again that
// information like license, developer or url are missing.
it.pom {
name.set("Fleks")
description.set("A lightweight entity component system written in Kotlin.")
url.set("https://github.com/Quillraven/Fleks")
scm {
connection.set("scm:git:git#github.com:quillraven/fleks.git")
developerConnection.set("scm:git:git#github.com:quillraven/fleks.git")
url.set("https://github.com/quillraven/fleks/")
}
licenses {
license {
name.set("MIT License")
url.set("https://opensource.org/licenses/MIT")
}
}
developers {
developer {
id.set("Quillraven")
name.set("Simon Klausner")
email.set("quillraven#gmail.com")
}
}
}
signing {
useInMemoryPgpKeys(System.getenv("SIGNING_KEY"), System.getenv("SIGNING_PASSWORD"))
sign(it)
}
}
}
// only sign if version is not a SNAPSHOT release.
// this makes it easier to publish to mavenLocal and test the packed version.
tasks.withType<Sign>().configureEach {
onlyIf { !project.version.toString().endsWith("SNAPSHOT") }
}
Here is my example gradle file that publishes a kotlin multiplatform project with sources and javadocs to a maven repository.
The project source is here: https://github.com/danbrough/misc_demos/tree/master/kotlin_multiplatform_dokka
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("multiplatform")
id("org.jetbrains.dokka")
id("com.android.library")
`maven-publish`
}
group = "dokka.test"
version = "0.0.1"
buildscript {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
repositories {
mavenCentral()
google()
}
kotlin {
jvm()
android()
linuxX64()
macosX64()
sourceSets {
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
}
val posixMain by sourceSets.creating {}
targets.withType<KotlinNativeTarget>() {
compilations["main"].defaultSourceSet.dependsOn(posixMain)
}
}
tasks.withType<AbstractTestTask>() {
testLogging {
events = setOf(
TestLogEvent.PASSED, TestLogEvent.SKIPPED, TestLogEvent.FAILED
)
exceptionFormat = TestExceptionFormat.FULL
showStandardStreams = true
showStackTraces = true
}
outputs.upToDateWhen {
false
}
}
tasks.withType(KotlinCompile::class) {
kotlinOptions {
jvmTarget = "11"
}
}
tasks.dokkaHtml.configure {
outputDirectory.set(buildDir.resolve("dokka"))
}
val javadocJar by tasks.registering(Jar::class) {
archiveClassifier.set("javadoc")
from(tasks.dokkaHtml)
}
publishing {
repositories {
maven(project.buildDir.resolve("m2").toURI()) {
name = "m2"
}
}
publications.forEach {
if (it !is MavenPublication) {
return#forEach
}
it.artifact(javadocJar)
}
}
android {
compileSdk = 33
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
namespace = project.group.toString()
defaultConfig {
minSdk = 23
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
signingConfigs.register("release") {
storeFile = File(System.getProperty("user.home"), ".android/keystore")
keyAlias = "keyAlias"
storePassword = System.getenv("KEYSTORE_PASSWORD") ?: ""
keyPassword = System.getenv("KEYSTORE_PASSWORD") ?: ""
}
lint {
abortOnError = false
}
buildTypes {
getByName("debug") {
//debuggable(true)
}
getByName("release") {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
)
signingConfig = signingConfigs.getByName("release")
}
}
}

Gradle Configuration for Kotlin Multiplatform Project in Java and Js

Seems all ok! But...
I have created a Kotlin Multiplatform Project in Js and Java. I worked on create the right tests for all target and the right configuration for the build. Seems all go right, i managed to create the build and set it in the right way. Today i have opened the project and it stop, the build complete successfull but test and java compilations don't be executed.
So how i can configure it?
With code and result all will be more clear
build.gradle.kts
import com.moowork.gradle.node.npm.NpmTask
import com.moowork.gradle.node.task.NodeTask
import org.jetbrains.kotlin.gradle.dsl.KotlinJsCompile
buildscript {
repositories {
mavenCentral()
jcenter()
maven {
url = uri("https://plugins.gradle.org/m2/")
}
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.50")
}
}
plugins {
kotlin("multiplatform") version "1.3.50"
id("com.moowork.node") version "1.3.1"
}
repositories {
mavenCentral()
jcenter()
}
group = "com.example"
version = "0.0.1"
kotlin {
jvm()
js()
jvm {
withJava()
}
js {
nodejs()
}
sourceSets {
val commonMain by getting {
dependencies {
implementation(kotlin("stdlib-common"))
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
jvm {
compilations["main"].defaultSourceSet {
dependencies {
implementation(kotlin("stdlib-jdk8"))
}
}
compilations["test"].defaultSourceSet {
dependencies {
implementation(kotlin("test"))
implementation(kotlin("test-junit"))
}
}
}
js {
sequenceOf("", "Test").forEach {
tasks.getByName<KotlinJsCompile>("compile${it}KotlinJs") {
kotlinOptions {
moduleKind = "umd"
noStdlib = true
metaInfo = true
}
}
}
compilations["main"].defaultSourceSet {
dependencies {
implementation(kotlin("stdlib-js"))
}
}
compilations["test"].defaultSourceSet {
dependencies {
implementation(kotlin("test-js"))
}
}
}
}
}
val compileKotlinJs = tasks.getByName("compileKotlinJs")
val compileTestKotlinJs = tasks.getByName("compileTestKotlinJs")
val libDir = "$buildDir/lib"
val compileOutput = compileKotlinJs.getOutputs().getFiles()
val testOutput = compileTestKotlinJs.getOutputs().getFiles()
val populateNodeModules = tasks.create<Copy>("populateNodeModules") {
afterEvaluate {
from(compileOutput)
from(testOutput)
configurations["testCompile"].forEach {
if (it.exists() && !it.isDirectory) {
from(zipTree(it.absolutePath).matching { include("*.js") })
}
}
for (sourceSet in kotlin.sourceSets) {
from(sourceSet.resources)
}
into("$buildDir/node_modules")
}
dependsOn("compileKotlinJs")
}
node {
download = true;
}
tasks.create<NpmTask> ("installJest") {
setArgs(setOf("install", "jest"))
}
tasks.create<NodeTask> ("runJest") {
setDependsOn(setOf("installJest", "populateNodeModules", "compileTestKotlinJs"))
setScript(file("node_modules/jest/bin/jest.js"))
setArgs(compileTestKotlinJs.outputs.files.toMutableList().map {projectDir.toURI().relativize(it.toURI())})
}
tasks.getByName("test").dependsOn("runJest")
Look how many task are skiped! and how build Dir is created
buid result
build dir
I use jest to test in js.
Thanks in advance for support me

Handling multi project builds with Grade, Jacoco and Sonarqube

This is working so far (at least it looks like it works), but I don't feel that it is idiomatic or would stand the test of time even for a few months. Is there any way to do it better or more "by the book"?
We have a few multi project builds in Gradle where a project's test could touch another one's code, so it is important to see the coverage even if it wasn't in the same project, but it was in the same multiproject. I might be solving non existing problems, but in earlier SonarQube versions I had to "jacocoMerge" coverage results.
jacocoTestReport {
reports {
executionData (tasks.withType(Test).findAll { it.state.upToDate || it.state.executed })
xml.enabled true
}
}
if(!project.ext.has('jacocoXmlReportPathsForSonar')) {
project.ext.set('jacocoXmlReportPathsForSonar', [] as Set<File>)
}
task setJacocoXmlReportPaths {
dependsOn('jacocoTestReport')
doLast {
project.sonarqube {
properties {
property 'sonar.coverage.jacoco.xmlReportPaths', project.
ext.
jacocoXmlReportPathsForSonar.
findAll { d -> d.exists() }.
collect{ f -> f.path}.
join(',')
}
}
}
}
project.rootProject.tasks.getByName('sonarqube').dependsOn(setJacocoXmlReportPaths)
sonarqube {
properties {
property "sonar.java.coveragePlugin", "jacoco"
property "sonar.tests", []
property "sonar.junit.reportPaths", []
}
}
afterEvaluate { Project evaldProject ->
JacocoReport jacocoTestReportTask = (JacocoReport) evaldProject.tasks.getByName('jacocoTestReport')
evaldProject.ext.jacocoXmlReportPathsForSonar += jacocoTestReportTask.reports.xml.destination
Set<Project> dependentProjects = [] as Set<Project>
List<String> configsToCheck = [
'Runtime',
'RuntimeOnly',
'RuntimeClasspath',
'Compile',
'CompileClasspath',
'CompileOnly',
'Implementation'
]
evaldProject.tasks.withType(Test).findAll{ it.state.upToDate || it.state.executed }.each { Test task ->
logger.debug "JACOCO ${evaldProject.path} test task: ${task.path}"
sonarqube {
properties {
properties["sonar.junit.reportPaths"] += task.reports.junitXml.destination.path
properties["sonar.tests"] += task.testClassesDirs.findAll { d -> d.exists() }
}
}
configsToCheck.each { c ->
try {
Configuration cfg = evaldProject.configurations.getByName("${task.name}${c}")
logger.debug "JACOCO ${evaldProject.path} process config: ${cfg.name}"
def projectDependencies = cfg.getAllDependencies().withType(ProjectDependency.class)
projectDependencies.each { projectDependency ->
Project depProj = projectDependency.dependencyProject
dependentProjects.add(depProj)
}
} catch (UnknownConfigurationException uc) {
logger.debug("JACOCO ${evaldProject.path} unknown configuration: ${task.name}Runtime", uc)
}
}
}
dependentProjects.each { p ->
p.plugins.withType(JacocoPlugin) {
if (!p.ext.has('jacocoXmlReportPathsForSonar')) {
p.ext.set('jacocoXmlReportPathsForSonar', [] as Set<File>)
}
p.ext.jacocoXmlReportPathsForSonar += jacocoTestReportTask.reports.xml.destination
JacocoReport dependentJacocoTestReportTask = (JacocoReport) p.tasks.getByName('jacocoTestReport')
dependentJacocoTestReportTask.dependsOn(jacocoTestReportTask)
setJacocoXmlReportPaths.dependsOn(dependentJacocoTestReportTask)
}
}
}

Xtext: Couldn't resolve reference to JvmType MyGeneratorModule

I am copying the structure of the example Xtext Web project for multiple dsl's using the Entities and StateMachine example. I am using Gradle as my build system. I have a class MyGeneratorModule in both of my grammar projects. In my workflow I reference it like this:
configuration = MyGeneratorModule {...}
I can run the workflow fine in each project, but when I try to perform a jettyRun from the web project I get this error:
Task :com.selinc.logic.program:generateXtextLanguage FAILED
0 [main] ERROR mf.mwe2.launch.runtime.Mwe2Launcher - [XtextLinkingDiagnostic: null:17 Couldn't resolve reference to JvmType 'MyGeneratorModule'.
Am i missing something in the workflow or gradle build? Here is a more complete example of my languages build.gradle file and workflow:
mwe2:
component = XtextGenerator {
configuration = MyGeneratorModule { // <- This is what is not resolving
project = StandardProjectConfig {
baseName = baseName
rootPath = rootPath
runtimeTest = {
enabled = true
}
web = {
enabled = true
root = "../myWebProject"
src = "../myWebProject/src/main/java"
srcGen = "../myWebProject/src/main/xtext-gen"
assets = "../myWebProject/src/main/webapp"
}
mavenLayout = true
}
code = {
encoding = "UTF-8"
lineDelimiter = "\r\n"
fileHeader = "/*\n * generated by Xtext \${version}\n */"
}
}
cleaner = {
exclude = "MyOtherLanguageWebModule.java"
}
language = StandardLanguage {
name = "MyLang"
fileExtensions = "lang"
serializer = {
generateStub = false
}
webSupport = {
generateHtmlExample = true
framework = "CODEMIRROR"
generateJsHighlighting = false
generateServlet = false
generateWebXml=true
}
junitSupport = {
junitVersion = "5"
}
}
}
build.gradle:
dependencies {
testCompile "org.junit.jupiter:junit-jupiter-api:5.1.0"
testRuntime "org.junit.jupiter:junit-jupiter-engine:5.1.0"
testCompile "org.eclipse.xtext:org.eclipse.xtext.testing:${xtextVersion}"
compile project(':myOtherLang')
compile project(':myXCoreModel')
compile project(":util")
compile group: 'org.eclipse.xtext', name: 'org.eclipse.xtext.ecore', version: '2.15.0'
compile "org.eclipse.xtext:org.eclipse.xtext.xbase:${xtextVersion}"
}
sourceSets {
mwe2 {}
}
configurations {
mwe2 {
extendsFrom compile
}
mwe2Compile.extendsFrom mainCompile
mwe2Runtime.extendsFrom mainRuntime
}
sourceSets.mwe2.java.srcDir 'generator'
dependencies {
mwe2Compile "org.eclipse.emf:org.eclipse.emf.mwe2.launch:2.9.1.201705291010"
mwe2Compile "org.eclipse.xtext:org.eclipse.xtext.common.types:${xtextVersion}"
mwe2Compile "org.eclipse.xtext:org.eclipse.xtext.xtext.generator:${xtextVersion}"
mwe2Compile "org.eclipse.xtext:xtext-antlr-generator:[2.1.1, 3)"
//added for xcore support
mwe2Compile 'org.eclipse.emf:org.eclipse.emf.ecore.xcore:1.3.1'
mwe2Compile 'org.eclipse.emf:org.eclipse.emf.codegen.ecore.xtext:+'
}
task generateXtextLanguage(type: JavaExec) {
main = 'org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher'
classpath = project.sourceSets.mwe2.runtimeClasspath
inputs.file "path/GenerateMyLang.mwe2"
inputs.file "path/MyLang.xtext"
outputs.dir "src-gen"
args += "path/GenerateMyLang.mwe2"
args += "-p"
args += "rootPath=/${projectDir}/.."
}
test {
useJUnitPlatform()
}
generateXtext.dependsOn(generateXtextLanguage)
clean.dependsOn(cleanGenerateXtextLanguage)
eclipse.classpath.plusConfigurations += [configurations.mwe2]
i have doubts if this will work. the code you copy from does not generate an xtext language
you should move custom modules to a separate gradle project and thus compile it separately. alernatively you can experiment with gradle buildSrc/separate source folder code
(am not sure if this works for this usecase)
dependencies {
compile "org.eclipse.xtext:org.eclipse.xtext:${xtextVersion}"
compile "org.eclipse.xtext:org.eclipse.xtext.xbase:${xtextVersion}"
}
sourceSets {
mwe2 {}
}
configurations {
mwe2 {
extendsFrom compile
}
mwe2Compile.extendsFrom mainCompile
mwe2Runtime.extendsFrom mainRuntime
}
sourceSets.mwe2.java.srcDir 'generator'
dependencies {
mwe2Compile "org.eclipse.emf:org.eclipse.emf.mwe2.launch:2.9.1.201705291010"
mwe2Compile "org.eclipse.xtext:org.eclipse.xtext.common.types:${xtextVersion}"
mwe2Compile "org.eclipse.xtext:org.eclipse.xtext.xtext.generator:${xtextVersion}"
mwe2Compile "org.eclipse.xtext:xtext-antlr-generator:[2.1.1, 3)"
}
task generateXtextLanguage(type: JavaExec) {
main = 'org.eclipse.emf.mwe2.launch.runtime.Mwe2Launcher'
classpath = project.sourceSets.mwe2.runtimeClasspath
inputs.file "src/org/xtext/example/mydsl/GenerateMyDsl.mwe2"
inputs.file "src/org/xtext/example/mydsl/MyDsl.xtext"
outputs.dir "src-gen"
args += "src/org/xtext/example/mydsl/GenerateMyDsl.mwe2"
args += "-p"
args += "rootPath=/${projectDir}/.."
}
generateXtext.dependsOn(generateXtextLanguage)
clean.dependsOn(cleanGenerateXtextLanguage)
eclipse.classpath.plusConfigurations += [configurations.mwe2]

Integration tests with Gradle Kotlin DSL

I'm using this blog post to configure integration tests for a Spring Boot project, but I'm pretty stuck on declaring the source sets. I also found this post on StackOverflow, but I think I'm a bit further already.
My project structure is
project
|_ src
|_ main
| |_ kotlin
| |_ resources
|_ testIntegration
| |_ kotlin
| |_ resources
|_ test
| |_ kotlin
| |_ resources
|_ build.gradle.kts
|_ ... other files
And build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
idea
kotlin("jvm")
id("org.springframework.boot") version "2.0.5.RELEASE"
id("org.jetbrains.kotlin.plugin.spring") version "1.2.71"
}
fun DependencyHandlerScope.springBoot(module: String) = this.compile("org.springframework.boot:spring-boot-$module:2.0.5.RELEASE")
fun DependencyHandlerScope.springBootStarter(module: String) = this.springBoot("starter-$module")
dependencies {
springBoot("devtools")
springBootStarter("batch")
springBootStarter("... spring boot dependencies")
compile("... more dependencies")
testCompile("... more test dependencies")
}
val test by tasks.getting(Test::class) {
useJUnitPlatform { }
}
kotlin {
sourceSets {
val integrationTest by creating {
kotlin.srcDir("src/testIntegration/kotlin")
resources.srcDir("src/testIntegration/resources")
}
}
}
val integrationTestCompile by configurations.creating {
extendsFrom(configurations["testCompile"])
}
val integrationTestRuntime by configurations.creating {
extendsFrom(configurations["testRuntime"])
}
val testIntegration by tasks.creating(Test::class) {
group = "verification"
testClassesDirs = kotlin.sourceSets["integrationTest"].kotlin
}
idea {
module {
testSourceDirs.addAll(kotlin.sourceSets["integrationTest"].kotlin.srcDirs)
testSourceDirs.addAll(kotlin.sourceSets["integrationTest"].resources.srcDirs)
}
}
I think I'm pretty much in the right direction. At least it doesn't throw an exception any more :)
When I run the testIntegration task, I get the following output:
Testing started at 12:08 ...
12:08:49: Executing task 'testIntegration'...
> Task :project:compileKotlin UP-TO-DATE
> Task :project:compileJava NO-SOURCE
> Task :project:processResources UP-TO-DATE
> Task :project:classes UP-TO-DATE
> Task :project:compileTestKotlin UP-TO-DATE
> Task :project:compileTestJava NO-SOURCE
> Task :project:processTestResources UP-TO-DATE
> Task :project:testClasses UP-TO-DATE
> Task :project:testIntegration
BUILD SUCCESSFUL in 2s
5 actionable tasks: 1 executed, 4 up-to-date
12:08:51: Task execution finished 'testIntegration'.
Also, IntelliJ doesn't recognise the testIntegration directories as Kotlin packages.
I was finally able to figure it out thanks to some help on the Kotlin Slack channel. First of all I had to upgrade to Gradle version 4.10.2.
For more info have a look at these two pages from Gradle:
https://docs.gradle.org/release-nightly/userguide/organizing_gradle_projects.html#sec:separate_test_type_source_files
https://docs.gradle.org/release-nightly/userguide/organizing_gradle_projects.html#sec:separate_test_type_source_files
Then I just had to create the sourceSets for the integrationTests
sourceSets {
create("integrationTest") {
kotlin.srcDir("src/integrationTest/kotlin")
resources.srcDir("src/integrationTest/resources")
compileClasspath += sourceSets["main"].output + configurations["testRuntimeClasspath"]
runtimeClasspath += output + compileClasspath + sourceSets["test"].runtimeClasspath
}
}
This would work just fine for Java, but since I'm working with Kotlin I had to add an extra withConvention wrapper
sourceSets {
create("integrationTest") {
withConvention(KotlinSourceSet::class) {
kotlin.srcDir("src/integrationTest/kotlin")
resources.srcDir("src/integrationTest/resources")
compileClasspath += sourceSets["main"].output + configurations["testRuntimeClasspath"]
runtimeClasspath += output + compileClasspath + sourceSets["test"].runtimeClasspath
}
}
}
In the docs they only put runtimeClasspath += output + compileClasspath, but I added sourceSets["test"].runtimeClasspath so I can directly use the test dependencies instead of declaring new dependencies for the integrationTest task.
Once the sourceSets were created it was a matter of declaring a new task
task<Test>("integrationTest") {
description = "Runs the integration tests"
group = "verification"
testClassesDirs = sourceSets["integrationTest"].output.classesDirs
classpath = sourceSets["integrationTest"].runtimeClasspath
mustRunAfter(tasks["test"])
}
After this the tests still didn't run, but that was because I'm using JUnit4. So I just had to add useJUnitPlatform() which makes this the final code
task<Test>("integrationTest") {
description = "Runs the integration tests"
group = "verification"
testClassesDirs = sourceSets["integrationTest"].output.classesDirs
classpath = sourceSets["integrationTest"].runtimeClasspath
mustRunAfter(tasks["test"])
useJUnitPlatform()
}
I didnt like the use of withConvention and how the kotlin src dir was set. So after check out both gradle docs here and here, I came up with this:
sourceSets {
create("integrationTest") {
kotlin {
compileClasspath += main.get().output + configurations.testRuntimeClasspath
runtimeClasspath += output + compileClasspath
}
}
}
val integrationTest = task<Test>("integrationTest") {
description = "Runs the integration tests"
group = "verification"
testClassesDirs = sourceSets["integrationTest"].output.classesDirs
classpath = sourceSets["integrationTest"].runtimeClasspath
mustRunAfter(tasks["test"])
}
tasks.check {
dependsOn(integrationTest)
}
I preferr the less verbose style when using kotlin { and the use of variable for the new integrationTestTask.
As of Gradle 5.2.1 see https://docs.gradle.org/current/userguide/java_testing.html#sec:configuring_java_integration_tests
sourceSets {
create("intTest") {
compileClasspath += sourceSets.main.get().output
runtimeClasspath += sourceSets.main.get().output
}
}
val intTestImplementation by configurations.getting {
extendsFrom(configurations.testImplementation.get())
}
configurations["intTestRuntimeOnly"].extendsFrom(configurations.runtimeOnly.get())
dependencies {
intTestImplementation("junit:junit:4.12")
}
val integrationTest = task<Test>("integrationTest") {
description = "Runs integration tests."
group = "verification"
testClassesDirs = sourceSets["intTest"].output.classesDirs
classpath = sourceSets["intTest"].runtimeClasspath
shouldRunAfter("test")
}
tasks.check { dependsOn(integrationTest) }
Here is git repo that you can refer to: enter link description here
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent
plugins {
application
kotlin("jvm") version "1.3.72"
id("com.diffplug.gradle.spotless") version "3.24.2"
id("org.jmailen.kotlinter") version "1.26.0"
checkstyle
}
version = "1.0.2"
group = "org.sample"
application {
mainClass.set("org.sample.MainKt")
}
repositories {
mavenCentral()
jcenter()
}
tasks.checkstyleMain { group = "verification" }
tasks.checkstyleTest { group = "verification" }
spotless {
kotlin {
ktlint()
}
kotlinGradle {
target(fileTree(projectDir).apply {
include("*.gradle.kts")
} + fileTree("src").apply {
include("**/*.gradle.kts")
})
ktlint()
}
}
tasks.withType<Test> {
useJUnitPlatform()
testLogging {
lifecycle {
events = mutableSetOf(TestLogEvent.FAILED, TestLogEvent.PASSED, TestLogEvent.SKIPPED)
exceptionFormat = TestExceptionFormat.FULL
showExceptions = true
showCauses = true
showStackTraces = true
showStandardStreams = true
}
info.events = lifecycle.events
info.exceptionFormat = lifecycle.exceptionFormat
}
val failedTests = mutableListOf<TestDescriptor>()
val skippedTests = mutableListOf<TestDescriptor>()
addTestListener(object : TestListener {
override fun beforeSuite(suite: TestDescriptor) {}
override fun beforeTest(testDescriptor: TestDescriptor) {}
override fun afterTest(testDescriptor: TestDescriptor, result: TestResult) {
when (result.resultType) {
TestResult.ResultType.FAILURE -> failedTests.add(testDescriptor)
TestResult.ResultType.SKIPPED -> skippedTests.add(testDescriptor)
else -> Unit
}
}
override fun afterSuite(suite: TestDescriptor, result: TestResult) {
if (suite.parent == null) { // root suite
logger.lifecycle("----")
logger.lifecycle("Test result: ${result.resultType}")
logger.lifecycle(
"Test summary: ${result.testCount} tests, " +
"${result.successfulTestCount} succeeded, " +
"${result.failedTestCount} failed, " +
"${result.skippedTestCount} skipped")
failedTests.takeIf { it.isNotEmpty() }?.prefixedSummary("\tFailed Tests")
skippedTests.takeIf { it.isNotEmpty() }?.prefixedSummary("\tSkipped Tests:")
}
}
private infix fun List<TestDescriptor>.prefixedSummary(subject: String) {
logger.lifecycle(subject)
forEach { test -> logger.lifecycle("\t\t${test.displayName()}") }
}
private fun TestDescriptor.displayName() = parent?.let { "${it.name} - $name" } ?: "$name"
})
}
dependencies {
implementation(kotlin("stdlib"))
implementation("com.sparkjava:spark-core:2.5.4")
implementation("org.slf4j:slf4j-simple:1.7.30")
testImplementation("com.squareup.okhttp:okhttp:2.5.0")
testImplementation("io.kotest:kotest-runner-junit5-jvm:4.0.5")
testImplementation("io.kotest:kotest-assertions-core-jvm:4.0.5") // for kotest core jvm assertions
testImplementation("io.kotest:kotest-property-jvm:4.0.5")
}
sourceSets {
create("integTest") {
kotlin {
compileClasspath += main.get().output + configurations.testRuntimeClasspath
runtimeClasspath += output + compileClasspath
}
}
}
val integTest = task<Test>("integTest") {
description = "Runs the integTest tests"
group = "verification"
testClassesDirs = sourceSets["integTest"].output.classesDirs
classpath = sourceSets["integTest"].runtimeClasspath
mustRunAfter(tasks["test"])
}
tasks.check {
dependsOn(integTest)
}
sourceSets {
create("journeyTest") {
kotlin {
compileClasspath += main.get().output + configurations.testRuntimeClasspath
runtimeClasspath += output + compileClasspath
}
}
}
val journeyTest = task<Test>("journeyTest") {
description = "Runs the JourneyTest tests"
group = "verification"
testClassesDirs = sourceSets["journeyTest"].output.classesDirs
classpath = sourceSets["journeyTest"].runtimeClasspath
mustRunAfter(tasks["integTest"])
}
tasks.check {
dependsOn(journeyTest)
}
I hope this helps. :)
There is a dedicated Gradle feature called Declarative Test Suite that supports this case:
testing {
suites {
val test by getting(JvmTestSuite::class) {
useJUnitJupiter()
}
register("integrationTest", JvmTestSuite::class) {
dependencies {
implementation(project())
}
targets {
all {
testTask.configure {
shouldRunAfter(test)
}
}
}
}
}
}
More:
https://docs.gradle.org/current/userguide/java_testing.html#sec:configuring_java_integration_tests

Resources