How to share sub-projects among many other projects?


(Samantha Chan) #1

Hi, I am new to Gradle, and am running into issues while trying to share sub-projects among many other projects. My project structure looks as follows:

Project A
|
|--------------Project B
|--------------Project C
|--------------Project D

Project E
|
|--------------Project C
|--------------Project D

where:
Project D
|
|---------------Project C

Project C and Project D will be used in other projects very frequently, as they act as libraries for data classes and database utilities.

Project A settings.gradle file:

rootProject.name = 'ProjectA'

include ":ProjectB"
project(":ProjectB").projectDir = file("/home/myFolder/NetBeansProject/ProjectB")

include ":ProjectC"
project(":ProjectC").projectDir = file("/home/myFolder/NetBeansProject/ProjectC")

include ":ProjectD"
project(":ProjectD").projectDir = file("/home/myFolder/NetBeansProject/ProjectD")

Project A build.gradle file:

apply plugin: 'java'
apply plugin: 'application'

sourceCompatibility = '1.8'
[compileJava, compileTestJava]*.options*.encoding = 'UTF-8'

if (!hasProperty('mainClass')) {
    ext.mainClass = ''
}

repositories {
    mavenCentral()
}

dependencies {
    compile project (':ProjectB')
    compile project (':ProjectC')
    compile project (':ProjectD')
}

Likewise, all other projects have similar settings and build files, containing their respective sub-projects they are dependent on.

Unfortunately, it would seem that Gradle gets confused when multiple projects are dependent on the same sub-projects.

For example, Project E runs just fine, while Project A is unable to import any classes from any of its sub-projects.

Error Log:

Exception 1:
    org.netbeans.gradle.model.util.TransferableExceptionWrapper: org.gradle.api.internal.artifacts.ivyservice.DefaultLenientConfiguration$ArtifactResolveException: Could not resolve all files for configuration ':compileClasspath'.
Caused by: org.netbeans.gradle.model.util.TransferableExceptionWrapper: org.gradle.internal.resolve.ModuleVersionResolveException: Could not resolve project :ProjectC.
Caused by: org.netbeans.gradle.model.util.TransferableExceptionWrapper: org.gradle.internal.component.NoMatchingConfigurationSelectionException: Unable to find a matching configuration of project :ProjectC: None of the consumable configurations have attributes.
	at org.gradle.internal.component.model.LocalComponentDependencyMetadata.selectConfigurations(LocalComponentDependencyMetadata.java:122)

Exception 2:
org.netbeans.gradle.model.util.TransferableExceptionWrapper: org.gradle.api.internal.artifacts.ivyservice.DefaultLenientConfiguration$ArtifactResolveException: Could not resolve all files for configuration ':runtimeClasspath'.
Caused by: org.netbeans.gradle.model.util.TransferableExceptionWrapper: org.gradle.internal.resolve.ModuleVersionResolveException: Could not resolve project :ProjectC.
Caused by: org.netbeans.gradle.model.util.TransferableExceptionWrapper: org.gradle.internal.component.NoMatchingConfigurationSelectionException: Unable to find a matching configuration of project :ProjectC: None of the consumable configurations have attributes.

Exception 3:
org.netbeans.gradle.model.util.TransferableExceptionWrapper: org.gradle.api.internal.artifacts.ivyservice.DefaultLenientConfiguration$ArtifactResolveException: Could not resolve all files for configuration ':testCompileClasspath'.
Caused by: org.netbeans.gradle.model.util.TransferableExceptionWrapper: org.gradle.internal.resolve.ModuleVersionResolveException: Could not resolve project :ProjectC.
Caused by: org.netbeans.gradle.model.util.TransferableExceptionWrapper: org.gradle.internal.component.NoMatchingConfigurationSelectionException: Unable to find a matching configuration of project :ProjectC: None of the consumable configurations have attributes.

Exception 4:
org.netbeans.gradle.model.util.TransferableExceptionWrapper: org.gradle.api.internal.artifacts.ivyservice.DefaultLenientConfiguration$ArtifactResolveException: Could not resolve all files for configuration ':testRuntimeClasspath'.
Caused by: org.netbeans.gradle.model.util.TransferableExceptionWrapper: org.gradle.internal.resolve.ModuleVersionResolveException: Could not resolve project :ProjectC.
Caused by: org.netbeans.gradle.model.util.TransferableExceptionWrapper: org.gradle.internal.component.NoMatchingConfigurationSelectionException: Unable to find a matching configuration of project :ProjectC: None of the consumable configurations have attributes.

Project C and Project D both generate a Root Project folder that points to the settings and build files for Project E, but not for Project A.

Why is this happening? What shall I do in order to get Project A to work with its sub-projects?

