Thursday, 5 April 2018

Build jpostal for Windows

Want to use libpostal in Java applications on Windows? Here's how to build jpostal using MSYS2.

  1. Follow instructions to build libpostal 43795a3d903991d3864926393af10c3ec31a161c
  2. Git clone jpostal 60a8b6fd4346731d708511e20633c748acd5f601
  3. Follow jpostal steps in MSYS2 up to and including "Make sure pkg-config can find libpostal"
  4. Apply the patch.
diff --git a/build.sh b/build.sh
index 42e0714..55a17d3 100755
--- a/build.sh
+++ b/build.sh
@@ -1,4 +1,4 @@
#!/usr/bin/env bash
./bootstrap.sh
-./configure --libdir=$(pwd)/src/main/jniLibs
-make install
\ No newline at end of file
+./configure --libdir=$(pwd)/src/main/jniLibs --disable-static --enable-shared
+make install
diff --git a/configure.ac b/configure.ac
index ed71dde..419681e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -8,7 +8,7 @@ AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_HEADERS([config.h])
AM_INIT_AUTOMAKE([foreign subdir-objects])
AC_CONFIG_SRCDIR([src/main/c])
-LT_INIT([shared])
+LT_INIT([win32-dll])

# Checks for programs.
AC_PROG_CC_C99
diff --git a/src/main/c/Makefile.am b/src/main/c/Makefile.am
index eead86d..4b0a2c5 100644
--- a/src/main/c/Makefile.am
+++ b/src/main/c/Makefile.am
@@ -3,7 +3,9 @@ lib_LTLIBRARIES = libjpostal_parser.la libjpostal_expander.la
libjpostal_expander_la_SOURCES = jpostal_AddressExpander.c
libjpostal_expander_la_CFLAGS = $(LIBPOSTAL_CFLAGS)
libjpostal_expander_la_LIBADD = $(LIBPOSTAL_LIBS)
+libjpostal_expander_la_LDFLAGS = -no-undefined

libjpostal_parser_la_SOURCES = jpostal_AddressParser.c
libjpostal_parser_la_CFLAGS = $(LIBPOSTAL_CFLAGS)
libjpostal_parser_la_LIBADD = $(LIBPOSTAL_LIBS)
+libjpostal_parser_la_LDFLAGS = -no-undefined

  1. Add javac to MSYS2 path: export PATH=$PATH:/c/path/to/javac
  2. Finally, run ./build.sh
  3. The dlls will be in src/main/c/.libs, rename them to jpostal_expander.dll, jpostal_parser.dll.
  4. Here's the java example.
package my_jpostal;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;

import com.mapzen.jpostal.AddressExpander;
import com.mapzen.jpostal.AddressParser;
import com.mapzen.jpostal.ParsedComponent;

public class MyPostal {

    static {
        File tempDir = createTempDir();
        String[] dllFileNames = { "libpostal-1.dll", "jpostal_expander.dll", "jpostal_parser.dll" };
        copyDllToTempDir(tempDir, dllFileNames);
        updateJavaLibraryPath(tempDir.getPath());
    }

    private static void updateJavaLibraryPath(String tempDirPath) {
        String originalJavaLibraryPath = System.getProperty("java.library.path");
        System.setProperty("java.library.path", tempDirPath + ";" + originalJavaLibraryPath);
        // force reload of sys_path
        try {
            final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
            sysPathsField.setAccessible(true);
            sysPathsField.set(null, null);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            System.out.println("unexpected exception while updating java.library.path");
        }
    }

    private static File createTempDir() {
        String tempDir = System.getProperty("java.io.tmpdir");
        File myTempDir = new File(tempDir, "MyPostal" + System.nanoTime());
        if (!myTempDir.mkdir()) {
            throw new RuntimeException("Failed to create temporary directory to extract dll in jar file.");
        }
        myTempDir.deleteOnExit();
        return myTempDir;
    }

    private static void copyDllToTempDir(File tempDir, String[] dllFileNames) {
        for (String dllFileName : dllFileNames) {
            String pathOfDllInJar = removePathPrefix(
                    MyPostal.class.getClassLoader().getResource(dllFileName).getPath());
            File myTempDll = new File(tempDir, dllFileName);
            myTempDll.deleteOnExit();
            try {
                Files.copy(Paths.get(pathOfDllInJar), Paths.get(removePathPrefix(myTempDll.getPath())));
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            System.load(myTempDll.getAbsolutePath());
        }
    }

    private static String removePathPrefix(String path) {
        int colonPosition = path.lastIndexOf(':');
        int driveLetterPosition = colonPosition - 1;
        return path.substring(driveLetterPosition);
    }

    public static void main(String[] args) {
        // Singleton, libpostal setup is done in the constructor
        AddressExpander e = AddressExpander.getInstance();
        String[] expansions = e.expandAddress("Quatre vingt douze Ave des Champs-Élysées");
        for (String expansion : expansions) {
            System.out.println(expansion);
        }

        // Singleton, parser setup is done in the constructor
        AddressParser p = AddressParser.getInstance();
        ParsedComponent[] components = p.parseAddress(
                "The Book Club 100-106 Leonard St, Shoreditch, London, Greater London, EC2A 4RH, United Kingdom");
        for (ParsedComponent c : components) {
            System.out.printf("%s: %s\n", c.getLabel(), c.getValue());
        }
    }

}


Useful links

No comments:

Post a Comment