tdlight-java/tdlight-java/src/main/java/it/tdlight/util/Native.java

171 lines
4.8 KiB
Java

/*
* Copyright (c) 2018. Ernesto Castellotti <erny.castell@gmail.com>
* This file is part of JTdlib.
*
* JTdlib is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License.
*
* JTdlib is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with JTdlib. If not, see <http://www.gnu.org/licenses/>.
*/
package it.tdlight.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.ByteOrder;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* The class to load the libraries needed to run Tdlib
*/
public final class Native {
/**
* Internal util
*/
public static void loadNativesInternal() throws UnsupportedNativeLibraryException {
loadLibrary("tdjni");
}
private static final Logger logger = LoggerFactory.getLogger(Native.class);
/**
* Load a native library
* @param libraryName Library name
* @throws UnsupportedNativeLibraryException The library can't be loaded
*/
private static void loadLibrary(String libraryName) throws UnsupportedNativeLibraryException {
ClassLoader cl = Native.class.getClassLoader();
String staticLibName = libraryName;
List<String> sharedLibNames = getNormalizedArchitectures().map(suffix -> staticLibName + "." + suffix).collect(Collectors.toList());
if (sharedLibNames.isEmpty()) {
throw new IllegalStateException();
}
try {
NativeLibraryLoader.loadFirstAvailable(cl, sharedLibNames.toArray(new String[0]));
} catch (IllegalArgumentException | UnsatisfiedLinkError e1) {
try {
NativeLibraryLoader.load(staticLibName, cl);
logger.debug("Failed to load {}", String.join(", ", sharedLibNames), e1);
} catch (UnsatisfiedLinkError e2) {
if (e2.getMessage().contains("libc++.so.1: cannot open shared")) {
throw new UnsupportedNativeLibraryException("Install \"libc++\" to use TDLight Java!");
} else {
e1.addSuppressed(e2);
throw new UnsupportedNativeLibraryException(e1);
}
}
}
}
private static Stream<String> getNormalizedArchitectures() {
String os = getOs();
String arch = getCpuArch();
if (os.equals("unknown") || arch.equals("unknown")) {
return getAllNormalizedArchitectures();
}
return getNormalizedArchitectures(os, arch);
}
private static Stream<String> getAllNormalizedArchitectures() {
Set<String> all = new LinkedHashSet<>();
for (String os : new String[]{"windows"}) {
for (String arch : new String[]{"arm64", "amd64", "armhf", "i386", "s390x", "ppc64le"}) {
getNormalizedArchitectures(os, arch).forEach(all::add);
}
}
return all.stream();
}
private static Stream<String> getNormalizedArchitectures(String os, String arch) {
switch (os) {
case "linux": {
return Stream.of("linux_" + arch + "_ssl1", "linux_" + arch + "_ssl3");
}
case "windows": {
return Stream.of("windows_" + arch);
}
case "macos": {
return Stream.of("macos_" + arch);
}
default: {
throw new UnsupportedOperationException();
}
}
}
private static String getCpuArch() {
String architecture = System.getProperty("os.arch").trim();
switch (architecture) {
case "amd64":
case "x86_64":
return "amd64";
case "i386":
case "x86":
case "386":
case "i686":
case "686":
return "i386";
case "armv6":
case "arm":
case "armhf":
case "aarch32":
case "armv7":
case "armv7l":
return "armhf";
case "arm64":
case "aarch64":
case "armv8":
case "armv8l":
return "arm64";
case "s390x":
return "s390x";
case "powerpc":
case "powerpc64":
case "powerpc64le":
case "powerpc64el":
case "ppc":
case "ppc64":
case "ppc64le":
case "ppc64el":
if (ByteOrder
.nativeOrder()
.equals(ByteOrder.LITTLE_ENDIAN)) // Java always returns ppc64 for all 64-bit powerpc but
{
return "ppc64le"; // powerpc64le (our target) is very different, it uses this condition to accurately identify the architecture
} else {
return "unknown";
}
default:
return "unknown";
}
}
public static String getOs() {
String os = System.getProperty("os.name").toLowerCase().trim();
if (os.contains("linux")) {
return "linux";
}
if (os.contains("windows")) {
return "windows";
}
if (os.contains("mac")) {
return "macos";
}
if (os.contains("darwin")) {
return "macos";
}
return "unknown";
}
}