Как создать make-файл для конфигурации JNI в Linux и eclipse

1

Я внимательно следил за учебником JNI здесь, чтобы использовать некоторые собственные функции, которые работают с магнитными ленточными накопителями. Поэтому я хочу создать программу Java, которая открывает ленты с использованием этих родных библиотек.

Для достижения своей цели я использую Eclipse Kepler CDT на Linux 12.04

Ниже я упоминаю сделанные шаги:

Прежде всего, у меня есть три класса Java:

TestTape
BasicTapeDevice
LogicalEOMException

и, наконец, файл TapeLinux.c

TestTape содержит основную функцию.

public class TestTape {

public static void main(String[] args) throws IOException {


    BasicTapeDevice d = new BasicTapeDevice("/dev/nst0");

    System.out.print("Rewinding...");
    System.out.flush();
    d.rewind();
    System.out.println("done!");

    System.out.print("Spacing to end of data...");
    System.out.flush();
    d.spaceEOD();
    System.out.println("done!");
}

}

Ниже класса BasicTapeDevice, который имеет реализацию Java этих встроенных функций:

class BasicTapeDevice {

private FileDescriptor fd;
private InputStream in;
private OutputStream out;
private boolean eof;
private boolean eom;
private boolean ignoreEOM;

public BasicTapeDevice(String pathName) throws IOException {
    fd = new FileDescriptor();
    tapeOpen(pathName);
    in = new TapeInputStream();
    out = new TapeOutputStream();
    eof = false;
    eom = false;
    ignoreEOM = false;
}
public synchronized void close() throws IOException {
    if (fd != null) {
        try {
            if (fd.valid()) {
                tapeClose();
            }
        } finally {
            fd = null;
        }
    }
}
public InputStream getInputStream() throws IOException {
    ensureOpen();
    return in;
}
public OutputStream getOutputStream() throws IOException {
    ensureOpen();
    return out;
}
public int getBlockSize() throws IOException {
    ensureOpen();
    return tapeGetBlockSize();
}
public void setBlockSize(int bs) throws IOException {
    ensureOpen();
    tapeSetBlockSize(bs);
}
public void rewind() throws IOException {
    ensureOpen();
    tapeRewind();
}
public void spaceEOD() throws IOException {
    ensureOpen();
    tapeSpaceEOD();
}
public void clearEOF() throws IOException {
    ensureOpen();
    if (eof) {
        eof = false;
        /* assume that the file mark has already been skipped */
    } else { 
        throw new IOException("not at end of file");
    }
}
public void clearEOM() throws IOException {
    ensureOpen();

    if (eom) {
        ignoreEOM = true;
    } else {
        throw new IOException("not at logical end of media");
    }
}
class TapeInputStream extends InputStream {
    private byte[] temp = new byte[1];
    public int read() throws IOException {
        int n = read(temp, 0, 1);
        if (n <= 0) {
            return -1;
        }
        return temp[0] & 0xff;
    }
    public int read(byte[] b, int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        }
        if (off < 0 || len < 0 || off+len > b.length) {
            throw new IndexOutOfBoundsException();
        }
        if (len == 0) {
            return 0;
        }
        if (eof) {
            return -1;
        }
        ensureOpen();
        int n = tapeRead(b, off, len);
        if (n <= 0) {
            return -1;
        }
        return n;
    }
    public long skip(long numbytes) throws IOException {
        return 0;
    }
    public void close() throws IOException {
        BasicTapeDevice.this.close();
    }
}
class TapeOutputStream extends OutputStream {

    private byte[] temp = new byte[1];

    public void write(int b) throws IOException {
        temp[0] = (byte) b;
        write(temp, 0, 1);
    }
    public void write(byte[] b) throws IOException {
        write(b, 0, b.length);
    }
    public void write(byte[] b, int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();

        }
        if (off < 0 || len < 0 || off+len > b.length) {
            throw new IndexOutOfBoundsException();
        }
        if (eom && !ignoreEOM) {
            throw new LogicalEOMException("logical end-of-media");
        }
        int n = tapeWrite(b, off, len);
        while (n < len) {
            n += tapeWrite(b, off + n, len - n);
        }
    }
    public void close() throws IOException {
        BasicTapeDevice.this.close();
    }
}
protected void finalize() {
    try {
        close();
    } catch (IOException ex) {
    }
}
private void ensureOpen() throws IOException {
    if (fd == null || !fd.valid()) {
        throw new IOException("tape device is not open");
    }
}
private static native void initFields();
private native void tapeOpen(String pathName) throws IOException;
private native void tapeClose() throws IOException;
private native int tapeRead(byte[] b, int off, int len) throws IOException;
private native int tapeWrite(byte[] b, int off, int len) throws IOException;
private native int tapeGetBlockSize() throws IOException;
private native void tapeSetBlockSize(int bs) throws IOException;
private native void tapeRewind() throws IOException;
private native void tapeSpaceEOD() throws IOException;

