Get rootPoject.name, or reference to settings.gradle, of included builds - gradle

I'm using includeBuild to include modules from my own library in settings.gradle:
rootProject.name = "MyApp"
include ':app'
includeBuild '/usr/local/library/Android/Event'
includeBuild '/usr/local/library/Android/Location'
includeBuild '/usr/local/library/Android/Widget'
I know I can iterate these later with:
gradle.includedBuilds.each{ includeBuild ->
println includeBuild.name
}
However, that prints:
Event
Location
Widget
Is there a simple way to get the rootProject.names that I have defined in each of those individual library projects' settings.gradle files?
I know I can do the following to give alternative names:
includeBuild('/usr/local/library/Android/Event', {name = 'com.example.android.event'})
includeBuild('/usr/local/library/Android/Location', {name = 'com.example.android.location'})
includeBuild('/usr/local/library/Android/Widget', {name = 'com.example.android.widget'})
... but that is redundant and cumbersome when I've already defined those as rootProject.name is their respective settings.gradle.
Rather, I'm looking for something akin to:
gradle.includedBuilds.each{ includeBuild ->
println includeBuild.rootProject.name
}
For instance, I know about includeBuild.projectDir. Can I somehow get a (parsed) reference to the settings.gradle file in that directory?

I've managed to solve it using org.gradle.tooling.GradleConnector:
import org.gradle.tooling.GradleConnector
import org.gradle.tooling.ProjectConnection
import org.gradle.tooling.model.GradleProject
def getIncludedProjectNamesMap(Project project) {
def projectNamesMap = new HashMap<String, String>()
project.gradle.includedBuilds.each { includedBuild ->
ProjectConnection connection = GradleConnector.newConnector()
.forProjectDirectory(includedBuild.projectDir)
.connect()
GradleProject includedProject = connection.getModel(GradleProject.class);
def name = includedProject.getName();
connection.close();
projectNamesMap.put includedBuild.name, name;
}
return projectNamesMap
}
println getIncludedProjectNamesMap(project)
... which prints:
{Event=com.example.android.event, Location=com.example.android.location, Widget=com.example.android.widget}
... but that appears to be rather slow, probably due to all the connections it need to make. It does the job for now, but I'm still looking for alternative approaches, if available.

Related

Gradle processResources expand on different projects modules

i'm trying to get a project.version so i wrote project2 gradle before codes
processResources {
def props = ['version': project.properties.get('version')]
filesMatching("**/MyConfiguration.kt") {
expand(props)
}
}
the MyConfiguration.kt file located project1 but the code return null
so i fixed codes like below
processResources {
def props = ['version': project.properties.get('version')]
expand(props)
}
then that codes return right value
maybe the problem is filesMatching input another module file?
i missed filesMatching input
need to setup .properties or .yaml (get project.properties this scope)

Quarkus Gradle plugin: overriding duplicate file entries coming from dependency libraries

