24.4. Building C Header and Stub Files: the JavaH Builder

You can generate C header and source files for implementing native methods, by using the JavaH Builder. There are several ways of using the JavaH Builder. One typical invocation might look like:

classes = Java(target = 'classes', source = 'src/pkg/sub')
JavaH(target = 'native', source = classes)
      

The source is a list of class files generated by the call to the Java Builder, and the target is the output directory in which we want the C header files placed. The target gets converted into the -d when SCons runs javah:

% scons -Q
javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java src/pkg/sub/Example3.java
javah -d native -classpath classes pkg.sub.Example1 pkg.sub.Example2 pkg.sub.Example3

In this case, the call to javah will generate the header files native/pkg_sub_Example1.h, native/pkg_sub_Example2.h and native/pkg_sub_Example3.h. Notice that SCons remembered that the class files were generated with a target directory of classes, and that it then specified that target directory as the -classpath option to the call to javah.

Although it's more convenient to use the list of class files returned by the Java Builder as the source of a call to the JavaH Builder, you can specify the list of class files by hand, if you prefer. If you do, you need to set the $JAVACLASSDIR construction variable when calling JavaH:

Java(target='classes', source='src/pkg/sub')
class_file_list = [
    'classes/pkg/sub/Example1.class',
    'classes/pkg/sub/Example2.class',
    'classes/pkg/sub/Example3.class',
]
JavaH(target='native', source=class_file_list, JAVACLASSDIR='classes')
      

The $JAVACLASSDIR value then gets converted into the -classpath when SCons runs javah:

% scons -Q
javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java src/pkg/sub/Example3.java
javah -d native -classpath classes pkg.sub.Example1 pkg.sub.Example2 pkg.sub.Example3

Lastly, if you don't want a separate header file generated for each source file, you can specify an explicit File Node as the target of the JavaH Builder:

classes = Java(target='classes', source='src/pkg/sub')
JavaH(target=File('native.h'), source=classes)
      

Because SCons assumes by default that the target of the JavaH builder is a directory, you need to use the File function to make sure that SCons doesn't create a directory named native.h. When a file is used, though, SCons correctly converts the file name into the javah -o option:

% scons -Q
javac -d classes -sourcepath src/pkg/sub src/pkg/sub/Example1.java src/pkg/sub/Example2.java src/pkg/sub/Example3.java
javah -o native.h -classpath classes pkg.sub.Example1 pkg.sub.Example2 pkg.sub.Example3

Note that the the javah command was removed from the JDK as of JDK 10, and the approved method (available since JDK 8) is to use javac to generate native headers at the same time as the Java source code is compiled.. As such the JavaH builder is of limited utility in later Java versions.