/* load the JNI library specific for this platform */
static {
    StringBuffer buf = new StringBuffer("Tape");
    String osName = System.getProperty("os.name");
    if (osName.equals("Windows NT") || osName.equals("Windows 2000")) {
        buf.append("WinNT");
    } else {
        buf.append(osName);
    }
    System.loadLibrary(buf.toString());
    initFields();
}

}

Возможно, вы видели, что класс BasicTapeDevice имеет System.loadLibrary(), который должен импортировать библиотеку.so, которую я собираюсь создать.

Наконец, последний класс - LogicalEOMException:

class LogicalEOMException extends IOException {

public LogicalEOMException() {
    super();
}
public LogicalEOMException(String s) {
    super(s);
}
}

Ниже TapeLinux.c

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include <sys/mtio.h>

#include <jni.h>

#include "BasicTapeDevice.h"

#define TRUE 1
#define FALSE 0


/* field IDs for commonly used object fields */
static jfieldID td_fdID;
static jfieldID td_eofID;
static jfieldID td_eomID;
static jfieldID IO_fd_fdID;


/* forward reference for utility functions */
static int getFD(JNIEnv* env, jobject obj);
static void setFD(JNIEnv* env, jobject obj, int fd);
static void throw(JNIEnv* env, int err);

/*
 * Class:     BasicTapeDevice
 * Method:    initFields
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_BasicTapeDevice_initFields
 (JNIEnv *env, jclass cls) 
{
/* retrieve field IDs for the fd, eof, and eom member variables */
td_fdID = (*env)->GetFieldID(env, cls, "fd", "Ljava/io/FileDescriptor;");
td_eofID = (*env)->GetFieldID(env, cls, "eof", "Z");
td_eomID = (*env)->GetFieldID(env, cls, "eom", "Z");

/* retrieve the field ID for the private fd member of FileDescriptor */
cls = (*env)->FindClass(env, "java/io/FileDescriptor");
IO_fd_fdID = (*env)->GetFieldID(env, cls, "fd", "I");
}

/*
 * Class:     BasicTapeDevice
 * Method:    tapeOpen
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_BasicTapeDevice_tapeOpen
 (JNIEnv *env, jobject this, jstring path)
{
int fd;
const char* p;

p = (*env)->GetStringUTFChars(env, path, 0);
fd = open(p, O_RDWR);
(*env)->ReleaseStringUTFChars(env, path, p);

if (fd == -1) {
    throw(env, errno);
}

setFD(env, this, fd);
}

/*
 * Class:     BasicTapeDevice
 * Method:    tapeClose
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_BasicTapeDevice_tapeClose
 (JNIEnv *env, jobject this)
{
int fd = getFD(env, this);
if (close(fd) == -1) {
    throw(env, errno);
}

fd = -1;
setFD(env, this, fd);
}


/*
 * Class:     BasicTapeDevice
 * Method:    tapeRead
 * Signature: ([BII)I
 */
JNIEXPORT jint JNICALL Java_BasicTapeDevice_tapeRead
 (JNIEnv *env, jobject this, jbyteArray buf, jint off, jint len)
{
int n, fd;
jbyte* bufp;

fd = getFD(env, this);
bufp = (*env)->GetByteArrayElements(env, buf, 0);
n = read(fd, &bufp[off], len);
(*env)->ReleaseByteArrayElements(env, buf, bufp, 0);

if (n < 0) {
    throw(env, errno);
} else if (n == 0) {
    (*env)->SetBooleanField(env, this, td_eofID, TRUE);
}

return n;
}

/*
 * Class:     BasicTapeDevice
 * Method:    tapeWrite
 * Signature: ([BII)I
 */
