mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-14 09:46:53 -04:00
Make native library config options more flexible
This replaces the forceNativeLibWithName config option with two new ones: forceNativeLibPlatform and forceNativeLibPathFirst. forceNativeLibPlatform allows overriding the normally auto-detected platform string to a custom value, in case the user is on an unsupported platform. forceNativeLibPathFirst allows choosing a directory to check for natives in, instead of always searching in the jar for one. This allows custom natives to be used without packing them into the mod jar first, which should be much easier for end users.
This commit is contained in:
parent
00c5a9b3dd
commit
b40b330e6f
@ -1506,10 +1506,21 @@ opencomputers {
|
||||
# to work.
|
||||
logFullNativeLibLoadErrors: false
|
||||
|
||||
# Force loading one specific library, to avoid trying to load any
|
||||
# others. Use this if you get warnings in the log or are told to do
|
||||
# so for debugging purposes ;-)
|
||||
forceNativeLibWithName: ""
|
||||
# Force the platform name for the native libraries, instead of relying
|
||||
# on auto-detecting it. Use this if your system is using an otherwise
|
||||
# unsupported operating system or CPU architecture. If unsure, leave blank.
|
||||
#
|
||||
# Examples of platform strings include "solaris-x86_64" for 64-bit Solaris,
|
||||
# or "windows-aarch64" for Windows on the aarch64 (64-bit arm) architecture.
|
||||
forceNativeLibPlatform: ""
|
||||
|
||||
# Force the native library loader to check the specified directory for natives first,
|
||||
# before trying to find libraries packaged with OpenComputers. Use this if you want to
|
||||
# use custom native libraries, or are on an unsupported platform. If unsure, leave blank.
|
||||
#
|
||||
# This can be an absolute or relative path. If relative, the base directory will be the .minecraft
|
||||
# directory of the running Minecraft instance.
|
||||
forceNativeLibPathFirst: ""
|
||||
|
||||
# Used to suppress log spam for OpenGL errors on derpy drivers. I'm
|
||||
# quite certain the code in the font render is valid, display list
|
||||
|
@ -438,7 +438,8 @@ class Settings(val config: Config) {
|
||||
val limitMemory = !config.getBoolean("debug.disableMemoryLimit")
|
||||
val forceCaseInsensitive = config.getBoolean("debug.forceCaseInsensitiveFS")
|
||||
val logFullLibLoadErrors = config.getBoolean("debug.logFullNativeLibLoadErrors")
|
||||
val forceNativeLib = config.getString("debug.forceNativeLibWithName")
|
||||
val forceNativeLibPlatform = config.getString("debug.forceNativeLibPlatform")
|
||||
val forceNativeLibPathFirst = config.getString("debug.forceNativeLibPathFirst")
|
||||
val logOpenGLErrors = config.getBoolean("debug.logOpenGLErrors")
|
||||
val logHexFontErrors = config.getBoolean("debug.logHexFontErrors")
|
||||
val alwaysTryNative = config.getBoolean("debug.alwaysTryNative")
|
||||
|
@ -4,8 +4,8 @@ import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.nio.channels.Channels
|
||||
import java.nio.file.Paths
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import com.google.common.base.Strings
|
||||
import com.google.common.io.PatternFilenameFilter
|
||||
import li.cil.oc.OpenComputers
|
||||
@ -130,36 +130,39 @@ abstract class LuaStateFactory {
|
||||
private var currentLib = ""
|
||||
|
||||
private val libraryName = {
|
||||
if (!Strings.isNullOrEmpty(Settings.get.forceNativeLib)) Settings.get.forceNativeLib
|
||||
|
||||
else {
|
||||
val libExtension = {
|
||||
if (SystemUtils.IS_OS_MAC) ".dylib"
|
||||
else if (SystemUtils.IS_OS_WINDOWS) ".dll"
|
||||
else ".so"
|
||||
}
|
||||
|
||||
val systemName = {
|
||||
if (SystemUtils.IS_OS_FREE_BSD) "freebsd"
|
||||
else if (SystemUtils.IS_OS_NET_BSD) "netbsd"
|
||||
else if (SystemUtils.IS_OS_OPEN_BSD) "openbsd"
|
||||
else if (SystemUtils.IS_OS_SOLARIS) "solaris"
|
||||
else if (SystemUtils.IS_OS_LINUX) "linux"
|
||||
else if (SystemUtils.IS_OS_MAC) "darwin"
|
||||
else if (SystemUtils.IS_OS_WINDOWS) "windows"
|
||||
else "unknown"
|
||||
}
|
||||
|
||||
val archName = {
|
||||
if (Architecture.IS_OS_ARM64) "aarch64"
|
||||
else if (Architecture.IS_OS_ARM) "arm"
|
||||
else if (Architecture.IS_OS_X64) "x86_64"
|
||||
else if (Architecture.IS_OS_X86) "x86"
|
||||
else "unknown"
|
||||
}
|
||||
|
||||
"libjnlua" + version + "-" + systemName + "-" + archName + libExtension
|
||||
val libExtension = {
|
||||
if (SystemUtils.IS_OS_MAC) ".dylib"
|
||||
else if (SystemUtils.IS_OS_WINDOWS) ".dll"
|
||||
else ".so"
|
||||
}
|
||||
|
||||
val platformName = {
|
||||
if (!Strings.isNullOrEmpty(Settings.get.forceNativeLibPlatform)) Settings.get.forceNativeLibPlatform
|
||||
else {
|
||||
val systemName = {
|
||||
if (SystemUtils.IS_OS_FREE_BSD) "freebsd"
|
||||
else if (SystemUtils.IS_OS_NET_BSD) "netbsd"
|
||||
else if (SystemUtils.IS_OS_OPEN_BSD) "openbsd"
|
||||
else if (SystemUtils.IS_OS_SOLARIS) "solaris"
|
||||
else if (SystemUtils.IS_OS_LINUX) "linux"
|
||||
else if (SystemUtils.IS_OS_MAC) "darwin"
|
||||
else if (SystemUtils.IS_OS_WINDOWS) "windows"
|
||||
else "unknown"
|
||||
}
|
||||
|
||||
val archName = {
|
||||
if (Architecture.IS_OS_ARM64) "aarch64"
|
||||
else if (Architecture.IS_OS_ARM) "arm"
|
||||
else if (Architecture.IS_OS_X64) "x86_64"
|
||||
else if (Architecture.IS_OS_X86) "x86"
|
||||
else "unknown"
|
||||
}
|
||||
|
||||
systemName + "-" + archName
|
||||
}
|
||||
}
|
||||
|
||||
"libjnlua" + version + "-" + platformName + libExtension
|
||||
}
|
||||
|
||||
protected def create(maxMemory: Option[Int] = None): jnlua.LuaState
|
||||
@ -170,13 +173,26 @@ abstract class LuaStateFactory {
|
||||
|
||||
def isAvailable = haveNativeLibrary
|
||||
|
||||
val is64Bit = Architecture.IS_OS_X64
|
||||
val is64Bit = {
|
||||
val dataModel = try System.getProperty("sun.arch.data.model") catch {
|
||||
case ex: SecurityException => null
|
||||
}
|
||||
|
||||
if (dataModel != null)
|
||||
"64".equals(dataModel)
|
||||
else {
|
||||
// Best effort, will probably miss some esoteric architectures
|
||||
// Examples this works for: x86_64, ppc64le, aarch64_be
|
||||
SystemUtils.OS_ARCH.matches(".*64_?([bl]e)?")
|
||||
}
|
||||
}
|
||||
|
||||
// Since we use native libraries we have to do some work. This includes
|
||||
// figuring out what we're running on, so that we can load the proper shared
|
||||
// libraries compiled for that system. It also means we have to unpack the
|
||||
// shared libraries somewhere so that we can load them, because we cannot
|
||||
// load them directly from a JAR.
|
||||
// load them directly from a JAR. Lastly, we need to handle library overrides in
|
||||
// case the user wants to use custom libraries, or are not on a supported platform.
|
||||
def init() {
|
||||
if (libraryName == null) {
|
||||
return
|
||||
@ -194,108 +210,123 @@ abstract class LuaStateFactory {
|
||||
}
|
||||
}
|
||||
|
||||
val libraryUrl = classOf[Machine].getResource(s"/assets/${Settings.resourceDomain}/lib/$libraryName")
|
||||
if (libraryUrl == null) {
|
||||
OpenComputers.log.warn(s"Native library with name '$version/$libraryName' not found.")
|
||||
return
|
||||
var tmpLibFile: File = null
|
||||
if (!Strings.isNullOrEmpty(Settings.get.forceNativeLibPathFirst)) {
|
||||
val libraryTest = new File(Settings.get.forceNativeLibPathFirst, libraryName);
|
||||
if (libraryTest.canRead) {
|
||||
tmpLibFile = libraryTest
|
||||
currentLib = libraryTest.getAbsolutePath
|
||||
OpenComputers.log.info(s"Found forced-path filesystem library $currentLib.")
|
||||
}
|
||||
else
|
||||
OpenComputers.log.warn(s"forceNativeLibPathFirst is set, but $currentLib was not found there. Falling back to checking the built-in libraries.")
|
||||
}
|
||||
|
||||
val tmpLibName = s"OpenComputersMod-${OpenComputers.Version}-$version-$libraryName"
|
||||
val tmpBasePath = if (Settings.get.nativeInTmpDir) {
|
||||
val path = System.getProperty("java.io.tmpdir")
|
||||
if (path == null) ""
|
||||
else if (path.endsWith("/") || path.endsWith("\\")) path
|
||||
else path + "/"
|
||||
}
|
||||
else "./"
|
||||
val tmpLibFile = new File(tmpBasePath + tmpLibName)
|
||||
if (currentLib.isEmpty) {
|
||||
val libraryUrl = classOf[Machine].getResource(s"/assets/${Settings.resourceDomain}/lib/$libraryName")
|
||||
if (libraryUrl == null) {
|
||||
OpenComputers.log.warn(s"Native library with name '$libraryName' not found.")
|
||||
return
|
||||
}
|
||||
|
||||
// Clean up old library files when not in tmp dir.
|
||||
if (!Settings.get.nativeInTmpDir) {
|
||||
val libDir = new File(tmpBasePath)
|
||||
if (libDir.isDirectory) {
|
||||
for (file <- libDir.listFiles(new PatternFilenameFilter("^" + Pattern.quote("OpenComputersMod-") + ".*" + Pattern.quote("-" + libraryName) + "$"))) {
|
||||
if (file.compareTo(tmpLibFile) != 0) {
|
||||
file.delete()
|
||||
val tmpLibName = s"OpenComputersMod-${OpenComputers.Version}-$version-$libraryName"
|
||||
val tmpBasePath = if (Settings.get.nativeInTmpDir) {
|
||||
val path = System.getProperty("java.io.tmpdir")
|
||||
if (path == null) ""
|
||||
else if (path.endsWith("/") || path.endsWith("\\")) path
|
||||
else path + "/"
|
||||
}
|
||||
else "./"
|
||||
tmpLibFile = new File(tmpBasePath + tmpLibName)
|
||||
|
||||
// Clean up old library files when not in tmp dir.
|
||||
if (!Settings.get.nativeInTmpDir) {
|
||||
val libDir = new File(tmpBasePath)
|
||||
if (libDir.isDirectory) {
|
||||
for (file <- libDir.listFiles(new PatternFilenameFilter("^" + Pattern.quote("OpenComputersMod-") + ".*" + Pattern.quote("-" + libraryName) + "$"))) {
|
||||
if (file.compareTo(tmpLibFile) != 0) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the file, already exists, make sure it's the same we need, if it's
|
||||
// not disable use of the natives.
|
||||
if (tmpLibFile.exists()) {
|
||||
var matching = true
|
||||
try {
|
||||
val inCurrent = libraryUrl.openStream()
|
||||
val inExisting = new FileInputStream(tmpLibFile)
|
||||
var inCurrentByte = 0
|
||||
var inExistingByte = 0
|
||||
do {
|
||||
inCurrentByte = inCurrent.read()
|
||||
inExistingByte = inExisting.read()
|
||||
if (inCurrentByte != inExistingByte) {
|
||||
matching = false
|
||||
inCurrentByte = -1
|
||||
inExistingByte = -1
|
||||
}
|
||||
}
|
||||
while (inCurrentByte != -1 && inExistingByte != -1)
|
||||
inCurrent.close()
|
||||
inExisting.close()
|
||||
}
|
||||
catch {
|
||||
case _: Throwable =>
|
||||
matching = false
|
||||
}
|
||||
if (!matching) {
|
||||
// Try to delete an old instance of the library, in case we have an update
|
||||
// and deleteOnExit fails (which it regularly does on Windows it seems).
|
||||
// Note that this should only ever be necessary for dev-builds, where the
|
||||
// version number didn't change (since the version number is part of the name).
|
||||
// If the file, already exists, make sure it's the same we need, if it's
|
||||
// not disable use of the natives.
|
||||
if (tmpLibFile.exists()) {
|
||||
var matching = true
|
||||
try {
|
||||
tmpLibFile.delete()
|
||||
val inCurrent = libraryUrl.openStream()
|
||||
val inExisting = new FileInputStream(tmpLibFile)
|
||||
var inCurrentByte = 0
|
||||
var inExistingByte = 0
|
||||
do {
|
||||
inCurrentByte = inCurrent.read()
|
||||
inExistingByte = inExisting.read()
|
||||
if (inCurrentByte != inExistingByte) {
|
||||
matching = false
|
||||
inCurrentByte = -1
|
||||
inExistingByte = -1
|
||||
}
|
||||
}
|
||||
while (inCurrentByte != -1 && inExistingByte != -1)
|
||||
inCurrent.close()
|
||||
inExisting.close()
|
||||
}
|
||||
catch {
|
||||
case t: Throwable => // Ignore.
|
||||
case _: Throwable =>
|
||||
matching = false
|
||||
}
|
||||
if (tmpLibFile.exists()) {
|
||||
OpenComputers.log.warn(s"Could not update native library '${tmpLibFile.getName}'!")
|
||||
if (!matching) {
|
||||
// Try to delete an old instance of the library, in case we have an update
|
||||
// and deleteOnExit fails (which it regularly does on Windows it seems).
|
||||
// Note that this should only ever be necessary for dev-builds, where the
|
||||
// version number didn't change (since the version number is part of the name).
|
||||
try {
|
||||
tmpLibFile.delete()
|
||||
}
|
||||
catch {
|
||||
case t: Throwable => // Ignore.
|
||||
}
|
||||
if (tmpLibFile.exists()) {
|
||||
OpenComputers.log.warn(s"Could not update native library '${tmpLibFile.getName}'!")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the file contents to the temporary file.
|
||||
try {
|
||||
val in = Channels.newChannel(libraryUrl.openStream())
|
||||
// Copy the file contents to the temporary file.
|
||||
try {
|
||||
val out = new FileOutputStream(tmpLibFile).getChannel
|
||||
val in = Channels.newChannel(libraryUrl.openStream())
|
||||
try {
|
||||
out.transferFrom(in, 0, Long.MaxValue)
|
||||
tmpLibFile.deleteOnExit()
|
||||
// Set file permissions more liberally for multi-user+instance servers.
|
||||
tmpLibFile.setReadable(true, false)
|
||||
tmpLibFile.setWritable(true, false)
|
||||
tmpLibFile.setExecutable(true, false)
|
||||
val out = new FileOutputStream(tmpLibFile).getChannel
|
||||
try {
|
||||
out.transferFrom(in, 0, Long.MaxValue)
|
||||
tmpLibFile.deleteOnExit()
|
||||
// Set file permissions more liberally for multi-user+instance servers.
|
||||
tmpLibFile.setReadable(true, false)
|
||||
tmpLibFile.setWritable(true, false)
|
||||
tmpLibFile.setExecutable(true, false)
|
||||
}
|
||||
finally {
|
||||
out.close()
|
||||
}
|
||||
}
|
||||
finally {
|
||||
out.close()
|
||||
in.close()
|
||||
}
|
||||
}
|
||||
finally {
|
||||
in.close()
|
||||
catch {
|
||||
// Java (or Windows?) locks the library file when opening it, so any
|
||||
// further tries to update it while another instance is still running
|
||||
// will fail. We still want to try each time, since the files may have
|
||||
// been updated.
|
||||
// Alternatively, the file could not be opened for reading/writing.
|
||||
case t: Throwable => // Nothing.
|
||||
}
|
||||
// Try to load the lib.
|
||||
currentLib = tmpLibFile.getAbsolutePath
|
||||
}
|
||||
catch {
|
||||
// Java (or Windows?) locks the library file when opening it, so any
|
||||
// further tries to update it while another instance is still running
|
||||
// will fail. We still want to try each time, since the files may have
|
||||
// been updated.
|
||||
// Alternatively, the file could not be opened for reading/writing.
|
||||
case t: Throwable => // Nothing.
|
||||
}
|
||||
// Try to load the lib.
|
||||
currentLib = tmpLibFile.getAbsolutePath
|
||||
|
||||
try {
|
||||
LuaStateFactory.synchronized {
|
||||
System.load(currentLib)
|
||||
|
Loading…
x
Reference in New Issue
Block a user