[JVM-Packages] Add support for detecting musl-based Linux (#7624)

Co-authored-by: Marc Philipp <marc@gradle.com>
This commit is contained in:
Daniel Clausen
2022-03-13 17:37:27 +01:00
committed by GitHub
parent 04fc575c0e
commit 4dafb5fac8
5 changed files with 296 additions and 10 deletions

View File

@@ -21,7 +21,12 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Locale;
import java.util.Optional;
import java.util.stream.Stream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -34,6 +39,8 @@ import org.apache.commons.logging.LogFactory;
class NativeLibLoader {
private static final Log logger = LogFactory.getLog(NativeLibLoader.class);
private static Path mappedFilesBaseDir = Paths.get("/proc/self/map_files");
/**
* Supported OS enum.
*/
@@ -41,14 +48,19 @@ class NativeLibLoader {
WINDOWS("windows"),
MACOS("macos"),
LINUX("linux"),
LINUX_MUSL("linux-musl"),
SOLARIS("solaris");
final String name;
private OS(String name) {
OS(String name) {
this.name = name;
}
static void setMappedFilesBaseDir(Path baseDir) {
mappedFilesBaseDir = baseDir;
}
/**
* Detects the OS using the system properties.
* Throws IllegalStateException if the OS is not recognized.
@@ -61,13 +73,47 @@ class NativeLibLoader {
} else if (os.contains("win")) {
return WINDOWS;
} else if (os.contains("nux")) {
return LINUX;
return isMuslBased() ? LINUX_MUSL : LINUX;
} else if (os.contains("sunos")) {
return SOLARIS;
} else {
throw new IllegalStateException("Unsupported OS:" + os);
}
}
/**
* Checks if the Linux OS is musl based. For this, we check the memory-mapped
* filenames and see if one of those contains the string "musl".
*
* @return true if the Linux OS is musl based, false otherwise.
*/
static boolean isMuslBased() {
try (Stream<Path> dirStream = Files.list(mappedFilesBaseDir)) {
Optional<String> muslRelatedMemoryMappedFilename = dirStream
.map(OS::toRealPath)
.filter(s -> s.toLowerCase().contains("musl"))
.findFirst();
muslRelatedMemoryMappedFilename.ifPresent(muslFilename -> {
logger.debug("Assuming that detected Linux OS is musl-based, "
+ "because a memory-mapped file '" + muslFilename + "' was found.");
});
return muslRelatedMemoryMappedFilename.isPresent();
} catch (IOException ignored) {
// ignored
}
return false;
}
private static String toRealPath(Path path) {
try {
return path.toRealPath().toString();
} catch (IOException e) {
return "";
}
}
}
/**
@@ -80,7 +126,7 @@ class NativeLibLoader {
final String name;
private Arch(String name) {
Arch(String name) {
this.name = name;
}
@@ -115,7 +161,7 @@ class NativeLibLoader {
* <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 UnsatisfiedLinkError if the library failed to load its dependencies.
* @throws IOException If the library could not be extracted from the jar.
*/
static synchronized void initXGBoost() throws IOException {
@@ -129,18 +175,37 @@ class NativeLibLoader {
platform + "/" + System.mapLibraryName(libName);
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");
String failureMessageIncludingOpenMPHint = "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(failureMessageIncludingOpenMPHint);
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");
logger.error(failureMessageIncludingOpenMPHint);
logger.error("You may need to install 'libomp.dylib', via `brew install libomp` " +
"or similar");
break;
case LINUX:
logger.error(failureMessageIncludingOpenMPHint);
logger.error("You may need to install 'libgomp.so' (or glibc) via your package " +
"manager.");
logger.error("Alternatively, your Linux OS is musl-based " +
"but wasn't detected as such.");
break;
case LINUX_MUSL:
logger.error(failureMessageIncludingOpenMPHint);
logger.error("You may need to install 'libgomp.so' (or glibc) via your package " +
"manager.");
logger.error("Alternatively, your Linux OS was wrongly detected as musl-based, " +
"although it is not.");
break;
case SOLARIS:
logger.error(failureMessageIncludingOpenMPHint);
logger.error("You may need to install 'libgomp.so' (or glibc) via your package " +
"manager.");
break;