JNIEXPORT jint JNICALL Java_BasicTapeDevice_tapeWrite
 (JNIEnv *env, jobject this, jbyteArray buf, jint off, jint len)
{
int n, fd;
jbyte* bufp;

fd = getFD(env, this);
bufp = (*env)->GetByteArrayElements(env, buf, 0);
n = write(fd, &bufp[off], len);
(*env)->ReleaseByteArrayElements(env, buf, bufp, 0);

if (n < 0) {
    throw(env, errno);
} else if (n == 0) {
    (*env)->SetBooleanField(env, this, td_eomID, TRUE);
}

return n;
}


/*
 * Class:     BasicTapeDevice
 * Method:    tapeGetBlockSize
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_BasicTapeDevice_tapeGetBlockSize
 (JNIEnv *env, jobject this)
{
int fd;
struct mtget mtget;
jint bs;

fd = getFD(env, this);
if (ioctl(fd, MTIOCGET, &mtget) == -1) {
    throw(env, errno);
    bs = -1;
} else {
    bs = mtget.mt_dsreg & MT_ST_BLKSIZE_MASK;
}

return bs;
}

/*
 * Class:     BasicTapeDevice
 * Method:    tapeSetBlockSize
 * Signature: ()I
 */
JNIEXPORT void JNICALL Java_BasicTapeDevice_tapeSetBlockSize
 (JNIEnv *env, jobject this, jint bs)
{
int fd;
struct mtop mtop;

mtop.mt_op = MTSETBLK;
mtop.mt_count = bs;
fd = getFD(env, this);
if (ioctl(fd, MTIOCTOP, &mtop) == -1) {
    throw(env, errno);
}
}


/*
 * Class:     BasicTapeDevice
 * Method:    tapeRewind
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_BasicTapeDevice_tapeRewind
 (JNIEnv *env, jobject this)
{
int fd;
struct mtop mtop;

mtop.mt_op = MTREW;
mtop.mt_count = 1;
fd = getFD(env, this);
if (ioctl(fd, MTIOCTOP, &mtop) == -1) {
    throw(env, errno);
}
}

/*
 * Class:     BasicTapeDevice
 * Method:    tapeSpaceEOD
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_BasicTapeDevice_tapeSpaceEOD
 (JNIEnv* env, jobject this)
{
int fd;
struct mtop mtop;

mtop.mt_op = MTEOM;
mtop.mt_count = 1;
fd = getFD(env, this);
if (ioctl(fd, MTIOCTOP, &mtop) == -1) {
    throw(env, errno);
}
}

/*
 * Retrieves the internal file descriptor from the BasicTapeDevice object
 */
static int getFD(JNIEnv* env, jobject obj) {
jobject fdobj;

fdobj = (*env)->GetObjectField(env, obj, td_fdID);
return (*env)->GetIntField(env, fdobj, IO_fd_fdID);
}

/*
 * Sets the internal file descriptor of the BasicTapeDevice object
 */
static void setFD(JNIEnv* env, jobject obj, int fd)
{
jobject fdobj = (*env)->GetObjectField(env, obj, td_fdID);
(*env)->SetIntField(env, fdobj, IO_fd_fdID, fd);
}

/*
 * Throws a new IOException
 */
static void throw(JNIEnv* env, int err)
{
jclass cls = (*env)->FindClass(env, "java/io/IOException");
if (cls != NULL) {
    (*env)->ThrowNew(env, cls, strerror(err));
}
}

Учитывая эту структуру программирования, я начну реализацию своей программы:

1) Прежде всего, я создаю проект Java (называемый Tape) тремя классами, а затем преобразую класс BasicTapeDevice в проект C/C++ (добавляет C/C++ Nature) со следующими параметрами:

Convert to C or C++ Project: C Project
Toolchains: LinuxGCC
Project Type: Makefile Project

Это создает следующее:

Изображение 174551

Пока все выглядит нормально. Теперь я создаю папку jni и создаю в ней файл makefile.

MAKEFILE

# Define a variable for classpath
CLASS_PATH = ../bin

# Define a virtual path for .class in the bin directory
vpath %.class $(CLASS_PATH)

# $* matches the target filename without the extension
BasicTapeDevice.h : BasicTapeDevice.class
javah -classpath $(CLASS_PATH) $*

Я создаю make-target для makefile BasicTapeDevice.h, а затем создаю его. Результат:

**** Build of configuration Default for project Tape ****
make BasicTapeDevice.h 
javah -classpath ../bin BasicTapeDevice

Build Finished (took 775ms)

