Some updates.

This commit is contained in:
Adam Murdoch
2012-10-10 16:28:54 +11:00
parent d70a620089
commit 827387dd78
2 changed files with 407 additions and 396 deletions

View File

@@ -1,208 +1,208 @@
apply plugin: 'groovy' apply plugin: 'groovy'
apply plugin: 'cpp' apply plugin: 'cpp'
allprojects { allprojects {
apply plugin: 'java' apply plugin: 'java'
apply plugin: 'idea' apply plugin: 'idea'
apply plugin: 'maven' apply plugin: 'maven'
repositories { repositories {
mavenCentral() mavenCentral()
maven { url "https://gradle.artifactoryonline.com/gradle/libs-releases-local" } maven { url "http://repo.gradle.org/gradle/libs-releases-local" }
} }
dependencies { dependencies {
testCompile 'org.spockframework:spock-core:0.6-groovy-1.8' testCompile 'org.spockframework:spock-core:0.6-groovy-1.8'
} }
group = 'net.rubygrapefruit' group = 'net.rubygrapefruit'
version = '0.2' version = '0.2'
sourceCompatibility = 1.5 sourceCompatibility = 1.5
targetCompatibility = 1.5 targetCompatibility = 1.5
configurations.compile.extendsFrom = [] configurations.compile.extendsFrom = []
tasks.withType(Upload) { tasks.withType(Upload) {
repositories { repositories {
mavenDeployer { mavenDeployer {
if (project.hasProperty('release')) { if (project.hasProperty('release')) {
repository(url: uri("https://gradle.artifactoryonline.com/gradle/libs-releases-local")) { repository(url: uri("https://gradle.artifactoryonline.com/gradle/libs-releases-local")) {
authentication(userName: artifactoryUserName, password: artifactoryPassword) authentication(userName: artifactoryUserName, password: artifactoryPassword)
} }
} else { } else {
repository(url: uri("$rootProject.buildDir/repo")) repository(url: uri("$rootProject.buildDir/repo"))
} }
} }
} }
} }
} }
dependencies { dependencies {
groovy 'org.codehaus.groovy:groovy:1.8.7' groovy 'org.codehaus.groovy:groovy:1.8.7'
} }
def nativeHeadersDir = file("$buildDir/nativeHeaders") def nativeHeadersDir = file("$buildDir/nativeHeaders")
cpp { cpp {
sourceSets { sourceSets {
main main
} }
} }
libraries { libraries {
if (org.gradle.internal.os.OperatingSystem.current().macOsX) { if (org.gradle.internal.os.OperatingSystem.current().macOsX) {
universal.spec { universal.spec {
baseName = 'native-platform-osx-universal' baseName = 'native-platform-osx-universal'
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("-lcurses", "-arch", "x86_64", "-arch", "i386", "-o", outputFile)
} }
} else if (org.gradle.internal.os.OperatingSystem.current().windows) { } else if (org.gradle.internal.os.OperatingSystem.current().windows) {
all { all {
spec { spec {
includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"]) includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"])
includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/win32"]) includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/win32"])
args("/DWIN32") args("/DWIN32")
} }
} }
def out = new ByteArrayOutputStream() def out = new ByteArrayOutputStream()
exec { exec {
commandLine "cl.exe", "/?" commandLine "cl.exe", "/?"
errorOutput = out errorOutput = out
standardOutput = new ByteArrayOutputStream() standardOutput = new ByteArrayOutputStream()
} }
def header = out.toString().readLines().head() def header = out.toString().readLines().head()
if (header.endsWith("for 80x86") || header.endsWith("for x86")) { if (header.endsWith("for 80x86") || header.endsWith("for x86")) {
i386.spec { i386.spec {
baseName = 'native-platform-windows-i386' baseName = 'native-platform-windows-i386'
} }
} else if (header.endsWith("for x64")) { } else if (header.endsWith("for x64")) {
amd64.spec { amd64.spec {
baseName = 'native-platform-windows-amd64' baseName = 'native-platform-windows-amd64'
} }
} else { } else {
throw new RuntimeException("Cannot determine compiler's target architecture") throw new RuntimeException("Cannot determine compiler's target architecture")
} }
} else if (org.gradle.internal.os.OperatingSystem.current().linux) { } else if (org.gradle.internal.os.OperatingSystem.current().linux) {
all { all {
spec { spec {
includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"]) includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"])
includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/linux"]) includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/linux"])
args("-lcurses") args("-lcurses")
} }
} }
if (System.getProperty('os.arch') == 'i386' || project.hasProperty('multiarch')) { if (System.getProperty('os.arch') == 'i386' || project.hasProperty('multiarch')) {
i386.spec { i386.spec {
baseName = 'native-platform-linux-i386' baseName = 'native-platform-linux-i386'
args("-m32") args("-m32")
} }
} }
if (System.getProperty('os.arch') == 'amd64' || project.hasProperty('multiarch')) { if (System.getProperty('os.arch') == 'amd64' || project.hasProperty('multiarch')) {
amd64.spec { amd64.spec {
baseName = 'native-platform-linux-amd64' baseName = 'native-platform-linux-amd64'
args("-m64") args("-m64")
} }
} }
} else { } else {
baseName = "native-platform-solaris" baseName = "native-platform-solaris"
main.spec { main.spec {
includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"]) includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include"])
includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/solaris"]) includes(["${org.gradle.internal.jvm.Jvm.current().javaHome}/include/solaris"])
args("-DSOLARIS", "-lcurses") args("-DSOLARIS", "-lcurses")
} }
} }
all { all {
spec { spec {
includes([nativeHeadersDir]) includes([nativeHeadersDir])
} }
sourceSets << cpp.sourceSets.main sourceSets << cpp.sourceSets.main
} }
} }
task nativeHeaders { task nativeHeaders {
def outputFile = file("$nativeHeadersDir/native.h") def outputFile = file("$nativeHeadersDir/native.h")
inputs.files sourceSets.main.output inputs.files sourceSets.main.output
outputs.file outputFile outputs.file outputFile
doLast { doLast {
outputFile.parentFile.mkdirs() outputFile.parentFile.mkdirs()
exec { exec {
executable org.gradle.internal.jvm.Jvm.current().getExecutable('javah') executable org.gradle.internal.jvm.Jvm.current().getExecutable('javah')
args '-o', outputFile args '-o', outputFile
args '-classpath', sourceSets.main.output.classesDir args '-classpath', sourceSets.main.output.classesDir
args 'net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions' args 'net.rubygrapefruit.platform.internal.jni.NativeLibraryFunctions'
args 'net.rubygrapefruit.platform.internal.jni.PosixFileFunctions' args 'net.rubygrapefruit.platform.internal.jni.PosixFileFunctions'
args 'net.rubygrapefruit.platform.internal.jni.PosixFileSystemFunctions' args 'net.rubygrapefruit.platform.internal.jni.PosixFileSystemFunctions'
args 'net.rubygrapefruit.platform.internal.jni.PosixProcessFunctions' args 'net.rubygrapefruit.platform.internal.jni.PosixProcessFunctions'
args 'net.rubygrapefruit.platform.internal.jni.PosixTerminalFunctions' args 'net.rubygrapefruit.platform.internal.jni.PosixTerminalFunctions'
args 'net.rubygrapefruit.platform.internal.jni.TerminfoFunctions' args 'net.rubygrapefruit.platform.internal.jni.TerminfoFunctions'
args 'net.rubygrapefruit.platform.internal.jni.WindowsConsoleFunctions' args 'net.rubygrapefruit.platform.internal.jni.WindowsConsoleFunctions'
} }
} }
} }
configurations { configurations {
jni jni
} }
def deployer = uploadJni.repositories.mavenDeployer def deployer = uploadJni.repositories.mavenDeployer
libraries.all { lib -> libraries.all { lib ->
def nativeJar = task("nativeJar${lib.name.capitalize()}", type: Jar) { def nativeJar = task("nativeJar${lib.name.capitalize()}", type: Jar) {
from lib.spec.task from lib.spec.task
baseName = lib.spec.baseName baseName = lib.spec.baseName
} }
lib.spec.task.dependsOn nativeHeaders lib.spec.task.dependsOn nativeHeaders
test.dependsOn lib.spec.task test.dependsOn lib.spec.task
artifacts { artifacts {
jni nativeJar jni nativeJar
runtime nativeJar runtime nativeJar
} }
def jniPom = deployer.addFilter(lib.name) { artifact, file -> def jniPom = deployer.addFilter(lib.name) { artifact, file ->
return file == nativeJar.archivePath return file == nativeJar.archivePath
} }
jniPom.groupId = project.group jniPom.groupId = project.group
jniPom.artifactId = lib.spec.baseName jniPom.artifactId = lib.spec.baseName
jniPom.version = project.version jniPom.version = project.version
jniPom.scopeMappings.mappings.clear() jniPom.scopeMappings.mappings.clear()
} }
javadoc { javadoc {
exclude '**/internal/**' exclude '**/internal/**'
} }
task sourceZip(type: Zip) { task sourceZip(type: Zip) {
from sourceSets.main.allSource from sourceSets.main.allSource
classifier = 'sources' classifier = 'sources'
} }
task javadocZip(type: Zip) { task javadocZip(type: Zip) {
from javadoc from javadoc
classifier = 'javadoc' classifier = 'javadoc'
} }
artifacts { artifacts {
archives sourceZip archives sourceZip
archives javadocZip archives javadocZip
} }
def mainPom = uploadArchives.repositories.mavenDeployer.pom def mainPom = uploadArchives.repositories.mavenDeployer.pom
mainPom.groupId = project.group mainPom.groupId = project.group
mainPom.artifactId = jar.baseName mainPom.artifactId = jar.baseName
mainPom.version = project.version mainPom.version = project.version
mainPom.scopeMappings.mappings.clear() mainPom.scopeMappings.mappings.clear()
mainPom.withXml { provider -> mainPom.withXml { provider ->
def node = provider.asNode() def node = provider.asNode()
def deps = node.appendNode('dependencies') def deps = node.appendNode('dependencies')
['osx-universal', 'linux-amd64', 'linux-i386', 'windows-amd64', 'windows-i386'].each { platform -> ['osx-universal', 'linux-amd64', 'linux-i386', 'windows-amd64', 'windows-i386'].each { platform ->
def dep = deps.appendNode('dependency') def dep = deps.appendNode('dependency')
dep.appendNode('groupId', project.group) dep.appendNode('groupId', project.group)
dep.appendNode('artifactId', "native-platform-${platform}") dep.appendNode('artifactId', "native-platform-${platform}")
dep.appendNode('version', project.version) dep.appendNode('version', project.version)
} }
} }
task wrapper(type: Wrapper) { task wrapper(type: Wrapper) {
gradleVersion = "1.3-20120907220018+0000" gradleVersion = "1.3-20120907220018+0000"
} }

