Making the Java library loader emit helpful error messages on missing dependencies. (#6926)
This commit is contained in:
parent
5cb51a191e
commit
2320aa0da2
@ -34,20 +34,120 @@ import org.apache.commons.logging.LogFactory;
|
|||||||
class NativeLibLoader {
|
class NativeLibLoader {
|
||||||
private static final Log logger = LogFactory.getLog(NativeLibLoader.class);
|
private static final Log logger = LogFactory.getLog(NativeLibLoader.class);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported OS enum.
|
||||||
|
*/
|
||||||
|
enum OS {
|
||||||
|
WINDOWS("windows"),
|
||||||
|
MACOS("macos"),
|
||||||
|
LINUX("linux"),
|
||||||
|
SOLARIS("solaris");
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
private OS(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects the OS using the system properties.
|
||||||
|
* Throws IllegalStateException if the OS is not recognized.
|
||||||
|
* @return The OS.
|
||||||
|
*/
|
||||||
|
static OS detectOS() {
|
||||||
|
String os = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH);
|
||||||
|
if (os.contains("mac") || os.contains("darwin")) {
|
||||||
|
return MACOS;
|
||||||
|
} else if (os.contains("win")) {
|
||||||
|
return WINDOWS;
|
||||||
|
} else if (os.contains("nux")) {
|
||||||
|
return LINUX;
|
||||||
|
} else if (os.contains("sunos")) {
|
||||||
|
return SOLARIS;
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Unsupported OS:" + os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Supported architecture enum.
|
||||||
|
*/
|
||||||
|
enum Arch {
|
||||||
|
X86_64("x86_64"),
|
||||||
|
AARCH64("aarch64"),
|
||||||
|
SPARC("sparc");
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
private Arch(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects the chip architecture using the system properties.
|
||||||
|
* Throws IllegalStateException if the architecture is not recognized.
|
||||||
|
* @return The architecture.
|
||||||
|
*/
|
||||||
|
static Arch detectArch() {
|
||||||
|
String arch = System.getProperty("os.arch", "generic").toLowerCase(Locale.ENGLISH);
|
||||||
|
if (arch.startsWith("amd64") || arch.startsWith("x86_64")) {
|
||||||
|
return X86_64;
|
||||||
|
} else if (arch.startsWith("aarch64") || arch.startsWith("arm64")) {
|
||||||
|
return AARCH64;
|
||||||
|
} else if (arch.startsWith("sparc")) {
|
||||||
|
return SPARC;
|
||||||
|
} else {
|
||||||
|
throw new IllegalStateException("Unsupported architecture:" + arch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean initialized = false;
|
private static boolean initialized = false;
|
||||||
private static final String nativeResourcePath = "/lib";
|
private static final String nativeResourcePath = "/lib";
|
||||||
private static final String[] libNames = new String[]{"xgboost4j"};
|
private static final String[] libNames = new String[]{"xgboost4j"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the XGBoost library.
|
||||||
|
* <p>
|
||||||
|
* Throws IllegalStateException if the architecture or OS is unsupported.
|
||||||
|
* <ul>
|
||||||
|
* <li>Supported OS: macOS, Windows, Linux, Solaris.</li>
|
||||||
|
* <li>Supported Architectures: x86_64, aarch64, sparc.</li>
|
||||||
|
* </ul>
|
||||||
|
* Throws UnsatisfiedLinkError if the library failed to load it's dependencies.
|
||||||
|
* @throws IOException If the library could not be extracted from the jar.
|
||||||
|
*/
|
||||||
static synchronized void initXGBoost() throws IOException {
|
static synchronized void initXGBoost() throws IOException {
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
String platform = computePlatformArchitecture();
|
OS os = OS.detectOS();
|
||||||
|
Arch arch = Arch.detectArch();
|
||||||
|
String platform = os.name + "/" + arch.name;
|
||||||
for (String libName : libNames) {
|
for (String libName : libNames) {
|
||||||
try {
|
try {
|
||||||
String libraryPathInJar = nativeResourcePath + "/" +
|
String libraryPathInJar = nativeResourcePath + "/" +
|
||||||
platform + "/" + System.mapLibraryName(libName);
|
platform + "/" + System.mapLibraryName(libName);
|
||||||
loadLibraryFromJar(libraryPathInJar);
|
loadLibraryFromJar(libraryPathInJar);
|
||||||
|
} catch (UnsatisfiedLinkError ule) {
|
||||||
|
logger.error("Failed to load " + libName + " due to missing native dependencies for " +
|
||||||
|
"platform " + platform + ", this is likely due to a missing OpenMP dependency");
|
||||||
|
switch (os) {
|
||||||
|
case WINDOWS:
|
||||||
|
logger.error("You may need to install 'vcomp140.dll' or 'libgomp-1.dll'");
|
||||||
|
break;
|
||||||
|
case MACOS:
|
||||||
|
logger.error("You may need to install 'libomp.dylib', via `brew install libomp`" +
|
||||||
|
" or similar");
|
||||||
|
break;
|
||||||
|
case LINUX:
|
||||||
|
case SOLARIS:
|
||||||
|
logger.error("You may need to install 'libgomp.so' (or glibc) via your package " +
|
||||||
|
"manager.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw ule;
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
logger.error("failed to load " + libName + " library from jar for platform " + platform);
|
logger.error("Failed to load " + libName + " library from jar for platform " + platform);
|
||||||
throw ioe;
|
throw ioe;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,44 +155,6 @@ class NativeLibLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Computes a String representing the path to look for.
|
|
||||||
* Assumes the libraries are stored in the jar in os/architecture folders.
|
|
||||||
* <p>
|
|
||||||
* Throws IllegalStateException if the architecture or OS is unsupported.
|
|
||||||
* Supported OS: macOS, Windows, Linux, Solaris.
|
|
||||||
* Supported Architectures: x86_64, aarch64, sparc.
|
|
||||||
* @return The platform & architecture path.
|
|
||||||
*/
|
|
||||||
private static String computePlatformArchitecture() {
|
|
||||||
String detectedOS;
|
|
||||||
String os = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH);
|
|
||||||
if (os.contains("mac") || os.contains("darwin")) {
|
|
||||||
detectedOS = "macos";
|
|
||||||
} else if (os.contains("win")) {
|
|
||||||
detectedOS = "windows";
|
|
||||||
} else if (os.contains("nux")) {
|
|
||||||
detectedOS = "linux";
|
|
||||||
} else if (os.contains("sunos")) {
|
|
||||||
detectedOS = "solaris";
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("Unsupported os:" + os);
|
|
||||||
}
|
|
||||||
String detectedArch;
|
|
||||||
String arch = System.getProperty("os.arch", "generic").toLowerCase(Locale.ENGLISH);
|
|
||||||
if (arch.startsWith("amd64") || arch.startsWith("x86_64")) {
|
|
||||||
detectedArch = "x86_64";
|
|
||||||
} else if (arch.startsWith("aarch64") || arch.startsWith("arm64")) {
|
|
||||||
detectedArch = "aarch64";
|
|
||||||
} else if (arch.startsWith("sparc")) {
|
|
||||||
detectedArch = "sparc";
|
|
||||||
} else {
|
|
||||||
throw new IllegalStateException("Unsupported architecture:" + arch);
|
|
||||||
}
|
|
||||||
|
|
||||||
return detectedOS + "/" + detectedArch;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads library from current JAR archive
|
* Loads library from current JAR archive
|
||||||
* <p/>
|
* <p/>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user