我正在尝试使用Gradle和LWJGL 3,但是我在构建时遇到了问题。 build.gradle
文件包含以下内容:
apply plugin: 'application'
mainClassName = "HelloWorld"
repositories {
mavenCentral()
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
}
project.ext.lwjglVersion = "3.0.0a"
dependencies {
compile "org.lwjgl:lwjgl:${lwjglVersion}"
compile "org.lwjgl:lwjgl-platform:${lwjglVersion}:natives-windows"
compile "org.lwjgl:lwjgl-platform:${lwjglVersion}:natives-linux"
compile "org.lwjgl:lwjgl-platform:${lwjglVersion}:natives-osx"
}
当我运行gradle run
时,我得到以下输出:
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:runjava.lang.UnsatisfiedLinkError: no lwjgl in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1857)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1119)
at org.lwjgl.LWJGLUtil.loadLibrarySystem(LWJGLUtil.java:337)
at org.lwjgl.Sys$1.run(Sys.java:36)
at java.security.AccessController.doPrivileged(Native Method)
at org.lwjgl.Sys.<clinit>(Sys.java:33)
at org.lwjgl.LWJGLUtil.initialize(LWJGLUtil.java:309)
at org.lwjgl.system.MemoryUtil.<clinit>(MemoryUtil.java:35)
at org.lwjgl.Pointer.<clinit>(Pointer.java:22)
at org.lwjgl.PointerBuffer.<init>(PointerBuffer.java:24)
at org.lwjgl.PointerBuffer.allocateDirect(PointerBuffer.java:281)
at org.lwjgl.BufferUtils.createPointerBuffer(BufferUtils.java:190)
at org.lwjgl.system.libffi.Closure.<clinit>(Closure.java:45)
at org.lwjgl.glfw.Callbacks.errorCallbackPrint(Callbacks.java:129)
at HelloWorld.<clinit>(HelloWorld.java:29)
Exception in thread "main" FAILED
FAILURE: Build failed with an exception.
HelloWorld.java
包含以下内容(教程中的示例代码):
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.glfw.Callbacks;
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWKeyCallback;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.glfw.GLFWvidmode;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryUtil.NULL;
public class HelloWorld {
private static GLFWErrorCallback errorCallback
= Callbacks.errorCallbackPrint(System.err);
private static GLFWKeyCallback keyCallback = new GLFWKeyCallback() {
@Override
public void invoke(long window, int key, int scancode, int action, int mods) {
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) {
glfwSetWindowShouldClose(window, GL_TRUE);
}
}
};
public static void main(String[] args) {
long window;
/* Set the error callback */
glfwSetErrorCallback(errorCallback);
/* Initialize GLFW */
if (glfwInit() != GL_TRUE) {
throw new IllegalStateException("Unable to initialize GLFW");
}
/* Create window */
window = glfwCreateWindow(640, 480, "Simple example", NULL, NULL);
if (window == NULL) {
glfwTerminate();
throw new RuntimeException("Failed to create the GLFW window");
}
/* Center the window on screen */
ByteBuffer vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
glfwSetWindowPos(window,
(GLFWvidmode.width(vidmode) - 640) / 2,
(GLFWvidmode.height(vidmode) - 480) / 2
);
glfwMakeContextCurrent(window);
GLContext.createFromCurrent();
glfwSwapInterval(1);
glfwSetKeyCallback(window, keyCallback);
IntBuffer width = BufferUtils.createIntBuffer(1);
IntBuffer height = BufferUtils.createIntBuffer(1);
while (glfwWindowShouldClose(window) != GL_TRUE) {
float ratio;
glfwGetFramebufferSize(window, width, height);
ratio = width.get() / (float) height.get();
width.rewind();
height.rewind();
glViewport(0, 0, width.get(), height.get());
glClear(GL_COLOR_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-ratio, ratio, -1f, 1f, 1f, -1f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef((float) glfwGetTime() * 50f, 0f, 0f, 1f);
glBegin(GL_TRIANGLES);
glColor3f(1f, 0f, 0f);
glVertex3f(-0.6f, -0.4f, 0f);
glColor3f(0f, 1f, 0f);
glVertex3f(0.6f, -0.4f, 0f);
glColor3f(0f, 0f, 1f);
glVertex3f(0f, 0.6f, 0f);
glEnd();
glfwSwapBuffers(window);
glfwPollEvents();
width.flip();
height.flip();
}
glfwDestroyWindow(window);
keyCallback.release();
glfwTerminate();
errorCallback.release();
}
}
导致错误的原因是什么?如何解决?
您可能已经注意到,在您下载的lwjgl文件夹(带有jar文件的文件夹)中,应该有一个名为“native”的目录。在这个目录中应该有三个带有系统名称的子文件夹(windows,mac ...)。此本机文件夹应位于项目内的文件夹中(我创建了一个名为lwjgl的文件夹)。然后,在程序的第一行,你写System.setProperty("java.library.path", "./lwjgl")
。这行告诉java在那里搜索所有本机文件。
您必须在运行时将Lwjgl的本机库链接到您的应用程序。感谢gradle,本地库位于类路径中......某处。
你可以继续找到它们,将它们提取到一个临时位置,然后链接它们,但已经有一个example project为你做了。
您需要做的就是将他们的类SharedLibraryLoader
包含在您的项目中并调用load()
方法。
如果此链接死亡,这里是您需要的课程的完整内容:
/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.UUID;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/** Loads shared libraries from JAR files. Call {@link SharedLibraryLoader#load() to load the
* required LWJGL 3 native shared libraries.
* @author mzechner
* @author Nathan Sweet */
public class SharedLibraryLoader {
static public boolean isWindows = System.getProperty("os.name").contains("Windows");
static public boolean isLinux = System.getProperty("os.name").contains("Linux");
static public boolean isMac = System.getProperty("os.name").contains("Mac");
static public boolean isIos = false;
static public boolean isAndroid = false;
static public boolean isARM = System.getProperty("os.arch").startsWith("arm");
static public boolean is64Bit = System.getProperty("os.arch").equals("amd64")
|| System.getProperty("os.arch").equals("x86_64");
// JDK 8 only.
static public String abi = (System.getProperty("sun.arch.abi") != null ? System.getProperty("sun.arch.abi") : "");
static {
String vm = System.getProperty("java.runtime.name");
if (vm != null && vm.contains("Android Runtime")) {
isAndroid = true;
isWindows = false;
isLinux = false;
isMac = false;
is64Bit = false;
}
if (!isAndroid && !isWindows && !isLinux && !isMac) {
isIos = true;
is64Bit = false;
}
}
static boolean load = true;
static {
// Don't extract natives if using JWS.
try {
Method method = Class.forName("javax.jnlp.ServiceManager").getDeclaredMethod("lookup", new Class[] {String.class});
method.invoke(null, "javax.jnlp.PersistenceService");
load = false;
} catch (Throwable ex) {
load = true;
}
}
/** Extracts the LWJGL native libraries from the classpath and sets the "org.lwjgl.librarypath" system property. */
static public synchronized void load () {
load(false);
}
/** Extracts the LWJGL native libraries from the classpath and sets the "org.lwjgl.librarypath" system property. */
static public synchronized void load (boolean disableOpenAL) {
if (!load) return;
SharedLibraryLoader loader = new SharedLibraryLoader();
File nativesDir = null;
try {
if (SharedLibraryLoader.isWindows) {
nativesDir = loader.extractFile(SharedLibraryLoader.is64Bit ? "lwjgl.dll" : "lwjgl32.dll", null).getParentFile();
if (!disableOpenAL)
loader.extractFile(SharedLibraryLoader.is64Bit ? "OpenAL.dll" : "OpenAL32.dll", nativesDir.getName());
} else if (SharedLibraryLoader.isMac) {
nativesDir = loader.extractFile("liblwjgl.dylib", null).getParentFile();
if (!disableOpenAL) loader.extractFile("libopenal.dylib", nativesDir.getName());
} else if (SharedLibraryLoader.isLinux) {
nativesDir = loader.extractFile(SharedLibraryLoader.is64Bit ? "liblwjgl.so" : "liblwjgl32.so", null).getParentFile();
if (!disableOpenAL)
loader.extractFile(SharedLibraryLoader.is64Bit ? "libopenal.so" : "libopenal32.so", nativesDir.getName());
}
} catch (Throwable ex) {
throw new RuntimeException("Unable to extract LWJGL natives.", ex);
}
System.setProperty("org.lwjgl.librarypath", nativesDir.getAbsolutePath());
load = false;
}
static private final HashSet<String> loadedLibraries = new HashSet<String>();
private String nativesJar;
public SharedLibraryLoader () {
}
/** Fetches the natives from the given natives jar file. Used for testing a shared lib on the fly.
* @param nativesJar */
public SharedLibraryLoader (String nativesJar) {
this.nativesJar = nativesJar;
}
/** Returns a CRC of the remaining bytes in the stream. */
public String crc (InputStream input) {
if (input == null) throw new IllegalArgumentException("input cannot be null.");
CRC32 crc = new CRC32();
byte[] buffer = new byte[4096];
try {
while (true) {
int length = input.read(buffer);
if (length == -1) break;
crc.update(buffer, 0, length);
}
} catch (Exception ex) {
if(input != null) {
try {
input.close();
} catch (IOException e) {
}
}
}
return Long.toString(crc.getValue(), 16);
}
/** Maps a platform independent library name to a platform dependent name. */
public String mapLibraryName (String libraryName) {
if (isWindows) return libraryName + (is64Bit ? "64.dll" : ".dll");
if (isLinux) return "lib" + libraryName + (isARM ? "arm" + abi : "") + (is64Bit ? "64.so" : ".so");
if (isMac) return "lib" + libraryName + (is64Bit ? "64.dylib" : ".dylib");
return libraryName;
}
/** Loads a shared library for the platform the application is running on.
* @param libraryName The platform independent library name. If not contain a prefix (eg lib) or suffix (eg .dll). */
public synchronized void load (String libraryName) {
// in case of iOS, things have been linked statically to the executable, bail out.
if (isIos) return;
libraryName = mapLibraryName(libraryName);
if (loadedLibraries.contains(libraryName)) return;
try {
if (isAndroid)
System.loadLibrary(libraryName);
else
loadFile(libraryName);
} catch (Throwable ex) {
throw new RuntimeException("Couldn't load shared library '" + libraryName + "' for target: "
+ System.getProperty("os.name") + (is64Bit ? ", 64-bit" : ", 32-bit"), ex);
}
loadedLibraries.add(libraryName);
}
private InputStream readFile (String path) {
if (nativesJar == null) {
InputStream input = SharedLibraryLoader.class.getResourceAsStream("/" + path);
if (input == null) throw new RuntimeException("Unable to read file for extraction: " + path);
return input;
}
// Read from JAR.
ZipFile file = null;
try {
file = new ZipFile(nativesJar);
ZipEntry entry = file.getEntry(path);
if (entry == null) throw new RuntimeException("Couldn't find '" + path + "' in JAR: " + nativesJar);
return file.getInputStream(entry);
} catch (IOException ex) {
throw new RuntimeException("Error reading '" + path + "' in JAR: " + nativesJar, ex);
} finally {
if(file != null) {
try {
file.close();
} catch (IOException e) {
}
}
}
}
/** Extracts the specified file into the temp directory if it does not already exist or the CRC does not match. If file
* extraction fails and the file exists at java.library.path, that file is returned.
* @param sourcePath The file to extract from the classpath or JAR.
* @param dirName The name of the subdirectory where the file will be extracted. If null, the file's CRC will be used.
* @return The extracted file. */
public File extractFile (String sourcePath, String dirName) throws IOException {
try {
String sourceCrc = crc(readFile(sourcePath));
if (dirName == null) dirName = sourceCrc;
File extractedFile = getExtractedFile(dirName, new File(sourcePath).getName());
return extractFile(sourcePath, sourceCrc, extractedFile);
} catch (RuntimeException ex) {
// Fallback to file at java.library.path location, eg for applets.
File file = new File(System.getProperty("java.library.path"), sourcePath);
if (file.exists()) return file;
throw ex;
}
}
/** Returns a path to a file that can be written. Tries multiple locations and verifies writing succeeds. */
private File getExtractedFile (String dirName, String fileName) {
// Temp directory with username in path.
File idealFile = new File(System.getProperty("java.io.tmpdir") + "/libgdx" + System.getProperty("user.name") + "/"
+ dirName, fileName);
if (canWrite(idealFile)) return idealFile;
// System provided temp directory.
try {
File file = File.createTempFile(dirName, null);
if (file.delete()) {
file = new File(file, fileName);
if (canWrite(file)) return file;
}
} catch (IOException ignored) {
}
// User home.
File file = new File(System.getProperty("user.home") + "/.libgdx/" + dirName, fileName);
if (canWrite(file)) return file;
// Relative directory.
file = new File(".temp/" + dirName, fileName);
if (canWrite(file)) return file;
return idealFile; // Will likely fail, but we did our best.
}
/** Returns true if the parent directories of the file can be created and the file can be written. */
private boolean canWrite (File file) {
File parent = file.getParentFile();
File testFile;
if (file.exists()) {
if (!file.canWrite() || !canExecute(file)) return false;
// Don't overwrite existing file just to check if we can write to directory.
testFile = new File(parent, UUID.randomUUID().toString());
} else {
parent.mkdirs();
if (!parent.isDirectory()) return false;
testFile = file;
}
try {
new FileOutputStream(testFile).close();
if (!canExecute(testFile)) return false;
return true;
} catch (Throwable ex) {
return false;
} finally {
testFile.delete();
}
}
private boolean canExecute (File file) {
try {
Method canExecute = File.class.getMethod("canExecute");
if ((Boolean)canExecute.invoke(file)) return true;
Method setExecutable = File.class.getMethod("setExecutable", boolean.class, boolean.class);
setExecutable.invoke(file, true, false);
return (Boolean)canExecute.invoke(file);
} catch (Exception ignored) {
}
return false;
}
private File extractFile (String sourcePath, String sourceCrc, File extractedFile) throws IOException {
String extractedCrc = null;
if (extractedFile.exists()) {
try {
extractedCrc = crc(new FileInputStream(extractedFile));
} catch (FileNotFoundException ignored) {
}
}
// If file doesn't exist or the CRC doesn't match, extract it to the temp dir.
if (extractedCrc == null || !extractedCrc.equals(sourceCrc)) {
try {
InputStream input = readFile(sourcePath);
extractedFile.getParentFile().mkdirs();
FileOutputStream output = new FileOutputStream(extractedFile);
byte[] buffer = new byte[4096];
while (true) {
int length = input.read(buffer);
if (length == -1) break;
output.write(buffer, 0, length);
}
input.close();
output.close();
} catch (IOException ex) {
throw new RuntimeException("Error extracting file: " + sourcePath + "\nTo: " + extractedFile.getAbsolutePath(), ex);
}
}
return extractedFile;
}
/** Extracts the source file and calls System.load. Attemps to extract and load from multiple locations. Throws runtime
* exception if all fail. */
private void loadFile (String sourcePath) {
String sourceCrc = crc(readFile(sourcePath));
String fileName = new File(sourcePath).getName();
// Temp directory with username in path.
File file = new File(System.getProperty("java.io.tmpdir") + "/libgdx" + System.getProperty("user.name") + "/" + sourceCrc,
fileName);
Throwable ex = loadFile(sourcePath, sourceCrc, file);
if (ex == null) return;
// System provided temp directory.
try {
file = File.createTempFile(sourceCrc, null);
if (file.delete() && loadFile(sourcePath, sourceCrc, file) == null) return;
} catch (Throwable ignored) {
}
// User home.
file = new File(System.getProperty("user.home") + "/.libgdx/" + sourceCrc, fileName);
if (loadFile(sourcePath, sourceCrc, file) == null) return;
// Relative directory.
file = new File(".temp/" + sourceCrc, fileName);
if (loadFile(sourcePath, sourceCrc, file) == null) return;
// Fallback to java.library.path location, eg for applets.
file = new File(System.getProperty("java.library.path"), sourcePath);
if (file.exists()) {
System.load(file.getAbsolutePath());
return;
}
throw new RuntimeException(ex);
}
/** @return null if the file was extracted and loaded. */
private Throwable loadFile (String sourcePath, String sourceCrc, File extractedFile) {
try {
System.load(extractFile(sourcePath, sourceCrc, extractedFile).getAbsolutePath());
return null;
} catch (Throwable ex) {
ex.printStackTrace();
return ex;
}
}
}
此示例项目与Lwjgl 3一起使用(请求OP)。但是,如果您使用Lwjgl 2.9.x它仍然有效,您只需要在load
方法中更改库的名称。
如果Java虚拟机无法找到声明为native的方法的适当本机语言定义,则抛出该异常。
因此,鉴于no lwjgl in java.library.path
,由于某种原因它无法找到本机库。对不起,没有gradle经验可以帮助你...