Using custom JNI libraries

Our team wrote some code in C++ that we would like to use on our robot, but we’re using Java for our main codebase. Is using JNI with GradleRIO possible for custom stuff? If not, how can we write our own vendordeps JSON with JNI?

1 Like

Pathfinder 2 is a pretty good example of using JNI with GradleRIO. While Jaci has mentioned here that the library isn’t ready for FRC use, the JNI bindings are a good example to get you where you need to be.

I’m struggling to understand some of the basics of getting this working. I tried building a HelloWorld version of this on a Windows machine but didn’t get very far. The Cross-compiler doesn’t seem to like that I am using a Windows JDK to build the binary for athenalinux.

The JNI library (which is a C/C++ library) must be built with the cross-compiler in order for it to be able to be loaded on the RoboRIO. The JDK has no relationship to that.

So here is at least one version of wrong that I have so far:

I stripped out all of the robot java code and added in some basic JNI to bridge the Java and CPP. I added in the following to the build.gradle:

model {
  components {
    bridge(JniNativeLibrarySpec) { // Use JniNativeLibrarySpec to get a JNI library
    //   targetPlatform wpi.platforms.roborio
      enableCheckTask true // Set to true to enable a JNI check task. This will search all generated JNI headers, and check to ensure their symbols exist in the native library
      javaCompileTasks << compileJava // set javaCompileTasks to any java compile tasks that contain your JNI classes. It is a list of tasks
      jniCrossCompileOptions << JniCrossCompileOptions('athena')
      // See below for more JniCrossCompileOptions
      sources {
        cpp {
            source {
                srcDirs 'src/main/cpp'
                include '**/*.cpp'
            }
            exportedHeaders {
                srcDir 'src/main/include'
                if (project.hasProperty('generatedHeaders')) {
                    srcDir generatedHeaders
                }
                include '**/*.h'
            }
        }
      }
    }
  }
}

This built but I have .dll, .exp, and .lib files in the libs directory. I was expecting to see .so files. Any idea where I went wrong?

Thanks,

You commented out the targetPlatform line, which is what triggers it to use the cross-compiler.

Yes. That is another version of wrong that I cannot figure out. If I leave that uncommented, the build fails with the following error:

Task :compileBridgeDebugSharedLibraryBridgeCpp FAILED
In file included from C:\Users\warre\FRC_2019_Projects\JNIRobotTest\src\main\include/example_JniBridge.h:2:0,
                 from C:\Users\warre\FRC_2019_Projects\JNIRobotTest\src\main\cpp\example_JniBridge.cpp:2:
C:\Users\Public\frc2019\jdk\include/jni.h:45:20: fatal error: jni_md.h: No such file or directory
 #include "jni_md.h"
                    ^
compilation terminated.

This was the reason why I was wondering if there was a special jdk that was getting used because it can’t seem to find the jni_md.h file.

Now, I have tried to also use this file from the win32 folder:

model {
  components {
    bridge(JniNativeLibrarySpec) { // Use JniNativeLibrarySpec to get a JNI library
      targetPlatform wpi.platforms.roborio
      enableCheckTask true // Set to true to enable a JNI check task. This will search all generated JNI headers, and check to ensure their symbols exist in the native library
      javaCompileTasks << compileJava // set javaCompileTasks to any java compile tasks that contain your JNI classes. It is a list of tasks
      jniCrossCompileOptions << JniCrossCompileOptions('athena')
      // See below for more JniCrossCompileOptions
      sources {
        cpp {
            source {
                srcDirs 'src/main/cpp'
                include '**/*.cpp'
            }
            exportedHeaders {
                srcDirs 'src/main/include', 'C:/Users/Public/frc2019/jdk/include/win32'
                if (project.hasProperty('generatedHeaders')) {
                    srcDir generatedHeaders
                }
                include '**/*.h'
            }
        }
      }
    }
  }
}

But that throws a lot of what look windows type compatibility errors:

Example:
C:\Users\Public\frc2019\jdk\include\win32/jni_md.h:29:29: error: expected constructor, destructor, or type conversion before ‘(’ token
#define JNIEXPORT __declspec(dllexport)

Thanks,

You need a linux jni_md.h. You can find it here: https://github.com/wpilibsuite/gradle-jni/tree/master/src/main/resources/arm-linux-jni

You might be able to use/configure gradle-jni to make this easier. See the readme of that repo for an example. https://github.com/wpilibsuite/gradle-jni

Thank you so much! That did the trick.