initial version

This commit is contained in:
Adam Murdoch
2012-07-29 16:35:28 +10:00
commit 1d56f93e64
10 changed files with 221 additions and 0 deletions

43
build.gradle Normal file
View File

@@ -0,0 +1,43 @@
apply plugin: 'java'
apply plugin: 'groovy'
apply plugin: 'cpp-lib'
apply plugin: 'idea'
repositories {
mavenCentral()
}
dependencies {
groovy 'org.codehaus.groovy:groovy:1.8.7'
testCompile 'org.spockframework:spock-core:0.6-groovy-1.8'
}
def nativeHeadersDir = file("$buildDir/nativeHeaders")
libraries {
main {
spec {
includes([nativeHeadersDir])
includes(['/System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/'])
}
}
}
task nativeHeaders {
def outputFile = file("$nativeHeadersDir/native.h")
inputs.files sourceSets.main.output
outputs.file outputFile
doLast {
outputFile.parentFile.mkdirs()
exec {
executable org.gradle.internal.jvm.Jvm.current().getExecutable('javah')
args '-o', outputFile
args '-classpath', sourceSets.main.output.classesDir
args 'net.rubygrapefruit.platform.internal.PosixFileFunctions'
}
}
}
compileMain.dependsOn nativeHeaders
test.dependsOn compileMain

View File

@@ -0,0 +1,33 @@
#include "native.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
void markFailed(JNIEnv *env, jobject result) {
jclass destClass = env->GetObjectClass(result);
jfieldID errnoField = env->GetFieldID(destClass, "errno", "I");
env->SetIntField(result, errnoField, errno);
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_PosixFileFunctions_chmod(JNIEnv *env, jclass target, jstring path, jint mode, jobject result) {
const char* pathUtf8 = env->GetStringUTFChars(path, NULL);
int retval = chmod(pathUtf8, mode);
if (retval != 0) {
markFailed(env, result);
}
}
JNIEXPORT void JNICALL
Java_net_rubygrapefruit_platform_internal_PosixFileFunctions_stat(JNIEnv *env, jclass target, jstring path, jobject dest, jobject result) {
struct stat fileInfo;
const char* pathUtf8 = env->GetStringUTFChars(path, NULL);
int retval = stat(pathUtf8, &fileInfo);
if (retval != 0) {
markFailed(env, result);
return;
}
jclass destClass = env->GetObjectClass(dest);
jfieldID modeField = env->GetFieldID(destClass, "mode", "I");
env->SetIntField(dest, modeField, 0777 & fileInfo.st_mode);
}

View File

@@ -0,0 +1,7 @@
package net.rubygrapefruit.platform;
public class NativeException extends RuntimeException {
public NativeException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,4 @@
package net.rubygrapefruit.platform;
public interface NativeIntegration {
}

View File

@@ -0,0 +1,48 @@
package net.rubygrapefruit.platform;
import net.rubygrapefruit.platform.internal.FileStat;
import net.rubygrapefruit.platform.internal.FunctionResult;
import net.rubygrapefruit.platform.internal.PosixFileFunctions;
import java.io.File;
import java.io.IOException;
public class Platform {
private static final Object lock = new Object();
private static boolean loaded;
static <T extends NativeIntegration> T get(Class<T> type) {
synchronized (lock) {
if (!loaded) {
System.setProperty("java.library.path", new File("build/binaries").getAbsolutePath());
try {
System.load(new File("build/binaries/libnative-platform.dylib").getCanonicalPath());
} catch (IOException e) {
throw new RuntimeException(e);
}
loaded = true;
}
}
return type.cast(new UnixFileMode() {
@Override
public void setMode(File file, int perms) {
FunctionResult result = new FunctionResult();
PosixFileFunctions.chmod(file.getPath(), perms, result);
if (result.isFailed()) {
throw new NativeException(String.format("Could not set UNIX mode on %s. Errno is %d.", file, result.getErrno()));
}
}
@Override
public int getMode(File file) {
FunctionResult result = new FunctionResult();
FileStat stat = new FileStat();
PosixFileFunctions.stat(file.getPath(), stat, result);
if (result.isFailed()) {
throw new NativeException(String.format("Could not get UNIX mode on %s. Errno is %d.", file, result.getErrno()));
}
return stat.mode;
}
});
}
}

View File

@@ -0,0 +1,9 @@
package net.rubygrapefruit.platform;
import java.io.File;
public interface UnixFileMode extends NativeIntegration {
void setMode(File path, int perms) throws NativeException;
int getMode(File path) throws NativeException;
}

View File

@@ -0,0 +1,5 @@
package net.rubygrapefruit.platform.internal;
public class FileStat {
public int mode;
}

View File

@@ -0,0 +1,13 @@
package net.rubygrapefruit.platform.internal;
public class FunctionResult {
int errno;
public boolean isFailed() {
return errno != 0;
}
public int getErrno() {
return errno;
}
}

View File

@@ -0,0 +1,7 @@
package net.rubygrapefruit.platform.internal;
public class PosixFileFunctions {
public static native void chmod(String file, int perms, FunctionResult result);
public static native void stat(String file, FileStat stat, FunctionResult result);
}

View File

@@ -0,0 +1,52 @@
package net.rubygrapefruit.platform
import spock.lang.Specification
import org.junit.Rule
import org.junit.rules.TemporaryFolder
class UnixFileModeTest extends Specification {
@Rule TemporaryFolder tmpDir
final UnixFileMode file = Platform.get(UnixFileMode.class)
def "can set mode on a file"() {
def testFile = tmpDir.newFile("test.txt")
when:
file.setMode(testFile, 0740)
then:
file.getMode(testFile) == 0740
}
def "can set mode on a file with unicode in its name"() {
def testFile = tmpDir.newFile("test\u03b1.txt")
when:
file.setMode(testFile, 0740)
then:
file.getMode(testFile) == 0740
}
def "throws exception on failure to set mode"() {
def file = new File(tmpDir.root, "unknown")
when:
this.file.setMode(file, 0660)
then:
NativeException e = thrown()
e.message == "Could not set UNIX mode on $file. Errno is 2."
}
def "throws exception on failure to get mode"() {
def file = new File(tmpDir.root, "unknown")
when:
this.file.getMode(file)
then:
NativeException e = thrown()
e.message == "Could not get UNIX mode on $file. Errno is 2."
}
}