Изображение 174551

Теперь созданный ранее файл c помещается в папку jni в файле TapeLinux.c, и теперь можно создать библиотеку.so (помните, что мы находимся под Linux)

Это созданный мной файл makefile

MAKEFILE

# Define a variable for classpath
CLASS_PATH = ../bin

# Define a virtual path for .class in the bin directory
vpath %.class $(CLASS_PATH)

all : TapeLinux.so

# $@ matches the target, $< matches the first dependancy
TapeLinux.so : TapeLinux.o
gcc -Wl,--add-stdcall-alias -shared -o $@ $<

# $@ matches the target, $< matches the first dependancy
TapeLinux.o : TapeLinux.c TapeLinux.h
gcc -I"/home/tanio/DevelopmentEnvironment/jdk1.7.0_51/include/linux" -I"/home/tanio/DevelopmentEnvironment/jdk1.7.0_51/include" -c $< -o $@

# $* matches the target filename without the extension
BasicTapeDevice.h : BasicTapeDevice.class
javah -classpath $(CLASS_PATH) $*

clean :
rm TapeLinux.h TapeLinux.o TapeLinux.so

К сожалению, я не понял эту часть должным образом, и я застрял.

Компилятор дает мне эту проблему:

**** Build of configuration Default for project Tape ****
make all 
make: *** No rule to make target 'all'. Stop.

Build Finished (took 74ms)

Не могли бы вы помочь мне выяснить, в чем проблема?

спасибо

РЕДАКТИРОВАТЬ

# Define a variable for classpath
CLASS_PATH = ../bin

# Define a virtual path for .class in the bin directory
vpath %.class $(CLASS_PATH)

all : BasicTapeDevice.so

# $@ matches the target, $< matches the first dependancy
BasicTapeDevice.so : BasicTapeDevice.o
gcc -Wl -shared -o $@ $<

# $@ matches the target, $< matches the first dependancy
BasicTapeDevice.o : TapeLinux.c BasicTapeDevice.h
gcc -I"/home/tanio/DevelopmentEnvironment/jdk1.7.0_51/include/linux" -I"/home/tanio/DevelopmentEnvironment/jdk1.7.0_51/include" -c $< -o $@

# $* matches the target filename without the extension
BasicTapeDevice.h : BasicTapeDevice.class
javah -classpath $(CLASS_PATH) $*

clean :
rm BasicTapeDevice.h BasicTapeDevice.o BasicTapeDevice.so

Этот make файл компилируется правильно, но я получаю сообщение об ошибке при запуске приложения, потому что главное не в BasicTapeDevice

Должен ли я компилировать каждый класс отдельно, а затем запускать программу или лучше разместить все в одном классе?

  • 0
    Какое правило создает TapeLinux.h ? Также ваше правило BasicTapeDevice.h использует функцию совместимости GNU make, а не первичную. $* - это «основа» правила шаблона, которое соответствует. В не-шаблонном правиле (например, вашем) оно также определяется как имя цели без (распознанного) суффикса, но, согласно руководству, это функция совместимости, а не та, от которой следует зависеть.
  • 0
    Спасибо @EtanReisner. Что я должен сделать, чтобы этот программный продукт работал? Есть ли что-то не так на пути?
Показать ещё 5 комментариев
Теги:
makefile
jni

1 ответ

1

Спасибо, ребята, за вашу помощь. Я решил проблему с модификацией make файла

Здесь результат

# Define a variable for classpath
CLASS_PATH = ../bin

# Define a virtual path for .class in the bin directory
vpath %.class $(CLASS_PATH)

all : libTape.so

# $@ matches the target, $< matches the first dependancy
libTape.so : TapeJNI.o
gcc -shared -fpic -o $@ $<

# $@ matches the target, $< matches the first dependancy
TapeJNI.o : TapeJNI.c TapeJNI.h
gcc -fpic -I"/home/tanio/DevelopmentEnvironment/jdk1.7.0_51/include" -I"/home/tanio/DevelopmentEnvironment/jdk1.7.0_51/include/linux" -c $< -o $@

# $* matches the target filename without the extension
TapeJNI.h : TapeJNI.class
javah -classpath $(CLASS_PATH) $*

clean :
rm TapeJNI.h TapeJNI.o libTape.so

Я скомпилировал его с другим именем, извините, если оно не соответствует

  • 0
    что именно ты модифицировал?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню