Hi, as i am trying to understand how the `runtimeC...
# community-support
g
Hi, as i am trying to understand how the
runtimeClasspath
works, i found something weird. we are on
8.11.1
, and running this task on different projects yield weird results:
Copy code
tasks.register('printMyTestClasspath') {
    dependsOn 'testClasses'

    doLast {
        def classpathUrls = sourceSets.test.runtimeClasspath.files
        classpathUrls.each {
            if (!it.toString().contains(".gradle")) { // to exclude external dependencies
                println "classpath file: $it"
            }
        }
    }
}
we have a setup where we have multiple builds depending on each other via
includeBuild
. what's weird is that using the above task in show inconsistent results: • sometime the
main
class directory is in there (
build/classes/java/main
) and sometime the jar file is in there (
build/libs/something-1.0.jar
) • similarly, this is the same for the included builds - i sometimes see the
classes/java/main
of the dependency project, and sometimes i see the
jar
• on repeated executions the results seem to stay the same. My questions are: • What could cause this? The
build.gradle
files are very similar in our different builds, i did not find any indication of what could cause this difference (sure, i could be missing something.) • Is this a bug? Or something that has been fixed in later versions? • Are there any workarounds that I can use to get some consistency? I want to write tooling that loads the test classes with the class loader, and then checks stuff on them - but i am getting random errors, based on weather some projects classpath has the
jar
or the
classes
...
c
i believe this is the expected behaviour (unable to locate doc reference atm). For referenced projects if there is a JAR artifact that will be used, otherwise the classes directory will be used.
g
my problem is that in a lot of cases the
jar
artifact is there on the
runtimeClasspath
, instead of the
classes
- even when the
jar
does not exist, which is causing errors...
c
is it possibly a race condition, that the JAR has not been built yet?
g
sure, but then how to resolve that race condition? adding
dependsOn "assemble"
kinda works for runtime dependencies, but it still does not build test runtime dependencies (like test fixtures). also - when and where does gradle decide to use the
jar
even if it does not exist yet? if it does not exist yet - how to trigger it's build? and i see cases where an included project has both it's
classes
dir and it's
jar
on the classpath...
v
It is actually the opposite of what @Chris Lee said. One of Gradle's biggest strengths is to avoid unnecessary work. So wherever possible is prefers to use the classes directory over the jar. If a project applies the
java-library
plugin, it gets additional secondary variants that expose for example the classes directory. Compilation tasks request the classes directory and compatibility rules allow to get the jar if no variant for the classes directory exist, so that if you depend on a project that does not apply the
java-library
plugin for example, you can still get the jar.
For some part (I don't remember which) you can indeed also configure to get the jar even if classes would be available. I think this had some performance reasons for very big projects on Windows filesystem or something like that.
The building or the jar or classes directories happen automatically, iff you properly wired inputs / outputs. If you did not, you might see absent files or stale files from previous runs.
Your shown example for example will only take care that things needed to do
testClasses
are built, which might of course miss things in the runtime classpath and on the ohter hand might build things without reason
Besides that explicit
dependsOn
is evil (unless a lifecycle task is on the left-hand side)
g
hey, tanks for responding. in the meantime i was trying stuff, and this seems to be working:
Copy code
tasks.register('dummyTest', Test) {
    exclude "**"
}

tasks.register('verifyTestClassPath') {
    dependsOn 'dummyTest'

    doLast {
        def classpathUrls = sourceSets.test.runtimeClasspath.files
        classpathUrls.each {
            if (!it.toString().contains(".gradle")) { // to exclude external dependencies
                if (it.name.endsWith('.jar') && !it.exists()) {
                    throw new GradleException("Classpath file does not exist: $it")
                }
            }
        }
    }
}
if i clean every project and run verifyTestClassPath when it depends on testClasses - it will fail. if i do the same but depend on the dummyTest - it won't fail. still yucky... 😛
is there a more reasonable way i can make a task actually force building all the stuff on the classpath?
v
That should not do anything. Why should your
Test
task cause anything to build? It does not do anything and does not have any inputs.
Besides that, probably just removing the evil
dependsOn
and just doing
inputs.files(configurations.testRuntimeClasspath)
g
That should not do anything.
Why should your
Test
task cause anything to build?
It does not do anything and does not have any inputs.
yet it still works for some reason :)
Besides that, probably just removing the evil
dependsOn
and just doing
inputs.files(configurations.testRuntimeClasspath)
okay, that seems to work too! and simpler! and makes more sense! thank you! 🙂
👌 1