Split out terminfo jni functions into a separate native library, so that curses/ncurses is not required to load the main native library.

This commit is contained in:
Adam Murdoch
2012-11-12 17:20:04 +11:00
parent c4a55e54e5
commit 802fb78f29
11 changed files with 188 additions and 99 deletions

View File

@@ -46,16 +46,40 @@ def nativeHeadersDir = file("$buildDir/nativeHeaders")
cpp { cpp {
sourceSets { sourceSets {
main main {
source.exclude 'curses.cpp'
}
curses {
source.srcDirs = ['src/main/cpp']
source.include 'curses.cpp'
source.include 'generic.cpp'
source.include 'generic_posix.cpp'
}
} }
} }
libraries { libraries {
if (org.gradle.internal.os.OperatingSystem.current().macOsX) { if (org.gradle.internal.os.OperatingSystem.current().macOsX) {
universal.spec { all {
baseName = 'native-platform-osx-universal' spec {
includes(['/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/']) includes(['/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/'])
args("-lcurses", "-arch", "x86_64", "-arch", "i386", "-o", outputFile) args("-arch", "x86_64", "-arch", "i386")
}
}
universal {
sourceSets << cpp.sourceSets.main
spec {
baseName = 'native-platform-osx-universal'
args("-o", outputFile)
}
}
cursesUniversal {
sourceSets << cpp.sourceSets.curses
spec {
baseName = 'native-platform-curses-osx-universal'
args("-lcurses")
args("-o", outputFile)
}
} }
} else if (org.gradle.internal.os.OperatingSystem.current().windows) { } else if (org.gradle.internal.os.OperatingSystem.current().windows) {
all { all {
@@ -115,9 +139,8 @@ libraries {
} }
all { all {
spec { spec {
includes([nativeHeadersDir]) includes([nativeHeadersDir, 'src/main/headers'])
} }
sourceSets << cpp.sourceSets.main
} }
} }

View File

@@ -162,6 +162,7 @@ You can run `$INSTALL_DIR/bin/native-platform-test` to run the test application.
### Fixes ### Fixes
* Posix: allow terminal to be detected when ncurses cannot be loaded
* Windows: fix detection of shared drive under VMWare fusion and Windows XP * Windows: fix detection of shared drive under VMWare fusion and Windows XP
* Linux: detect remote filesystems. * Linux: detect remote filesystems.
* Solaris: fix unicode file name handling. * Solaris: fix unicode file name handling.

View File

@@ -66,6 +66,11 @@ void write_param_capability(JNIEnv *env, const char* capability, int count, jobj
} }
} }
JNIEXPORT jint JNICALL
Java_net_rubygrapefruit_platform_internal_jni_TerminfoFunctions_getVersion(JNIEnv *env, jclass target) {
return NATIVE_VERSION;
}
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_TerminfoFunctions_initTerminal(JNIEnv *env, jclass target, jint output, jobject capabilities, jobject result) { Java_net_rubygrapefruit_platform_internal_jni_TerminfoFunctions_initTerminal(JNIEnv *env, jclass target, jint output, jobject capabilities, jobject result) {
if (!isatty(output+1)) { if (!isatty(output+1)) {

View File

@@ -17,5 +17,5 @@ void mark_failed_with_code(JNIEnv *env, const char* message, int error_code, job
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getVersion(JNIEnv *env, jclass target) { Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getVersion(JNIEnv *env, jclass target) {
return 10; return NATIVE_VERSION;
} }

View File

@@ -0,0 +1,62 @@
/*
* POSIX platform functions.
*/
#ifndef WIN32
#include "native.h"
#include "generic.h"
#include <stdlib.h>
#include <errno.h>
#include <xlocale.h>
#include <langinfo.h>
#include <string.h>
void mark_failed_with_errno(JNIEnv *env, const char* message, jobject result) {
mark_failed_with_code(env, message, errno, result);
}
char* java_to_char(JNIEnv *env, jstring string, jobject result) {
// TODO - share this code with nnn_getSystemInfo() below
// Empty string means load locale from environment.
locale_t locale = newlocale(LC_CTYPE_MASK, "", NULL);
if (locale == NULL) {
mark_failed_with_message(env, "could not create locale", result);
return NULL;
}
jstring encoding = env->NewStringUTF(nl_langinfo_l(CODESET, locale));
freelocale(locale);
jclass strClass = env->FindClass("java/lang/String");
jmethodID method = env->GetMethodID(strClass, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray byteArray = (jbyteArray)env->CallObjectMethod(string, method, encoding);
size_t len = env->GetArrayLength(byteArray);
char* chars = (char*)malloc(len + 1);
env->GetByteArrayRegion(byteArray, 0, len, (jbyte*)chars);
chars[len] = 0;
return chars;
}
jstring char_to_java(JNIEnv* env, const char* chars, jobject result) {
// TODO - share this code with nnn_getSystemInfo() below
// Empty string means load locale from environment.
locale_t locale = newlocale(LC_CTYPE_MASK, "", NULL);
if (locale == NULL) {
mark_failed_with_message(env, "could not create locale", result);
return NULL;
}
jstring encoding = env->NewStringUTF(nl_langinfo_l(CODESET, locale));
freelocale(locale);
size_t len = strlen(chars);
jbyteArray byteArray = env->NewByteArray(len);
jbyte* bytes = env->GetByteArrayElements(byteArray, NULL);
memcpy(bytes, chars, len);
env->ReleaseByteArrayElements(byteArray, bytes, JNI_COMMIT);
jclass strClass = env->FindClass("java/lang/String");
jmethodID method = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
return (jstring)env->NewObject(strClass, method, byteArray, encoding);
}
#endif

View File

@@ -11,62 +11,7 @@
#include <errno.h> #include <errno.h>
#include <unistd.h> #include <unistd.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <langinfo.h>
#include <sys/utsname.h> #include <sys/utsname.h>
#include <xlocale.h>
#include <locale.h>
#include <string.h>
/*
* Marks the given result as failed, using the current value of errno
*/
void mark_failed_with_errno(JNIEnv *env, const char* message, jobject result) {
mark_failed_with_code(env, message, errno, result);
}
char* java_to_char(JNIEnv *env, jstring string, jobject result) {
// TODO - share this code with nnn_getSystemInfo() below
// Empty string means load locale from environment.
locale_t locale = newlocale(LC_CTYPE_MASK, "", NULL);
if (locale == NULL) {
mark_failed_with_message(env, "could not create locale", result);
return NULL;
}
jstring encoding = env->NewStringUTF(nl_langinfo_l(CODESET, locale));
freelocale(locale);
jclass strClass = env->FindClass("java/lang/String");
jmethodID method = env->GetMethodID(strClass, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray byteArray = (jbyteArray)env->CallObjectMethod(string, method, encoding);
size_t len = env->GetArrayLength(byteArray);
char* chars = (char*)malloc(len + 1);
env->GetByteArrayRegion(byteArray, 0, len, (jbyte*)chars);
chars[len] = 0;
return chars;
}
jstring char_to_java(JNIEnv* env, const char* chars, jobject result) {
// TODO - share this code with nnn_getSystemInfo() below
// Empty string means load locale from environment.
locale_t locale = newlocale(LC_CTYPE_MASK, "", NULL);
if (locale == NULL) {
mark_failed_with_message(env, "could not create locale", result);
return NULL;
}
jstring encoding = env->NewStringUTF(nl_langinfo_l(CODESET, locale));
freelocale(locale);
size_t len = strlen(chars);
jbyteArray byteArray = env->NewByteArray(len);
jbyte* bytes = env->GetByteArrayElements(byteArray, NULL);
memcpy(bytes, chars, len);
env->ReleaseByteArrayElements(byteArray, bytes, JNI_COMMIT);
jclass strClass = env->FindClass("java/lang/String");
jmethodID method = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
return (jstring)env->NewObject(strClass, method, byteArray, encoding);
}
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getSystemInfo(JNIEnv *env, jclass target, jobject info, jobject result) { Java_net_rubygrapefruit_platform_internal_jni_NativeLibraryFunctions_getSystemInfo(JNIEnv *env, jclass target, jobject info, jobject result) {

View File

@@ -7,6 +7,8 @@
extern "C" { extern "C" {
#endif #endif
#define NATIVE_VERSION 11
/* /*
* Marks the given result as failed, using the given error message * Marks the given result as failed, using the given error message
*/ */

View File

@@ -1,9 +1,9 @@
package net.rubygrapefruit.platform.internal; package net.rubygrapefruit.platform.internal;
import net.rubygrapefruit.platform.NativeException;
import net.rubygrapefruit.platform.NativeIntegrationUnavailableException; import net.rubygrapefruit.platform.NativeIntegrationUnavailableException;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@@ -15,16 +15,22 @@ public class NativeLibraryLoader {
this.locator = locator; this.locator = locator;
} }
public void load(String name) throws IOException { public void load(String name) {
if (loaded.contains(name)) { if (loaded.contains(name)) {
return; return;
} }
File libFile = locator.find(name); try {
if (libFile == null) { File libFile = locator.find(name);
throw new NativeIntegrationUnavailableException(String.format( if (libFile == null) {
"Native library is not available for this operating system and architecture.")); throw new NativeIntegrationUnavailableException(String.format(
"Native library is not available for this operating system and architecture."));
}
System.load(libFile.getCanonicalPath());
} catch (NativeException e) {
throw e;
} catch (Throwable t) {
throw new NativeException(String.format("Failed to load native library '%s'.", name), t);
} }
System.load(libFile.getCanonicalPath());
loaded.add(name); loaded.add(name);
} }
} }

View File

@@ -2,6 +2,8 @@ package net.rubygrapefruit.platform.internal;
import net.rubygrapefruit.platform.*; import net.rubygrapefruit.platform.*;
import net.rubygrapefruit.platform.Process; import net.rubygrapefruit.platform.Process;
import net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions;
import net.rubygrapefruit.platform.internal.jni.TerminfoFunctions;
public abstract class Platform { public abstract class Platform {
private static Platform platform; private static Platform platform;
@@ -10,12 +12,25 @@ public abstract class Platform {
synchronized (Platform.class) { synchronized (Platform.class) {
if (platform == null) { if (platform == null) {
String osName = getOperatingSystem().toLowerCase(); String osName = getOperatingSystem().toLowerCase();
String arch = getArchitecture();
if (osName.contains("windows")) { if (osName.contains("windows")) {
platform = new Windows(); if (arch.equals("x86")) {
platform = new Window32Bit();
}
else if (arch.equals("amd64")) {
platform = new Window64Bit();
}
} else if (osName.contains("linux")) { } else if (osName.contains("linux")) {
platform = new Linux(); if (arch.equals("amd64")) {
platform = new Linux64Bit();
}
else if (arch.equals("i386") || arch.equals("x86")) {
platform = new Linux32Bit();
}
} else if (osName.contains("os x")) { } else if (osName.contains("os x")) {
platform = new OsX(); if (arch.equals("i386") || arch.equals("x86_64") || arch.equals("amd64")) {
platform = new OsX();
}
} else if (osName.contains("sunos")) { } else if (osName.contains("sunos")) {
platform = new Solaris(); platform = new Solaris();
} else { } else {
@@ -53,23 +68,12 @@ public abstract class Platform {
return System.getProperty("os.arch"); return System.getProperty("os.arch");
} }
private static class Windows extends Platform { private abstract static class Windows extends Platform {
@Override @Override
public boolean isWindows() { public boolean isWindows() {
return true; return true;
} }
@Override
public String getLibraryName() {
if (getArchitecture().equals("x86")) {
return "native-platform-windows-i386.dll";
}
if (getArchitecture().equals("amd64")) {
return "native-platform-windows-amd64.dll";
}
return super.getLibraryName();
}
@Override @Override
public <T extends NativeIntegration> T get(Class<T> type, NativeLibraryLoader nativeLibraryLoader) { public <T extends NativeIntegration> T get(Class<T> type, NativeLibraryLoader nativeLibraryLoader) {
if (type.equals(Process.class)) { if (type.equals(Process.class)) {
@@ -88,7 +92,23 @@ public abstract class Platform {
} }
} }
private static class Window32Bit extends Windows {
@Override
public String getLibraryName() {
return "native-platform-windows-i386.dll";
}
}
private static class Window64Bit extends Windows {
@Override
public String getLibraryName() {
return "native-platform-windows-amd64.dll";
}
}
private static abstract class Posix extends Platform { private static abstract class Posix extends Platform {
abstract String getCursesLibraryName();
@Override @Override
public <T extends NativeIntegration> T get(Class<T> type, NativeLibraryLoader nativeLibraryLoader) { public <T extends NativeIntegration> T get(Class<T> type, NativeLibraryLoader nativeLibraryLoader) {
if (type.equals(PosixFile.class)) { if (type.equals(PosixFile.class)) {
@@ -98,6 +118,11 @@ public abstract class Platform {
return type.cast(new DefaultProcess()); return type.cast(new DefaultProcess());
} }
if (type.equals(Terminals.class)) { if (type.equals(Terminals.class)) {
nativeLibraryLoader.load(getCursesLibraryName());
int nativeVersion = TerminfoFunctions.getVersion();
if (nativeVersion != NativeLibraryFunctions.VERSION) {
throw new NativeException(String.format("Unexpected native library version loaded. Expected %s, was %s.", nativeVersion, NativeLibraryFunctions.VERSION));
}
return type.cast(new TerminfoTerminals()); return type.cast(new TerminfoTerminals());
} }
if (type.equals(SystemInfo.class)) { if (type.equals(SystemInfo.class)) {
@@ -110,7 +135,7 @@ public abstract class Platform {
private abstract static class Unix extends Posix { private abstract static class Unix extends Posix {
} }
private static class Linux extends Unix { private abstract static class Linux extends Unix {
@Override @Override
public <T extends NativeIntegration> T get(Class<T> type, NativeLibraryLoader nativeLibraryLoader) { public <T extends NativeIntegration> T get(Class<T> type, NativeLibraryLoader nativeLibraryLoader) {
if (type.equals(FileSystems.class)) { if (type.equals(FileSystems.class)) {
@@ -118,25 +143,42 @@ public abstract class Platform {
} }
return super.get(type, nativeLibraryLoader); return super.get(type, nativeLibraryLoader);
} }
}
private static class Linux32Bit extends Linux {
@Override @Override
public String getLibraryName() { public String getLibraryName() {
if (getArchitecture().equals("amd64")) { return "libnative-platform-linux-i386.so";
return "libnative-platform-linux-amd64.so"; }
}
if (getArchitecture().equals("i386") || getArchitecture().equals("x86")) { @Override
return "libnative-platform-linux-i386.so"; String getCursesLibraryName() {
} return "libnative-platform-curses-linux-i386.so";
return super.getLibraryName();
} }
} }
private static class Linux64Bit extends Linux {
@Override
public String getLibraryName() {
return "libnative-platform-linux-amd64.so";
}
@Override
String getCursesLibraryName() {
return "libnative-platform-curses-linux-amd64.so";
}
}
private static class Solaris extends Unix { private static class Solaris extends Unix {
@Override @Override
public String getLibraryName() { public String getLibraryName() {
return "libnative-platform-solaris.so"; return "libnative-platform-solaris.so";
} }
@Override
String getCursesLibraryName() {
return "libnative-platform-curses-solaris.so";
}
} }
private static class OsX extends Posix { private static class OsX extends Posix {
@@ -150,11 +192,12 @@ public abstract class Platform {
@Override @Override
public String getLibraryName() { public String getLibraryName() {
String arch = getArchitecture(); return "libnative-platform-osx-universal.dylib";
if (arch.equals("i386") || arch.equals("x86_64") || arch.equals("amd64")) { }
return "libnative-platform-osx-universal.dylib";
} @Override
return super.getLibraryName(); String getCursesLibraryName() {
return "libnative-platform-curses-osx-universal.dylib";
} }
} }

View File

@@ -4,7 +4,7 @@ import net.rubygrapefruit.platform.internal.FunctionResult;
import net.rubygrapefruit.platform.internal.MutableSystemInfo; import net.rubygrapefruit.platform.internal.MutableSystemInfo;
public class NativeLibraryFunctions { public class NativeLibraryFunctions {
public static final int VERSION = 10; public static final int VERSION = 11;
public static native int getVersion(); public static native int getVersion();

View File

@@ -4,6 +4,8 @@ import net.rubygrapefruit.platform.internal.FunctionResult;
import net.rubygrapefruit.platform.internal.TerminalCapabilities; import net.rubygrapefruit.platform.internal.TerminalCapabilities;
public class TerminfoFunctions { public class TerminfoFunctions {
public static native int getVersion();
/** /**
* Sets up terminal info and switches output to normal mode. * Sets up terminal info and switches output to normal mode.
*/ */