Can I tell the Quarkus Gradle plugin (gradle quarkusDev or gradlew quarkusBuild -Dquarkus.package.uber-jar=true), to use resources provided by myself instead of choosing resources from dependency jars when they are duplicate?
I get these messages when building an uber-jar:
Duplicate entry META-INF/org.apache.uima.fit/types.txt entry from de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.api.segmentation-asl::jar:1.10.0(runtime) will be ignored. Existing file was provided by de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.api.syntax-asl::jar:1.10.0(runtime)
Duplicate entry META-INF/org.apache.uima.fit/types.txt entry from de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.api.lexmorph-asl::jar:1.10.0(runtime) will be ignored. Existing file was provided by de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.api.syntax-asl::jar:1.10.0(runtime)
Duplicate entry META-INF/org.apache.uima.fit/types.txt entry from de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.api.metadata-asl::jar:1.10.0(runtime) will be ignored. Existing file was provided by de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.api.syntax-asl::jar:1.10.0(runtime)
Duplicate entry META-INF/org.apache.uima.fit/types.txt entry from de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.api.ner-asl::jar:1.10.0(runtime) will be ignored. Existing file was provided by de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.api.syntax-asl::jar:1.10.0(runtime)
These DKPro / uimaFIT libraries are NLP libraries that bring all their own META-INF/org.apache.uima.fit/types.txt file. You are supposed to merge these files yourself and adding your own types, and then only include this newly merged file in your uber-jar, or as first one in your classpath.
There is an option quarkus.package.user-configured-ignored-entries in application.properties, but it also removes my own provided files. So that's not what I want (see also https://github.com/quarkusio/quarkus/blob/master/core/deployment/src/main/java/io/quarkus/deployment/pkg/steps/JarResultBuildStep.java#L186 ). I haven't checked the sources of gradle quarkusDev, but it results in the same runtime exceptions.
For reference for other people using uimaFIT, this incorrect META-INF/org.apache.uima.fit/types.txt file results in an error like
org.apache.uima.analysis_engine.AnalysisEngineProcessException: JCas type "org.apache.uima.conceptMapper.support.tokenizer.TokenAnnotation" used in Java code, but was not declared in the XML type descriptor..
So my question is, how do I tell Gradle or Quarkus to use this file provided by myself instead of randomly choosing a file from a dependency jar?
The example Gradle script written in Kotlin DSL. The task generateNlpFiles and the function joinResources automatically generate Java source files from XML files in src/main/typesystem into build/generated/sources/jcasgen/main/, as required by uimaFIT, and joins the duplicate resources like META-INF/org.apache.uima.fit/types.txt into /generated/resources/uimafit/. You don't need to look at them too hard.
import java.io.FileOutputStream
import java.net.URLClassLoader
import org.apache.commons.io.IOUtils
plugins {
id("java")
id("io.quarkus")
id("eclipse")
}
repositories {
jcenter()
// required for downloading OpenNLP models
maven("https://zoidberg.ukp.informatik.tu-darmstadt.de/artifactory/public-releases/")
}
group = "com.example"
version = "0.0.0-SNAPSHOT"
java.sourceCompatibility = JavaVersion.VERSION_11
java.targetCompatibility = JavaVersion.VERSION_11
dependencies {
val quarkusPlatformGroupId: String by project
val quarkusPlatformArtifactId: String by project
val quarkusPlatformVersion: String by project
// Quarkus dependencies
implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}"))
implementation("io.quarkus:quarkus-jaxb")
implementation("io.quarkus:quarkus-jackson")
implementation("io.quarkus:quarkus-resteasy")
implementation("io.quarkus:quarkus-jdbc-mariadb")
implementation("io.quarkus:quarkus-resteasy-jsonb")
implementation("io.quarkus:quarkus-smallrye-openapi")
implementation("io.quarkus:quarkus-container-image-docker")
// UIMA
implementation("org.apache.uima:uimaj-core:2.10.3")
implementation("org.apache.uima:ConceptMapper:2.10.2")
implementation("org.apache.uima:uimafit-core:2.4.0")
// DKPro
implementation("de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.io.xmi-asl:1.10.0")
implementation("de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.api.metadata-asl:1.10.0")
implementation("de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.langdetect-asl:1.10.0")
implementation("de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.icu-asl:1.10.0")
implementation("de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.opennlp-asl:1.10.0")
implementation("de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.opennlp-model-tagger-de-maxent:20120616.1")
implementation("de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.opennlp-model-tagger-en-maxent:20120616.1")
implementation("de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.opennlp-asl:1.10.0")
implementation("de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.opennlp-model-ner-de-nemgp:20141024.1")
implementation("de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.opennlp-model-ner-en-location:20100907.0")
implementation("de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.opennlp-model-ner-en-organization:20100907.0")
implementation("de.tudarmstadt.ukp.dkpro.core:de.tudarmstadt.ukp.dkpro.core.opennlp-model-ner-en-person:20130624.1")
// tests
testImplementation("io.quarkus:quarkus-junit5")
testImplementation("io.rest-assured:rest-assured")
// for generating NLP type system during compile time
compileOnly("org.apache.uima:uimaj-tools:2.10.4")
}
// joins resource files from classpath into single file
fun joinResources(classLoader: URLClassLoader, inputResourceName: String, outputFile: File) {
val outputStream = FileOutputStream(outputFile)
val resources = classLoader.findResources(inputResourceName).toList()
resources.forEach {
val inputStream = it.openStream()
IOUtils.copy(inputStream, outputStream)
outputStream.write('\n'.toInt());
inputStream.close()
}
outputStream.close()
}
// generate NLP type system from XML files and join uimaFIT files
val generateNlpFiles = task("generateNlpFiles") {
inputs.files(fileTree("src/main/typesystem"))
inputs.files(fileTree("src/main/resources"))
outputs.dir("${buildDir}/generated/sources/jcasgen/main/")
outputs.dir("${buildDir}/generated/resources/uimafit/")
val compileClasspath = project.sourceSets.main.get().compileClasspath
val runtimeClasspath = project.sourceSets.main.get().runtimeClasspath
val compileClassLoader = URLClassLoader(compileClasspath.map{ it.toURI().toURL() }.toTypedArray())
val runtimeClassLoader = URLClassLoader(runtimeClasspath.map{ it.toURI().toURL() }.toTypedArray())
// from XML files in src/main/typesystem/ generate Java sources into build/generated/sources/jcasgen/main/
val jCasGen = compileClassLoader.loadClass("org.apache.uima.tools.jcasgen.Jg").newInstance()
fileTree("src/main/typesystem").forEach() { typeSystemFile ->
doFirst {
// see https://github.com/Dictanova/gradle-jcasgen-plugin/blob/master/src/main/groovy/com/dictanova/jcasgen/gradle/JCasGenTask.groovy#L45
val jcasgeninput = "${typeSystemFile}"
val jcasgenoutput = "${buildDir}/generated/sources/jcasgen/main/"
val jcasgenclasspath = "${runtimeClasspath.asPath}"
val arguments: Array<String> = arrayOf("-jcasgeninput", jcasgeninput, "-jcasgenoutput", jcasgenoutput, "-jcasgenclasspath", jcasgenclasspath)
val main1 = jCasGen.javaClass.getMethod("main1", arguments.javaClass)
main1.invoke(jCasGen, arguments)
}
}
// collect types.txt and components.txt from classpath and join them in build/generated/resources/uimafit/META-INF/org.apache.uima.fit/
val uimafitDir = "${buildDir}/generated/resources/uimafit/META-INF/org.apache.uima.fit"
mkdir(uimafitDir)
joinResources(runtimeClassLoader, "META-INF/org.apache.uima.fit/types.txt", File("${uimafitDir}/types.txt"))
joinResources(runtimeClassLoader, "META-INF/org.apache.uima.fit/components.txt", File("${uimafitDir}/components.txt"))
}
eclipse {
project {
natures(
"org.eclipse.wst.common.project.facet.core.nature",
"org.eclipse.buildship.core.gradleprojectnature"
)
}
classpath {
file.withXml {
val attributes = mapOf("kind" to "src", "path" to "build/generated/sources/jcasgen/main")
this.asNode().appendNode("classpathentry", attributes)
}
}
}
tasks {
compileJava {
options.encoding = "UTF-8"
options.compilerArgs.add("-parameters") // was in original Quarkus Gradle file, not sure what this does
dependsOn(generateNlpFiles)
// add generated sources to source sets
sourceSets["main"].java.srcDir(file("${buildDir}/generated/sources/jcasgen/main/"))
sourceSets["main"].resources.srcDir(file("${buildDir}/generated/resources/uimafit/"))
}
compileTestJava {
options.encoding = "UTF-8"
}
"eclipse" {
dependsOn(generateNlpFiles)
}
}
One workaround would be using gradlew quarkusBuild -Dquarkus.package.uber-jar=true with entries in quarkus.package.user-configured-ignored-entries and adding my own files manually to the resulting jar, but that wouldn't work with gradle quarkusDev.
I am using Quarkus 1.3.2, as Quarkus 1.4.1 cannot handle multiple resource directories (see also https://github.com/quarkusio/quarkus/blob/master/devtools/gradle/src/main/java/io/quarkus/gradle/tasks/QuarkusDev.java#L391 ), as needed by my project.
I also tried to exclude files with some Gradle JarJar plugins, like https://github.com/shevek/jarjar , but couldn't get them running.
Right now, you can't, it will just take one from the jars providing it.
Could you create a feature request in our tracker: https://github.com/quarkusio/quarkus/issues/new?assignees=&labels=kind%2Fenhancement&template=feature_request.md&title= .
Sounds like something useful.
Thanks!

In a custom task in my buildSrc folder: How do I determine the file path of the subproject it is called in?

If I define a custom gradle task in buildSrc: How do I find out the relative path to the project from which the task is called?
In my buildSrc folder, I have a custom task that creates a Enum our of my message.properties file:
open class GenerateEnumTask : DefaultTask() {
#get:Input
open var inputFolder: String = "src/main/resources"
#get:Input
open val targetFilePath: String = "src/generated/kotlin/MessageCode.kt"
#get:OutputFile
val enumFile = File(targetFilePath)
#TaskAction
fun generateEnum() {
...
}
#Internal
override fun getDescription() = "This task uses downloaded property files and creates an enum kotlin file"
}
I then want to make sure the enum is generated before code compilation.
So I put this in the subproject "core", where I need the Enum.
build.gradle.kts:
tasks {
val generateEnumTask by registering(GenerateEnumTask::class)
withType<KotlinCompile> {
kotlinOptions.jvmTarget = Versions.jvmTarget
dependsOn(generateEnumTask)
dependsOn(formatKotlin)
doFirst{
println("compile kotlin in core project")
}
}
}
This does indeed work if I run gradle compileKotlin directly from the subfolder of the core project.
However, if I run the same command from the root project, the code searches for a src folder in the root directory.
Ah, the answer was simple: DefaultTask inherits from AbstractTask, which has a reference to the project that the task was called in (getProject)
This works nicely:
open var targetFolder: String = this.project.file("src/main/resources").absolutePath

Gradle publish multiple independent artifacts

I've got a project that builds using Gradle and the ivy-publish plugin. In addition to building a JAR, build.gradle also executes a run task that executes XmlFileGenerator.main(), which generates 5 XML files (call them A, B, C, D, and E). I'm looking to publish each of these XML files to our Ivy repository; each should have the same group and version but a different module and a different filename, and each should have its own ivy.xml that lists only itself.
I'm able to set the filename of the file that's published, but the module name remains the same as my project's name, and as a result all of my XML files are published under the same module name instead of under independent ones.
So for example, I want A.xml to be published at {myLocalIvyRootDir}\my-group\A\{version}\xmls\A-{version}.xml and I want B.xml to be published at {myLocalIvyRootDir}\my-group\B\{version}\xmls\B-{version}.xml. But instead A is published at {myLocalIvyRootDir}\my-group\my-project\{version}\xmls\A-{version}.xml and B is published alongside it at {myLocalIvyRootDir}\my-group\my-project\{version}\xmls\B-{version}.xml.
Here's the relevant subset of build.gradle (showing only A but not B-E):
apply plugin: 'ivy-publish'
group = 'my-group'
publishing {
publications {
ivy(IvyPublication) {
artifact jar
}
aXml(IvyPublication) {
artifact('target/A.xml') {
name = 'A'
extension = 'xml'
type = 'xml'
}
}
}
}
mainClassName = 'my-group.my-project.XmlFileGenerator'
I've tried defining the module property on the publication with this code:
aXml(IvyPublication) {
module 'A'
artifact('target/A.xml') {
name = 'A'
extension = 'xml'
type = 'xml'
}
}
But I get the following error message:
> org.gradle.api.internal.MissingMethodException: Could not find method module() for arguments [A] on org.gradle.api.publish.ivy.internal.publication.DefaultIvyPublication_Decorated#32384c50.
And I've tried changing the rootProject.name dynamically with code like:
publishing {
publications {
ivy(IvyPublication) {
artifact jar
}
project.metaClass.getName {"A"}
aXml(IvyPublication) {
artifact('target/A.xml') {
name = 'A'
extension = 'xml'
type = 'xml'
}
}
}
}
That produced no errors, but also no change in behavior.
I feel like I'm probably just missing something small, but don't know what it is. Can anyone point me in the right direction?
It turned out that this particular project was still pointing to Gradle 1.6, before these properties were made available (they were added in 1.7). So all that was needed was to point to 1.7, and everything worked as intended.

renaming project names is not working

In my settings.gradle file, I originally had this which works
include 'server', 'webapp'
I modify it to this and it breaks
include 'server', 'webapp'
modifyProjectNames(rootProject, "sdi-")
def modifyProjectNames(project, prefix) {
project.children.each {
it.name = prefix + it.name
}
}
It says "project with path ':webapp' could not be found in root project 'SDI'" so I modify my file to this instead
include 'master', 'sdi-webapp'
modifyProjectNames(rootProject, "sdi-")
def modifyProjectNames(project, prefix) {
project.children.each {
it.name = prefix + it.name
}
}
I get the same exact failure yet I have no :webapp in settings.gradle so it must be my main gradle build file so I rename that project now as well to :sdi-webapp so now the error changes slightly to
Project with path ':sdi-webapp' could not be found in root project 'SDI'.
I have a project structure like so
SDI
- server
- webapp
and I want all the project names to be sdi-server and sdi-webapp, etc. etc. BUT I want the directories to be called
/SDI/server instead of /SDI/sdiserver
/SDI/webapp instead of /SDI/sdiwebapp
ie. no sense in being redundant there.
EDIT
I tried the first answer on another project like so
include 'master', 'toneserver','webserver'
modifyProjectNames(rootProject, "sdi-")
def modifyProjectNames(project, prefix) {
project.children.each {
it.name = prefix + it.name
}
}
rootProject.children.each {proj ->
proj.projectDir = file(proj.name - "sdi-")
}
It simply failed with
Project with path ':toneserver' could not be found in root project 'stserver'
It kind of works if you go through your build.gradle file and modify EVERY reference to also have the prefix which kind of defeats that programming rule of trying not to repeat yourself :( :( so if I ever want to modify the prefix, I have to do an ugly find and replace :(...ick. but it does work.
thanks,
Dean
as a workaround, you ca try to use the projectnames you prefere (SDI-...) in your include calls and change the projectDir of each subproject:
rootproject.children.each{proj->
proj.projectDir = file(proj.name - "SDI-")
}
cheers,
rene

Resources