Could not resolve: org.jcuda:jcudnn:10.2.0 (Gradle)

My project recently broke. Here’s my situation:

I have added jCuda and jCudnn as dependencies to my Gradle project like this:

implementation "org.jcuda:jcuda:$jCudaVersion"
implementation "org.jcuda:jcudnnn:$jCudaVersion"
runtimeOnly "org.jcuda:jcuda-natives:$jCudaVersion:$jCudaClassifier"
runtimeOnly "org.jcuda:jcudnn-natives:$jCudaVersion:$jCudaClassifier"

jCudaVerson and jCudaClassifier are created as project.ext variables and use the the exact methods described here to get their values.

I updated the project to use the Gradle wrapper 6.5. It is the only thing I changed recently that’s related to dependencies. Now Gradle spits out this error:

<ij_msg_gr>Project resolve errors<ij_msg_gr><ij_nav>C:\Users\my-name\Java\aq-fishes\build.gradle<ij_nav><i><b>root project 'aq-fishes': Unable to resolve additional project configuration.</b><eol>Details: org.gradle.internal.resolve.ArtifactNotFoundException: Could not find jcuda-natives-${jcuda.os}-${jcuda.arch}.jar (org.jcuda:jcuda-natives:10.2.0).</i>
Could not resolve: org.jcuda:jcudnnn:10.2.0

What’s going on here?

Disclaimer: As noted in the README, the Gradle build infos have been provided by contributors. Now I tried to set up a Gradle-based project, and after a few minutes, I was a blink away from losing composure and just say „Use Maven instead of Gradle“, but … *sigh*… here we go:

I managed to set up a very basic Gradle-based test project with JCuda and JCudnn, and it worked … and I know: That doesn’t help you much. My test spilled out a warning saying

The compile configuration has been deprecated for dependency declaration. This will fail with an error in Gradle 7.0. Please use the implementation configuration instead. Consult the upgrading guide for further information: https://docs.gradle.org/6.5.1/userguide/upgrading_version_5.html#dependencies_should_no_longer_be_declared_using_the_compile_and_runtime_configurations

Ohhh-kayyy… (I don’t know what that means…)

Trying to zoom into the issue:

  • It complains about org.jcuda:jcuda-natives:10.2.0 and still says that it couldn’t find org.jcuda:jcudnn:10.2.0 - are these different error messages?
  • Is it right that the project did work with a previous Gradle version (including using JCuda and JCudnn with Gradle)? So the error appeared only due to the Gradle update?
  • (If so: Which was your previous Gradle version?)
  • Could it be that this $jCudaVersion:$jCudaClassifier part of the strings is simply no longer supported in the most recent version, and all this has to be solved differently?

That warning always comes up with Gradle. It has never broken anything for me :P.

  1. I don’t get it either.
  2. The only thing I changed about the project was the Gradle version, which is why I think it’s Gradle-related.
  3. I don’t know exactly what version it was before I upgraded to 6.5.0, but probably still 5.x.
  4. These are basically the exact import instructions used from the official jCUDA guide. It’d be funny if they’d broke something and not yet updated their tutorial.

PS: I’ve tried the usual steps: Restarting IDE, deleting caches, rebuilding project, reimporting project.

I tried it with these dependencies and it works:

dependencies {

    def jCudaClassifier = getOsString() + "-" + getArchString()
    def jCudaVersion = "10.2.0"

   
    implementation ("org.jcuda:jcuda:$jCudaVersion"){
        transitive = false
    }
    implementation ("org.jcuda:jcudnn:$jCudaVersion") {
        transitive = false
    }
    runtimeOnly ("org.jcuda:jcuda-natives:$jCudaVersion:$jCudaClassifier")
    runtimeOnly ("org.jcuda:jcudnn-natives:$jCudaVersion:$jCudaClassifier")
}

The important thing is the transitive = false. If this is not set, gradle always tries to load the transitive dependencies. In this case for org.jcuda:jcuda:10.1.0 in the pom is the following dependency declared:

<dependency>
   <groupId>org.jcuda</groupId>
   <artifactId>jcuda-natives</artifactId>
   <version>${project.version}</version>
   <classifier>${jcuda.os}-${jcuda.arch}</classifier>
</dependency>

The two variables ${jcuda.os}-${jcuda.arch} are defined in the parent pom but only in specific profiles. I don’t know jcuda, but if this dependency is only for the build and should not be used transitive, it might be a good idea to set scope to provided.

That sounds reasonable.

As I said, I added these instructions for Gradle to the README only based on comments from contributors, but hadn’t tested them myself. And if I understood the warning message correctly, then the exact approach that is used there (using the dependencies { compile(...) ... } part) is has been deprecated for quite a while in Gradle, and may now have basically been replaced by the implementation/runtimeOnly lines that you posted.

Considering that the compile(...)-based approach required the transitive=false flag, this might also be the case for the implementation-based approach, as @ButAlive showed.

A very rough summary of what is probably going on there:

  • jcuda-10.2.0 requires jcuda-natives-${jcuda.os}-${jcuda.arch}-10.2.0 (which contains the native libraries for different operating systems)
  • When just using implementation..., then Gradle looks at the jcuda-10.2.0 POM, and finds this dependency to the natives, but at this point, it does not know what jcuda.os and jcuda.arch are
  • When using implementation..{ transitive=false }, it ignores this dependency
  • In order to run the application, the natives-dependency is required, though. So the runtimeOnly... lines are used to tell Gradle: „Yeah, here are the dependencies that you skipped, and I also give you the exact classifier (i.e. the ${jcuda.os}-${jcuda.arch} part) that you couldn’t figure out from the POM“

More detailed:

The main JCuda artifacts (like jcuda) contain JAR files that only contain the Java classes. But in order to run applications, the native libraries (like .DLL files on Windows) are also required. These native libraries are contained in jcuda-natives-* artifacts.

So the main artifacts have dependencies to the native artifacts - for example :

        <dependency>
            <groupId>org.jcuda</groupId>
            <artifactId>jcuda-natives</artifactId>
            <version>${project.version}</version>
            <classifier>${jcuda.os}-${jcuda.arch}</classifier>
        </dependency>

The jcuda.os and jcuda.arch placeholders will be filled by Maven, depending on the target system where it is run. This is done via profiles in the parent POM. So when compiling or running a project that needs jcuda-10.2.0 on Windows, it will have a dependency to jcuda-natives-10.2.0-windows-x86_64.jar.

But this works only for Maven. As far as I understood, the Gradle snippet from the README is supposed to „emulate“ this, as far as reasonably possible. According to the error message…

Could not find jcuda-natives-${jcuda.os}-${jcuda.arch}.jar (org.jcuda:jcuda-natives:10.2.0)

it doesn’t seem to be able to resolve this dependency. (What confuses me a bit is that it still does append this org.jcuda:jcuda-natives:10.2.0 to the error message - so it must have resolved the placeholders somehow…)

The resolving of placeholders works, project.version is always defined. But the profile activation depending on the OS in the parent pom, does not work with gradle. So jcuda.os and jcuda.arch are not set.