Thank you all in advance for taking the time to help me with my Gradle issues!


(Sterling Greene) #2

Take a look at Composite builds. There are two other ways of dealing with this as well, you can just put everything in a single monolithic build (A-E all in the same build) or you can publish C & D as separate binary dependencies to a Maven repository.

For composite builds:
You would set things up so that Project C and Project D would be one build and Project A and Project E would be different builds.

In Project A's settings.gradle, you would do something like:

rootProject.name = "A"
include "B"
includeBuild '../path/to/CD'

In Project E's settings.gradle, you would have:

rootProject.name = "E"
includeBuild '../path/to/CD'

And in the “CD” project (alternatively, you could make D the root project):

rootProject.name = "CD"
include "C"
include "D"

Then in Project A and Project E, you would depend on the external coordinates of C and D.

So you would use:

dependencies {
   compile "com.example:C:1.0"
}

At runtime, Gradle would substitute the project dependencies as necessary.


(Samantha Chan) #3

Thank you for directing my attention to composite builds. I quickly tried what you have suggested, but I think I might have missed something.

Since Project C was already a sub-project of Project D to begin with, at first, I went with your suggestion to includeBuild Project D instead of creating a new ProjectCD, as follows:

settings.gradle for Project A:

rootProject.name = 'ProjectA'

include ":ProjectB"
project(":ProjectB").projectDir = file("/home/myPath/NetBeansProject/ProjectB")

includeBuild "/home/myPath/NetBeansProject/ProjectD"

and the build.gradle dependencies for Project A:

dependencies {
    compile project (':ProjectB')
    compile 'com.projects:ProjectD:1.0'
}

but that didn’t work for me…

Error Log:

org.gradle.tooling.BuildException: Could not run build action using Gradle distribution 'https://services.gradle.org/distributions/gradle-4.1-bin.zip'.
Caused by: org.gradle.internal.exceptions.LocationAwareException: Settings file '/home/myPath/NetBeansProjects/ProjectA/settings.gradle' line: 13
Caused by: org.gradle.api.GradleScriptException: A problem occurred evaluating settings 'ProjectA'.
Caused by: org.gradle.api.InvalidUserDataException: Included build 'ProjectD' must have a 'settings.gradle' file.

It’s saying it couldn’t find a settings.gradle file for ProjectD, but I’m positive it’s in the project directory for ProjectD. Would you know why this error is happening?

So I tried creating a Root Project ProjectCD and included Project C and Project D separately in the settings file:

ProjectCD settings.gradle:

rootProject.name = 'ProjectCD'

include ":ProjectD"
project(":ProjectD").projectDir = file("/home/myPath/NetBeansProject/ProjectD")

include ":ProjectC"
project(":ProjectC").projectDir = file("/home/myPath/NetBeansProject/

def subDirs = rootDir.listFiles(new FileFilter() {
    public boolean accept(File file) {
        if (!file.isDirectory()) {
            return false
        }
        if (file.name == 'buildSrc') {
            return false
        }
        return new File(file, 'build.gradle').isFile()
    }
});

subDirs.each { File dir ->
    include dir.name
}

I left the build.gradle and common.gradle files at its default state (generated by NetBeans Gradle Plugin).

But this will not work either. ProjectCD will not compile.

Error Log:

org.gradle.tooling.BuildException: Could not run build action using Gradle distribution 'https://services.gradle.org/distributions/gradle-4.1-bin.zip'.
Caused by: org.gradle.internal.exceptions.LocationAwareException: Script '/home/myPath/NetBeansProjects/ProjectCD/common.gradle' line: 20
Caused by: org.gradle.api.GradleScriptException: A problem occurred evaluating script.
Caused by: org.gradle.api.IllegalDependencyNotation: Supplied String module notation 'com.projects' is invalid. Example notations: 'org.gradle:gradle-core:2.2', 'org.mockito:mockito-core:1.9.5:javadoc'.

I must have some fundamental misunderstanding in how this is supposed to work. I have never created a “Root Project” before. With the exception of this Project CD, none of the projects I’ve created previously are technically “Root Projects”. They are all Single Gradle Projects. I wonder whether that might be a problem, since the dependency compile line with the external coordinates for Project D seems to require a Maven version number that I did not provide in the build.gradle file.

Sorry, I don’t know how to proceed from here. Do you have any idea why none of these solutions seem to be working for me?

Thank you again for your time. I really appreciate the help!


(Samantha Chan) #4

I’ve finally figured out what my problem was… Gradle was running Java (oracle) while NetBeans was running OpenJDK, which prevented Gradle from compiling correctly. All of those methods worked fine after I pointed NetBeans to the correct JDK. Sorry for the confusion. I did learn a lot about Gradle through all this troubleshooting. Thanks again!