Java subprojects classpath using classes dir instead of jars


(Jean-Baptiste Quenot) #1

Hi all,

Consider the following directory structure:

.
├── a
│   └── build.gradle
├── b
│   └── build.gradle
└── settings.gradle

Here is settings.gradle:

include "a", "b"

Here is a/build.gradle:

apply plugin: "java"

dependencies {
  compile project(":b")
}

task showcp << {
  println sourceSets.main.runtimeClasspath.join(':')
}

Here is b/build.gradle:

apply plugin: "java"

When I run gradle :a:showcp here is the output:

/Users/jbq/test-gradle/a/build/classes/main:/Users/jbq/test-gradle/a/build/resources/main:/Users/jbq/test-gradle/b/build/libs/b.jar

BUILD SUCCESSFUL

So as you can see the dependency from projet A to project B is added as a JAR entry in the classpath. Now what if I want to use the build classes dir instead of the JAR?

This is to achieve a better integration with IDEA for Java development. I want to let the IDE build the classes, and yet use Gradle to run my application without rebuilding the jars because it’s too slow.

Any advice? Any hint on how to achieve this without hardcoding the runtime classpath?


(Jean-Baptiste Quenot) #2

Replying to myself:

With the following a/build.gradle file I could achieve what I want:

apply plugin: "java"

repositories {
  mavenCentral()
}

dependencies {
  compile group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: '2.8.0'
  compile project(":b")
}

task showcp() {
  println "genuine cp: " + sourceSets.main.runtimeClasspath.join(':')

  def cp = []

  cp.add(sourceSets.main.output.classesDir)
  cp.add(sourceSets.main.output.resourcesDir)

  configurations.runtime.getAllDependencies().each { dep ->
    if (dep instanceof ProjectDependency) {
      def depProj = dep.getDependencyProject()
      def proj = project(depProj.path)
      cp.add(proj.sourceSets.main.output.classesDir)
      cp.add(proj.sourceSets.main.output.resourcesDir)
    }
    else
      project.configurations.runtime.files(dep).each { file ->
        cp.add(file)
      }
  }

  println " better cp: " + cp.join(":")
}

Sample output:

genuine cp: /Users/jbq/test-gradle/a/build/classes/main:/Users/jbq/test-gradle/a/build/resources/main:/Users/jbq/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.8.0/eeed20590bf2a6e367e6e5ce33f44d353881fa23/jackson-core-2.8.0.jar:/Users/jbq/test-gradle/b/build/libs/b.jar
 better cp: /Users/jbq/test-gradle/a/build/classes/main:/Users/jbq/test-gradle/a/build/resources/main:/Users/jbq/.gradle/caches/modules-2/files-2.1/com.fasterxml.jackson.core/jackson-core/2.8.0/eeed20590bf2a6e367e6e5ce33f44d353881fa23/jackson-core-2.8.0.jar:/Users/jbq/test-gradle/b/build/classes/main:/Users/jbq/test-gradle/b/build/resources/main
:a:showcp UP-TO-DATE

BUILD SUCCESSFUL

(Alexander Dorokhov) #3

Thanks for the code! However, it does not include classes for second+ dependency level (only jars again). Here is recursive solution:

    def addDependencies(List<File> classesCp, Project p) {
        p.configurations.runtime.getAllDependencies().each { dep ->
            if (dep instanceof ProjectDependency) {
                def depProj = dep.getDependencyProject()
                def proj = project(depProj.path)
                classesCp.add(proj.sourceSets.main.output.classesDirs)
                classesCp.add(proj.sourceSets.main.output.resourcesDir)
                addDependencies(classesCp, proj)
            } else {
                p.configurations.runtime.files(dep).each { file ->
                    classesCp.add(file)
                }
            }
        }
    }

    task showcp() {
        def cp = []
    
        cp.addAll(sourceSets.main.output.classesDirs.files)
        cp.add(sourceSets.main.output.resourcesDir)
    
        addDependencies(cp, project)
    
        println " better cp: " + cp.join(":")
    }