Search code examples
javajava-native-interfacenewlinejavah

Can javah be coaxed to generate .h files with consistent line endings?


I have a maven project that generates .h JNI files by executing javah as part of the normal build process of a java library. These .h files are then checked in to source control (such as git) and used to build the accompanying native library.

One minor annoyance is the files generated by javah differ by line endings depending on the platform where it is run. So if a Mac OSX developer runs the build and checks in (UNIX-style line endings), then a Windows developer will subsequently see that their build has changed all the .h files (to Windows-style line endings). But they've not actually changed--javah is just behaving in a platform dependent way.

How can I coax javah to always use, for example, UNIX-style line endings when generating .h files? There appears to be no appropriate command-line switch:

> javah.exe
Usage:
  javah [options] <classes>
where [options] include:
  -o <file>                Output file (only one of -d or -o may be used)
  -d <dir>                 Output directory
  -v  -verbose             Enable verbose output
  -h  --help  -?           Print this message
  -version                 Print version information
  -jni                     Generate JNI-style header file (default)
  -force                   Always write output files
  -classpath <path>        Path from which to load classes
  -bootclasspath <path>    Path from which to load bootstrap classes
<classes> are specified with their fully qualified names
(for example, java.lang.Object).

Perhaps it would be possible to manually launch the same class as the javah executable launches, except to explicitly set the "line.separator" property before doing so. However, I could not find what class that would be, or where.


Solution

  • I solved this problem by writing a custom launcher that explicitly sets the line.separator property, and then invoking that launcher during the build:

    public class JavahLauncher {
    
        public static void main(String[] args) {
            String original = System.getProperty("line.separator");
            System.setProperty("line.separator", "\n");
            try {
                com.sun.tools.javah.Main.run(args, new PrintWriter(System.out));
            }
            finally {
                System.setProperty("line.separator", original);
            }
        }
    }
    

    The try-finally allows this launcher to be invoked inside another JVM, such as Maven's JVM instance while performing a build, without permanently changing the line.separator value. One interesting note is com.sun.tools.javah.Main.main is unusable because it calls System.exit, which, if called as part of the Maven build causes Maven to exit!

    Compiling this launcher requires a dependency on tools.jar, something like:

    <dependency>
        <groupId>com.sun</groupId>
        <artifactId>tools</artifactId>
        <version>1.7.0</version>
        <scope>system</scope>
        <systemPath>${java.home}/../lib/tools.jar</systemPath>
    </dependency>