Set EclipseProject.referencedProjects using dependencies in Gradle - gradle

I am creating Eclipse project files as shown:
eclipse {
project {
natures 'org.eclipse.jdt.core.javanature'
buildCommand 'org.eclipse.jdt.core.javabuilder'
}
classpath {
downloadSources true
downloadJavadoc true
}
}
I have a multi-project gradle build where projects reference each other and 3rd party libs. For projectA, its dependencies are:
dependencies {
compile project(':projectB')
compile project(':projectC')
compile "com.google.guava:guava:${VER_GUAVA}"
}
This works great, except that the generated projects don't reference each other. It builds just fine, but it means that if I refactor something in projectB, references in projectA aren't refactored with it. The fix is apparently to set the referencedProjects variable of the eclipse configuration, but I'd like for this to be automatically populated based on the dependencies. Something like:
eclipse {
project {
referencedProjects dependencies.grep(dep is project)
...
Anybody have any hints?

This is how I ended up fixing it:
// add all project dependencies as referencedProjects
tasks.cleanEclipse.doLast {
project.configurations.stream()
.flatMap({config -> config.dependencies.stream()}) // get all deps
.filter({dep -> dep instanceof ProjectDependency}) // that are Projects
.forEach({dep -> eclipse.project.referencedProjects.add(dep.name)}) // and add to referencedProjects
}
// always create "fresh" projects
tasks.eclipse.dependsOn(cleanEclipse)
Probably ought to mention that I'm using the wuff plugin because I'm in an OSGI environment, and it does tricks on the dependencies. That might be the reason that I'm not getting this automatically, but the above snippet fixes it pretty easily.

Related

How to create multi project fat jar as bundle of some libraries with buildSrc

First of all, sorry for my poor english.
Goal
I want create multi project containing some custom libraries as subproject with gradle.
For centralized dependency version control, using buildSrc and setting versions (spring-boot, detekt, ktlint etc.)
my-core-project(root)
+buildSrc
+-src/main/kotlin
+--int-test-convention.gradle.kts
+--library-convention.gradle.kts
+library-A
+-src
+--main/kotlin
+--test/kotlin
+-build.gradle.kts
+library-B
+-src
+--main/kotlin
+--test/kotlin
+-build.gradle.kts
+build.gradle.kts
+setting.gradle.kts
buildSrc contains common tasks for libraries(integration test, detekt, etc.)
library-A and library-B are custom libraries based on spring boot.
There is no application module or any main method.
my goal is using method of library-A and/or library-B with another separated project with adding my-core-project to dependency.
Problem
./gradlew build created 3 jar files
my-core-project
+build/libs
+-my-core-project.jar
+library-A
+-build/libs
+--library-A.jar
+library-B
+-build/libs
+--library-B.jar
copied 3 jar files to libs directory under project which actually using these library,
tried adding dependency created jar
with implementation(files("libs/library-A.jar")), class and methods are resolved well.
but with implementation(files("libs/my-core-project.jar")),
class and methods are not unresolved.
when check my-core-project.jar, recognized that any information of sub projects contained.
Here is my setting.gradle.kts and build.gradle.kts of root directory.
# setting.gradle.kts
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
rootProject.name = "my-core-project"
include(
"library-A",
"library-B"
)
# build.gradle.kts
plugins {
id("java-library")
id("io.spring.dependency-management")
}
group = "com.demo"
version = "0.0.1-SNAPSHOT"
dependencies {
api(project(":library-A"))
api(project(":library-B"))
}
repositories {
mavenCentral()
}
Tried things
In my opinion, my-core-project.jar should be fatJar(uberJar),
so i added FatJar task like this
val fatJar = task("fatJar", type = Jar::class) {
archiveBaseName.set("${project.name}-fat")
from(configurations.runtimeClasspath.get().map { if (it.isDirectory) it else zipTree(it) })
with(tasks.jar.get() as CopySpec)
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
tasks {
"build" {
dependsOn(fatJar)
}
}
but cannot resolve class and method,
additionally occurs version conflict with other dependency of projects using this package, due to library-A created as fatJar too.
Question
Is there a simple way packaging/bundling sub-modules into one jar file?
if there are tasks like this already in gradle, prefer to use that.
Modifying fatJar task like "add jar of submodules, not contained dependencies" can solve this problem?(even couldn't try completely newbie to gradle and kts.)
if so, can you show me how to modify task?
tried shadowJar already. that solved version-conflict problem with relocate option. but still couldn't resolve package in library-A
If structure has problem, is there a good practice/example for "bundle of library"?
thanks for reading.
TL;DR
If someone faced this problem, try set archive name shorter than current one.
For someone who faced same problem, sharing some informations.
as result, resolved this problem.(maybe even not problem)
current shadowJar configure is as following
tasks.named<ShadowJar>("shadowJar").configure {
archiveBaseName.set("shorten-name")
archiveClassifier.set("")
exclude("**/*.kotlin_metadata")
exclude("**/*.kotlin_builtins")
}
exclude kotlin_metadata, kotlin_builtins
set shorten name(original project name was 30 long characters)
I have no idea but shorten jar file name has no problem.
Interesting one is, upload in artifactory package with original(long) name worked well.
I don't know Gradle declaring dependency with files has length constraints.
implementation(files("path/to/package"))
And now it works well with original name with local jar package file.

Building a project with Groovy extension methods in it

I'm trying to build a simple Groovy project which contains a package with a Groovy extension method.
When I try to compile the project the classes using the extension method can't find it. The descriptor and the class are picked up correctly by the IDE.
I guess the problem is that the extension method is needed at compile time by which it probably hasn't been compiled yet.
I've tried creating different sourceSets but with no success.
dependencies {
compile 'org.codehaus.groovy:groovy:2.4.7'
compile 'org.membrane-soa:service-proxy-core:4.2.2'
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
extensionCompile 'org.codehaus.groovy:groovy:2.4.7'
}
sourceSets {
extension {
groovy {
include '**/ClosureExtension.groovy'
}
}
main {
groovy {
compileClasspath += extension.output
}
}
}
The extension method lives in src/main/groovy/com/predic8/membrane/dsl/extension and the rest of the project in src/main/groovy/com/predic8/membrane/dsl/.
P.S.: The project classes are all annotated with #CompileStatic.
One approach: separate the extension code into its own project, and produce a jar from that project. Then, in another project, the DSL code can use that jar and specify the extension jar as a dependency.
This would make a huge answer, so I mocked it up and placed it on GitHub here (with all due credit to mrhaki's post). There are two projects: extension and dsl. Assuming you understand multi-project builds in Gradle, the essence is this line from dsl/build.gradle:
compileGroovy.dependsOn tasks.getByPath(':extension:jar')

Gradle - Make existing android project a child of other project and run "gradlew tasks"

Part 1.
I have an existing Android project which is built with gradle.
settings.gradle looks like this:
include ':android', ':androidTest', ':androidTestApp'
build.gradle looks like this:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
}
}
task wrapper(type: Wrapper) {
gradleVersion = '2.4'
}
task customClean(dependsOn: [':android:clean', ':androidTest:clean', ':androidTestApp:clean']) {
}
allprojects {
repositories {
mavenCentral()
mavenLocal()
}
}
What I need to do is a create a project that is a parent of this android project and runs this android project tasks. I add to settings.gradle of new root project only include 'android-sdk:android'which is all I need for now. And everything works fine, I can run tasks of android project like this gradlew androidBuild:
task androidBuild(dependsOn: 'android-sdk:android:assemble') << {
group unitySdkGroupName
}
Problems arise when I run gradlew tasks on the new root projects. I get
Caused by: org.gradle.api.UnknownTaskException: Task with path ':android:clean' not found in project ':android-sdk'.
I know I can fix this by removing colons like this, but I want to somehow make tasks command work without changing android project if possible.
task customClean(dependsOn: ['android:clean', 'androidTest:clean', 'androidTestApp:clean']) {
}
Part 2.
When I add to settings.gradle of root project other nested projects
include 'android-sdk:androidTest'
include 'android-sdk:androidTestApp'
I get errors here in dependencies sections of child projects that are dependent on android module (this is androidTest module):
dependencies {
compile project(path: ":android")
compile 'com.google.android.gms:play-services:7.3.0'
}
and getting this when I refresh projects (again seems like relative paths issue):
Error:(30, 0) Project with path ':android' could not be found in project ':android-sdk:androidTest'.
So the question is - can I make gradlew tasks command work properly without modifying the underlying android project and how can it be done the most elegant way?
Unfortunately: no. Nested multiproject builds are not possible. I should add yet, but although intention to support them has been shown, there is absolutely no indication of when this feature can be expected.
See https://discuss.gradle.org/t/nested-multimodule-builds-with-a-flat-directory-layout/7509/13
I could not even find a feature request at https://discuss.gradle.org/c/bugs

Gradle: assemble output of a specific configuration

For a project, I have a custom configuration which should simply extend the default one, both in terms of java sources and in terms of dependencies.
This is what it looks like at the moment:
configurations {
// Tools inherits everything from default and adds on
toolsCompile.extendsFrom(compile)
}
sourceSets {
main {
java {
srcDirs = ['src']
}
}
tools {
java {
// Tools extends on the core sources
srcDirs = sourceSets.main.java.srcDirs + ['src-tools']
}
}
}
dependencies {
compile libs.a
compile libs.b
compile libs.c
// Tools adds on the dependencies of the default configuration
toolsCompile libs.d
toolsCompile libs.e
}
I know I could have also used sub-projects for this. I gave up on it, after trying, because I can't get it work properly together with the Eclipse integration plugin (it works fine when used from command line).
I have a couple of questions on the solution above:
Is the way I extend tools.java.srcDirs correct? Is there a more elegant way?
EDIT: Apparently it is not correct, as gradle eclipse generates a .classpath with a duplicate entry for src. Help please?
After I created my tools configuration, I know I can for example use it as a dependency from another project, as in compile project(path: ':myproject', configuration: 'tools'). What do I need to add if I instead want to get the output of the assemble task for my tools configuration? Do I have to make an explicit task for that? The task toolsClasses is created automatically, but not toolsAssemble or toolsBuild.

Gradle - can I include task's output in project dependencies

I have a task that generates java sources and a set of jars from these sources (say, project a). I would like to export these jars to dependent projects (say, project b). So here's roughly what I have right now:
//a.gradle
configurations{
generatedJars
}
task generateJars(type: JavaExec) {
//generate jars ...
outputs.files += //append generated jars here
}
dependencies{
generatedJars generateJars.outputs.files
}
//b.gradle
dependencies{
project(path: ':a', configuration: 'generatedJars')
}
It works OK, except that adding generateJars.outputs.files as a dependency does not tell gradle that it has to run generateJars task when there are no jars generated yet. I have tried adding the task itself as a dependency hoping that it would work in the same way as it does when you add a jar/zip task to an artifact configuration (e.g. artifacts{ myJarTask }), but it throws an error telling me that I cannot do that. Of course I can inject the generateJars task somewhere in the build process before :b starts evaluating, but that's clumsy and brittle, so I would like to avoid it.
I feel like I should be adding the generated jars to artifacts{ ... } of the project, but I am not sure how to make them then visible to dependent projects. Is there a better way of achieving this?
Dependent projects (project b) will need to do setup IntelliJ IDEA module classpath to point to project a's generated jars. Something rather like this (pseudo-code):
//b.gradle
idea{
module{
scopes.COMPILE.plus += project(path: ':a', configuration: 'generatedJars').files
}
}
So far I have tried simply adding a project dependecy on :a's generatedJars in :b, but Idea plugin simply adds module :a as a module-dependency and assumes that it exports its generated jars (which is probably a correct assumption), therefore not adding the generated jars to :b's classpath.
Any help would be greatly appreciated!
First, do you need a separate configuration? That is, do you have clients of a that should not see the generated Jars? If not, you can add the generated Jars to the archives configuration, which will simplify things.
Second, the correct way to add the generated Jars to the configuration is (instead of the dependencies block):
artifacts {
generatedJars generateJars
}
This should make sure that the generateJars task gets run automatically when needed.
Third, I'd omit the += after outputs.files, although it might not make a difference. You should also add the necessary inputs.
Fourth, why do you need a JavaExec task to generate the Jars? Can you instead add the generated sources to some source set and let Gradle build them?
Fifth, IDEA doesn't have a concept corresponding to Gradle's project configuration dependencies. Either an IDEA module fully depends on another module, or not at all. You have two options: either use a module dependency and make the generated sources a source folder of the depended-on module (preferably both in the Gradle and the IDEA build), or pass the generated Jars as external dependencies to IDEA. In either case, you should probably add a task dependency from ideaModule to the appropriate generation task. If this still doesn't lead to a satisfactory IDEA setup, you could think about moving the generation of the Jars into a separate subproject.
For my use case, I had a C++ project which generated some native libraries which my java project needed to load in order to run.
In the project ':native' build.gradle:
task compile(type: Exec, group: 'build') {
dependsOn ...
outputs.files(fileTree('/some/build/directory') {
include 'mylib/libmy.so'
})
...
}
In project java application build.gradle:
configurations {
nativeDep
}
// Add dependency on the task that produces the library
dependencies {
nativeDep files(project(':native').tasks.findByPath('compile'))
}
// Unfortunately, we also have to do this because gradle will only
// run the ':native:compile' task if we needed the tasks inputs for another
// task
tasks.withType(JavaCompile) {
dependsOn ':native:compile'
}
run {
doFirst {
// Use the configuration to add our library to java.library.path
def libDirs = files(configurations.nativeDep.files.collect {it.parentFile})
systemProperty "java.library.path", libDirs.asPath
}
}

Resources