Skip to main content
  1. About
  2. For Teams
Asked
Modified 1 month ago
Viewed 8k times
12

An integral part of the Java Native Interface (JNI), is the bridging of JVM code and native code through C headers. The way to generate these header files used to be quite straight forward: simply call the command line utility javah on class files. This process would then generate prototypes for any method marked with the native modifier.

As of Java 10 however, the javah utility has been removed, and its suggested replacement is a new flag "-h" to javac. The replacement works fine if one has the Java source files available, however falls short in cases where only compiled class files are available. (The issue that sparked this question is that I'm trying to generate JNI bindings from Scala sources. My current approach has been to compile them first and then run javah over the resulting class files.)

In a situation where only compiled class files are available, is there a way to generate C header files, similar to the way javah used to?

4
  • 1
    Might just be hypothetical, but is compiling the scala sources into a jar and then using the jar on classpath for javac -h flag not an option?
    Naman
    –  Naman
    2018-03-27 07:32:03 +00:00
    Commented Mar 27, 2018 at 7:32
  • 1
    There is an ugly solution based on javap. You can decompile class, leave only native things and use javac to compile things back. But it's quite ugly approach.
    Oo.oO
    –  Oo.oO
    2018-03-27 08:51:27 +00:00
    Commented Mar 27, 2018 at 8:51
  • @nullpointer, unfortunately your proposed solution doesn't work. javac really requires native modifiers in source code. Currently mko's answer is looking the most promising.
    Jakob Odersky
    –  Jakob Odersky
    2018-03-27 18:34:38 +00:00
    Commented Mar 27, 2018 at 18:34
  • 1
    Similar question, regarding use of javah for Kotlin: stackoverflow.com/questions/48816188/…
    Jakob Odersky
    –  Jakob Odersky
    2018-04-11 03:03:16 +00:00
    Commented Apr 11, 2018 at 3:03

4 Answers 4

8

You can always go via javap. I know, I know. It's ugly, has lots of assumptions, but in case you desperately need to generate headers for lots of files it might be the only option.

#!/bin/bash

# FIRST_ARG - full class name (with package)
# SECOND_ARG - class path

CLASS_NAME=`javap -cp $2 $1 | \
  grep -v "Compiled from" | \
  grep "public class" | \
  cut -f3 -d" " | \
  awk -F"." '{ print $NF }'`

PACKAGE_NAME=`javap -cp $2 $1 | \
  grep -v "Compiled from" | \
  grep "public class" | \
  cut -f3 -d" " | \
  sed s/\.${CLASS_NAME}$//`

DIR_NAME=`echo $PACKAGE_NAME | sed 's|\.|/|g'`
mkdir -p java_jni/${DIR_NAME}

JAVA_FILE_NAME="java_jni/${DIR_NAME}/${CLASS_NAME}.java"

echo "package ${PACKAGE_NAME};" > ${JAVA_FILE_NAME}
echo "public class ${CLASS_NAME} {" >> ${JAVA_FILE_NAME}

javap -cp $2 $1 | grep "native" | while read line; do
  param=0
  comma=`echo $line | grep "," | wc -l`
  while [ $comma -gt 0 ]; do
    line=`echo $line | sed "s/,/ param_${param}|/"`
    let param=param+1
    comma=`echo $line | grep "," | wc -l`
  done
  line=`echo $line | sed "s/)/ param_${param})/" | sed 's/|/,/g'`
  echo "  $line" >> ${JAVA_FILE_NAME}
done

echo "}" >> ${JAVA_FILE_NAME}

mkdir -p c_header
javac -h c_header ${JAVA_FILE_NAME}

I bet it can be made way more beautiful.

For me, now, when I slowly start to think about inevitable move towards Java 10, and all these cases, where I might be surprised by non existing Java source code, I think it's not a bad idea to have some tool at my disposal. Just in case.

Sign up to request clarification or add additional context in comments.

2 Comments

This do not appear to work if the native methods take parameters (javac complains with error: <identifier> expected)
Thanks for spotting it @SalomonBRYS !!
6

We can use gjavah to generate JNI header files.

3 Comments

Is there any proper documentation how to setup and use gjavah?
@Andi You just need to run it using java -jar, it accepts parameters similar to javah. You can also use -help to view help.
i switched to the usage of java -h <dir> instead
-1

You are forced to compile the class with javac to get the headers.
The command consists of three parts.
-h is the directory where the headers go.
-d is the directory where classes go.
Finally comes the source file.

So new javah built inside javac works like this:
javac.exe -h C:/output/file_headers -d C:/output/classes C:/src/HelloC.java

1 Comment

And that works with scala source files?
-6

The best solution is just install a jdk8, i think. And no need to uninstall jdk10, just modify the environment variable.

Comments

Your Answer

Post as a guest

Required, but never shown

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.

Morty Proxy This is a proxified and sanitized view of the page, visit original site.