387
readme.md
View File

@@ -1,188 +1,199 @@
# Native-platform: Java bindings for various native APIs # Native-platform: Java bindings for various native APIs
A collection of cross-platform Java APIs for various native APIs. Supports OS X, Linux, Solaris and Windows. A collection of cross-platform Java APIs for various native APIs. Supports OS X, Linux, Solaris and Windows.
These APIs support Java 5 and later. Some of these APIs overlap with APIs available in later Java versions. These APIs support Java 5 and later. Some of these APIs overlap with APIs available in later Java versions.
## Available bindings ## Available bindings
### Generic ### System information
* Get PID of current process. * Get kernel name and version.
* Get kernel name and version. * Get machine architecture.
* Get machine architecture.
### Processes
### Terminal and console
* Get the PID of the current process.
These bindings work for both the UNIX terminal and the Windows console:
### Terminal and console
* Determine if stdout/stderr are attached to a terminal.
* Query the terminal size. These bindings work for both the UNIX terminal and the Windows console:
* Switch between bold and normal mode on the terminal.
* Change foreground color on the terminal. * Determine if stdout/stderr are attached to a terminal.
* Move terminal cursor up, down, left, right, start of line. * Query the terminal size.
* Clear to end of line. * Switch between bold and normal mode on the terminal.
* Change foreground color on the terminal.
### File systems * Move terminal cursor up, down, left, right, start of line.
* Clear to end of line.
* Get and set UNIX file mode.
* Create and read symbolic links. ### File systems
* List the available file systems on the machine
* Query file system mount point. * Get and set UNIX file mode.
* Query file system type. * Create and read symbolic links.
* Query file system device name. * List the available file systems on the machine
* Query whether a file system is local or remote. * Query file system mount point.
* Query file system type.
## Supported platforms * Query file system device name.
* Query whether a file system is local or remote.
Currently ported to OS X, Linux and Windows, with some support for Solaris and FreeBSD. Tested on:
## Supported platforms
* OS X 10.7.4, 10.8 (x86_64), 10.6.7 (i386)
* Ubunutu 12.04 (amd64), 8.04.4 (i386, amd64) Currently ported to OS X, Linux and Windows. Support for Solaris and FreeBSD is a work in progress. Tested on:
* Solaris 11 (x86)
* Windows 7 (x64), XP (x86) * OS X 10.7.4, 10.8 (x86_64), 10.6.7 (i386)
* Ubunutu 12.04 (amd64), 8.04.4 (i386, amd64)
## Using * Solaris 11 (x86)
* Windows 7 (x64), XP (x86)
Include `native-platform.jar` and `native-platform-${os}-${arch}.jar` in your classpath. From Gradle, you can do
this: ## Using
repositories { Include `native-platform.jar` and `native-platform-${os}-${arch}.jar` in your classpath. From Gradle, you can do
maven { url "https://gradle.artifactoryonline.com/gradle/libs-releases-local" } this:
}
repositories {
dependencies { maven { url "http://repo.gradle.org/gradle/libs-releases-local" }
compile "net.rubygrapefruit:native-platform:0.1" }
}
dependencies {
Some sample code to use the terminal: compile "net.rubygrapefruit:native-platform:0.1"
}
import net.rubygrapefruit.platform.Native;
import net.rubygrapefruit.platform.Terminals; You can also download [here](http://repo.gradle.org/gradle/libs-releases-local/net/rubygrapefruit/native-platform/0.1)
import net.rubygrapefruit.platform.Terminal;
import static net.rubygrapefruit.platform.Terminals.Output.*; Some sample code to use the terminal:
Terminals terminals = Native.get(Terminals.class); import net.rubygrapefruit.platform.Native;
import net.rubygrapefruit.platform.Terminals;
// check if terminal import net.rubygrapefruit.platform.Terminal;
terminals.isTerminal(Stdout); import static net.rubygrapefruit.platform.Terminals.Output.*;
// use terminal Terminals terminals = Native.get(Terminals.class);
Terminal stdout = terminals.getTerminal(Stdout);
stdout.bold(); // check if terminal
System.out.println("bold text"); terminals.isTerminal(Stdout);
## Changes // use terminal
Terminal stdout = terminals.getTerminal(Stdout);
### 0.2 stdout.bold();
System.out.println("bold text");
Fixes to make native library extraction multi-process safe.
## Changes
### 0.1
### 0.2
Initial release
Fixes to make native library extraction multi-process safe.
# Development
### 0.1
## Building
Initial release.
You will need to use the Gradle wrapper. Just run `gradlew` in the root directory.
# Development
### Ubuntu
## Building
The g++ compiler is required to build the native library. You will need the `g++` package for this. Usually this is already installed.
You will need to use the Gradle wrapper. Just run `gradlew` in the root directory.
You need to install the `libncurses5-dev` package to pick up the ncurses header files. Also worth installing the `ncurses-doc` package too.
### Ubuntu
#### 64-bit machines with multi-arch support
The g++ compiler is required to build the native library. You will need the `g++` package for this. Usually this is already installed.
Where multi-arch support is available (e.g. recent Ubuntu releases), you can build the i386 and amd64 versions of the library on the
same machine. You need to install the `libncurses5-dev` package to pick up the ncurses header files. Also worth installing the `ncurses-doc` package too.
You need to install the `gcc-multilib` and `g++-multilib` packages to pick up i386 support. #### 64-bit machines with multi-arch support
You need to install the `lib32ncurses5-dev` package to pick up the ncurses i386 version. Where multi-arch support is available (e.g. recent Ubuntu releases), you can build the i386 and amd64 versions of the library on the
same machine.
To build, include `-Pmultiarch` on the command-line.
You need to install the `gcc-multilib` and `g++-multilib` packages to pick up i386 support.
### Windows
You need to install the `lib32ncurses5-dev` package to pick up the ncurses i386 version.
You need to install Visual studio, and build from a Visual studio command prompt.
To build, include `-Pmultiarch` on the command-line.
### OS X
### Windows
The g++ compiler is required to build the native library. You will need to install the XCode tools for this.
You need to install Visual studio, and build from a Visual studio command prompt.
### Solaris
### OS X
For Solaris 11, you need to install the `development/gcc-45` and `system/header` packages.
The g++ compiler is required to build the native library. You will need to install the XCode tools for this.
## Running
### Solaris
Run `gradle installApp` to install the test application into `test-app/build/install/native-platform-test`. Or
`gradle distZip` to create an application distribtion in `test-app/build/distributions/native-platform-test-$version.zip`. For Solaris 11, you need to install the `development/gcc-45` and `system/header` packages.
You can run `$INSTALL_DIR/bin/native-platform-test` to run the test application. ## Running
# Releasing Run `gradle installApp` to install the test application into `test-app/build/install/native-platform-test`. Or
`gradle distZip` to create an application distribtion in `test-app/build/distributions/native-platform-test-$version.zip`.
1. Create a tag and push.
2. Build each variant: You can run `$INSTALL_DIR/bin/native-platform-test` to run the test application.
1. Checkout tag.
2. `./gradlew clean test :uploadJni -Prelease -PartifactoryUserName=<> -PartifactoryPassword=<>` # Releasing
* OS X universal
* Linux i386, using Ubunutu 8.04 1. Create a tag and push.
* Linux amd64, using Ubunutu 8.04 2. Build each variant:
* Windows x86, using VC++ 2010 1. Checkout tag.
* Windows x64 2. `./gradlew clean test :uploadJni -Prelease -PartifactoryUserName=<> -PartifactoryPassword=<>`
3. Build Java library and test app: * OS X universal
1. Checkout tag. * Linux i386, using Ubunutu 8.04
2. `./gradlew clean test :uploadArchives testApp:uploadArchives -Prelease` * Linux amd64, using Ubunutu 8.04
4. Checkout master * Windows x86, using VC++ 2010
5. Increment version number. * Windows x64
3. Build Java library and test app:
## Testing 1. Checkout tag.
2. `./gradlew clean test :uploadArchives testApp:uploadArchives -Prelease`
* Test on IBM JVM. 4. Checkout master
* Test on Java 5, 6, 7. 5. Increment version number.
* Test on Windows 7, Windows XP
## Testing
## TODO
* Test on IBM JVM.
### Fixes * Test on Java 5, 6, 7.
* Test on Windows 7, Windows XP
* Windows: fix detection of shared drive under VMWare fusion and Windows XP
* Linux: detect remote filesystems. ## TODO
* Solaris: fix unicode file name handling.
* Solaris: fail for unsupported architecture. ### Fixes
* Solaris: build 32 bit and 64 bit libraries.
* Freebsd: finish port. * Windows: fix detection of shared drive under VMWare fusion and Windows XP
* Freebsd: fail for unsupported architecture. * Linux: detect remote filesystems.
* Freebsd: build 32 bit and 64 bit libraries. * Solaris: fix unicode file name handling.
* Solaris: fail for unsupported architecture.
### Improvements * Solaris: build 32 bit and 64 bit libraries.
* Freebsd: finish port.
* Use wchar_to_java() for windows system and file system info. * Freebsd: fail for unsupported architecture.
* Test network file systems on Mac, Linux, Windows * Freebsd: build 32 bit and 64 bit libraries.
* Test mount points on Windows
* Cache class, method and field lookups (in particular for String conversions). ### Improvements
* Determine C charset once at startup
* Change readLink() implementation so that it does not need to NULL terminate the encoded content * Use wchar_to_java() for windows system and file system info.
* Don't use NewStringUTF() anywhere * Test network file systems on Mac, Linux, Windows
* Don't use NewString() anywhere * Test mount points on Windows
* Use iconv() to convert from C char string to UTF-16 when converting from C char string to Java String. * Cache class, method and field lookups (in particular for String conversions).
* Support for cygwin terminal * Determine C charset once at startup
* Use TERM=xtermc instead of TERM=xterm on Solaris. * Change readLink() implementation so that it does not need to NULL terminate the encoded content
* Add diagnostics for terminal. * Don't use NewStringUTF() anywhere
* Split out separate native library for terminal handling. * Don't use NewString() anywhere
* Version each native interface separately. * Use iconv() to convert from C char string to UTF-16 when converting from C char string to Java String.
* String names for errno values. * Support for cygwin terminal
* Split into multiple projects. * Use TERM=xtermc instead of TERM=xterm on Solaris.
* Convert to c. * Add diagnostics for terminal.
* Use fully decomposed form for unicode file names on hfs+ filesystems. * Split out separate native library for terminal handling.
* Extend FileSystem to deal with removable media. * Version each native interface separately.
* String names for errno values.
### Ideas * Split into multiple projects.
* Convert to c.
* Expose platform-specific HTTP proxy configuration. Query registry on windows to determine IE settings. * Use fully decomposed form for unicode file names on hfs+ filesystems.
* Extend FileSystem to deal with removable media.
### Ideas
* Expose platform-specific HTTP proxy configuration. Query registry on windows to determine IE settings.
* Expose native named semaphores, mutexes and condition variables (CreateMutex, CreateSemaphore, CreateEvent, semget, sem_open, etc).
* Expose infromation about network interfaces.
* Fire events when filesystems or network interfaces change in some way.
* Fire events when terminal size changes.
* Fire events when files change.
* Expose system keystores and authentication services.