io.qt.internal.LibraryUtility Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of qtjambi Show documentation
Show all versions of qtjambi Show documentation
QtJambi base module containing QtCore, QtGui and QtWidgets.
/****************************************************************************
**
** Copyright (C) 2009-2024 Dr. Peter Droste, Omix Visualization GmbH & Co. KG. All rights reserved.
**
** This file is part of Qt Jambi.
**
** $BEGIN_LICENSE$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
** $END_LICENSE$
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
****************************************************************************/
package io.qt.internal;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.jar.JarOutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import io.qt.NativeAccess;
import io.qt.QLibraryNotFoundError;
import io.qt.QLibraryNotLoadedError;
import io.qt.QNoSuchSlotException;
import io.qt.QtUninvokable;
import io.qt.QtUtilities;
import io.qt.QtUtilities.LibraryRequirementMode;
import io.qt.core.QDeclarableSignals;
import io.qt.core.QPair;
import io.qt.internal.LibraryBundle.Library;
import io.qt.internal.LibraryBundle.SpecificationException;
import io.qt.internal.LibraryBundle.Symlink;
import io.qt.internal.LibraryBundle.WrongBuildException;
import io.qt.internal.LibraryBundle.WrongConfigurationException;
/**
* @hidden
*/
final class LibraryUtility {
enum Architecture{
x86,
x86_64,
arm,
arm64,
sparc,
mips,
mips64;
static Architecture decideArchitecture() {
String arch = System.getProperty("os.arch").toLowerCase();
switch(arch) {
case "arm":
case "armv":
case "armv6":
case "armv7":
case "arm32":
return arm;
case "arm64":
case "aarch64":
return arm64;
case "x86_64":
case "x64":
case "amd64":
return x86_64;
case "i386":
case "x86":
return x86;
case "sparc":
return sparc;
case "mips":
return mips;
case "mips64":
return mips64;
default:
if(arch.startsWith("arm-"))
return arm;
else if(arch.startsWith("aarch64-"))
return arm64;
else
return x86;
}
}
}
enum OperatingSystem {
Windows,
MacOS,
Linux,
Android,
IOS,
FreeBSD,
NetBSD,
OpenBSD,
Solaris
;
final boolean isAndroid() {
return this==Android;
}
final boolean isUnixLike() {
switch (this) {
case FreeBSD:
case NetBSD:
case OpenBSD:
case Solaris:
case Linux:
return true;
default:
return false;
}
}
static OperatingSystem decideOperatingSystem() {
String osName = System.getProperty("os.name").toLowerCase();
if (osName.startsWith("windows")) return Windows;
else if (osName.startsWith("mac")) return MacOS;
else if (osName.startsWith("ios")) return IOS;
else if (osName.startsWith("freebsd")) return FreeBSD;
else if (osName.startsWith("netbsd")) return NetBSD;
else if (osName.startsWith("openbsd")) return OpenBSD;
else if (osName.startsWith("solaris") || osName.startsWith("sunos")) return Solaris;
else if (osName.startsWith("linux")) {
if(System.getProperty("java.runtime.name").toLowerCase().startsWith("android"))
return OperatingSystem.Android;
else
return OperatingSystem.Linux;
}
return OperatingSystem.Linux;
}
}
private LibraryUtility() { throw new RuntimeException();}
private static final String DEPLOY_XML = "!/META-INF/qtjambi-deployment.xml";
public static final String ICU_VERSION;
public static final String LIBINFIX;
private static final List systemLibrariesList;
private static final List jniLibdirBeforeList;
private static final List jniLibdirList;
private static boolean isMinGWBuilt = false;
static final OperatingSystem operatingSystem = OperatingSystem.decideOperatingSystem();
static final Architecture architecture = Architecture.decideArchitecture();
static final String osArchName;
private static LibraryBundle.Configuration configuration = null;
private static String qtJambiLibraryPath = null;
private static String qtLibraryPath = null;
private static Boolean dontUseQtJambiFrameworks = operatingSystem==OperatingSystem.MacOS ? null : Boolean.FALSE;
private static Boolean dontUseQtFrameworks = operatingSystem==OperatingSystem.MacOS ? null : Boolean.FALSE;
private static boolean dontSearchDeploymentSpec = false;
private static final boolean useStaticLibs = operatingSystem==OperatingSystem.IOS
|| Boolean.getBoolean("io.qt.staticlibs");
private static final boolean debugInfoDeployment = Boolean.getBoolean("io.qt.provide-debuginfo");
private static final boolean noNativeDeployment = Boolean.getBoolean("io.qt.no-native-deployment")
|| Boolean.getBoolean("qtjambi.no-native-deployment")
|| Boolean.getBoolean("io.qt.no-deployment-spec")
|| Boolean.getBoolean("qtjambi.no-deployment-spec")
|| operatingSystem==OperatingSystem.Android
|| useStaticLibs;
private static final Set loadedNativeDeploymentUrls = new HashSet<>();
private static final List nativeDeployments = new ArrayList<>();
private static final File jambiDeploymentDir;
private static final File jambiSourcesDir;
private static final File jambiHeadersDir;
private static final File jambiJarDir;
private static final boolean isMavenRepo, isGradleCache, isNativeSubdir, isDebuginfoSubdir;
private static final List pluginLibraries = Collections.synchronizedList(new ArrayList<>());
private static final boolean deleteTmpDeployment;
private static final Map> javaLibraryPaths = Collections.synchronizedMap(new TreeMap<>());
private static final Map> ldLibraryPaths = Collections.synchronizedMap(new TreeMap<>());
private static final Map> javaLibraryPathOverrides = Collections.synchronizedMap(new TreeMap<>());
private static final AtomicBoolean isQmlLoaded = new AtomicBoolean(false);
private static class LibVersion{
LibVersion(int qtMajorVersion, int qtMinorVersion, int qtJambiPatch) {
this.qtMajorVersion = qtMajorVersion;
this.qtMinorVersion = qtMinorVersion;
this.qtJambiPatch = qtJambiPatch;
}
final int qtMajorVersion;
final int qtMinorVersion;
final int qtJambiPatch;
}
private static final Map,LibVersion> qtJambiVersionsByClass = Collections.synchronizedMap(new HashMap<>());
private static final Set loadedLibraries = Collections.synchronizedSet(new TreeSet<>());
private static BiConsumer, String> libraryLoader;
static {
libraryLoader = (callerClass, lib) -> {
synchronized(LibraryUtility.class) {
@SuppressWarnings({ "rawtypes", "unchecked" })
QDeclarableSignals.Signal2, String> loadLibrary = new QDeclarableSignals.Signal2(Class.class, String.class);
try {
if(operatingSystem==OperatingSystem.Android)
loadLibrary.connect(Runtime.getRuntime(), "loadLibrary0(Class, String)");
else
loadLibrary.connect(Runtime.getRuntime(), "load0(Class, String)");
libraryLoader = loadLibrary::emit;
} catch (QNoSuchSlotException e) {
libraryLoader = (_callerClass, _lib)->Runtime.getRuntime().load(_lib);
}
}
libraryLoader.accept(callerClass, lib);
};
String icuVersion = null;
String libInfix = null;
List systemLibraries = null;
List jniLibdirsPrepended = null;
List jniLibdirs = null;
try {
libInfix = QtJambi_LibraryUtilities.properties.getProperty("qtjambi.libinfix");
if(QtJambi_LibraryUtilities.qtMajorVersion>6 || (Integer.compare(QtJambi_LibraryUtilities.qtMajorVersion, 6)==0 && QtJambi_LibraryUtilities.qtMinorVersion>=7)) {
icuVersion = QtJambi_LibraryUtilities.properties.getProperty("qtjambi.icu.version", "73.2");
}else {
icuVersion = QtJambi_LibraryUtilities.properties.getProperty("qtjambi.icu.version", "56.1");
}
SortedMap tmpSystemLibrariesMap = new TreeMap();
SortedMap tmpJniLibdirBeforeMap = new TreeMap();
SortedMap tmpJniLibdirMap = new TreeMap();
Enumeration extends Object> e = QtJambi_LibraryUtilities.properties.propertyNames();
while (e.hasMoreElements()) {
String key = (String) e.nextElement();
String value = QtJambi_LibraryUtilities.properties.getProperty(key);
if (key.equals("qtjambi.system.libraries") || key.startsWith("qtjambi.system.libraries.")) {
tmpSystemLibrariesMap.put(key, value);
} else if(key.equals("qtjambi.jni.libdir.before") || key.startsWith("qtjambi.jni.libdir.before.")) {
tmpJniLibdirBeforeMap.put(key, value);
} else if(key.equals("qtjambi.jni.libdir") || key.startsWith("qtjambi.jni.libdir.")) {
tmpJniLibdirMap.put(key, value);
}
}
// Map will automatically sort the lists { "", ".0", ".01", ".1", ".10", ".2", ".A", ".a" }
systemLibraries = new ArrayList();
for (String v : tmpSystemLibrariesMap.values())
systemLibraries.add(v);
if (systemLibraries.size() > 0)
systemLibraries = resolveSystemLibraries(systemLibraries);
else
systemLibraries = null;
jniLibdirsPrepended = new ArrayList();
for (String v : tmpJniLibdirBeforeMap.values())
jniLibdirsPrepended.add(v);
if (jniLibdirsPrepended.size() > 0)
jniLibdirsPrepended = Collections.unmodifiableList(jniLibdirsPrepended);
else
jniLibdirsPrepended = null;
jniLibdirs = new ArrayList();
for (String v : tmpJniLibdirMap.values())
jniLibdirs.add(v);
if (jniLibdirs.size() > 0)
jniLibdirs = Collections.unmodifiableList(jniLibdirs);
else
jniLibdirs = null;
} catch(Throwable e) {
java.util.logging.Logger.getLogger("io.qt.internal").log(java.util.logging.Level.SEVERE, "", e);
}finally {
ICU_VERSION = icuVersion;
systemLibrariesList = systemLibraries!=null ? Collections.unmodifiableList(systemLibraries) : Collections.emptyList();
jniLibdirBeforeList = jniLibdirsPrepended!=null ? Collections.unmodifiableList(jniLibdirsPrepended) : Collections.emptyList();
jniLibdirList = jniLibdirs!=null ? Collections.unmodifiableList(jniLibdirs) : Collections.emptyList();
LIBINFIX = libInfix;
}
switch (operatingSystem) {
case MacOS:
case IOS:
osArchName = operatingSystem.name().toLowerCase();
break;
default:
switch(architecture) {
case x86_64:
osArchName = operatingSystem.name().toLowerCase()+"-x64"; break;
default:
osArchName = operatingSystem.name().toLowerCase()+"-"+architecture.name().toLowerCase(); break;
}
break;
}
if(operatingSystem!=OperatingSystem.Android) {
try{
Preferences preferences = Preferences.userNodeForPackage(LibraryUtility.class);
Preferences pids = preferences.node("qtjambi.pids");
for(String pid : pids.keys()) {
if(!RetroHelper.isProcessAlive(pid)) {
String dir = pids.get(pid, "");
File jambiTempDir = new File(dir);
if(jambiTempDir.exists() && jambiTempDir.isDirectory()) {
clearAndDelete(jambiTempDir);
}
pids.remove(pid);
}
}
pids.sync();
if(pids.keys().length==0) {
preferences.remove("qtjambi.pids");
preferences.sync();
}
String dirs = preferences.get("qtjambi.previous.deployment.dir", null);
if(dirs!=null && !dirs.isEmpty()) {
preferences.remove("qtjambi.previous.deployment.dir");
for(String dir : dirs.split("\\"+File.pathSeparator)) {
File jambiTempDir = new File(dir);
if(jambiTempDir.exists() && jambiTempDir.isDirectory())
clearAndDelete(jambiTempDir);
}
}
}catch(Throwable t) {}
}
try {
boolean loadQtJambiFromLibraryPath = noNativeDeployment;
boolean loadQtFromLibraryPath = loadQtJambiFromLibraryPath;
File jambiLibraryDir = null;
if(!loadQtJambiFromLibraryPath){
List libraryPaths = new ArrayList<>();
if (System.getProperties().containsKey("io.qt.library-path-override")) {
libraryPaths.addAll(javaLibraryPathOverrides.computeIfAbsent(System.getProperty("io.qt.library-path-override"), LibraryUtility::splitPath));
} else {
libraryPaths.addAll(javaLibraryPaths.computeIfAbsent(System.getProperty("java.library.path"), LibraryUtility::splitPath));
switch(operatingSystem) {
case IOS:
case MacOS:
String ldPath = System.getenv("DYLD_FRAMEWORK_PATH");
if(ldPath!=null)
libraryPaths.addAll(ldLibraryPaths.computeIfAbsent(ldPath, LibraryUtility::splitPath));
ldPath = System.getenv("DYLD_LIBRARY_PATH");
if(ldPath!=null)
libraryPaths.addAll(ldLibraryPaths.computeIfAbsent(ldPath, LibraryUtility::splitPath));
break;
default:
if(operatingSystem.isUnixLike()) {
ldPath = System.getenv("LD_LIBRARY_PATH");
if(ldPath!=null)
libraryPaths.addAll(ldLibraryPaths.computeIfAbsent(ldPath, LibraryUtility::splitPath));
break;
}
break;
}
}
libraryPaths = mergeJniLibdir(libraryPaths);
List replacements = new ArrayList<>();
configuration = decideConfiguration();
if(configuration!=null) {
String libFormat = qtjambiLibraryName(null, "QtJambi", false, configuration, replacements, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion, QtJambi_LibraryUtilities.qtJambiPatch);
String libFormatNoFw = null;
if(operatingSystem==OperatingSystem.MacOS) {
if(dontUseQtJambiFrameworks==null) {
libFormatNoFw = qtjambiLibraryName(null, "QtJambi", true, configuration, replacements, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion, QtJambi_LibraryUtilities.qtJambiPatch);
}else if(dontUseQtJambiFrameworks) {
libFormat = null;
libFormatNoFw = qtjambiLibraryName(null, "QtJambi", true, configuration, replacements, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion, QtJambi_LibraryUtilities.qtJambiPatch);
}
}
loop1: for (String path : libraryPaths) {
Iterator iter = replacements.iterator();
do {
String lib = null;
String libNoFw = null;
if(!iter.hasNext()) {
lib = libFormat;
libNoFw = libFormatNoFw;
}else {
if(libFormat!=null)
lib = String.format(libFormat, iter.next());
if(libFormatNoFw!=null)
libNoFw = String.format(libFormatNoFw, iter.next());
}
if(lib!=null) {
File f = new File(path, lib);
if (f.exists()) {
loadQtJambiFromLibraryPath = true;
jambiLibraryDir = f.getParentFile();
if(operatingSystem==OperatingSystem.MacOS) {
jambiLibraryDir = jambiLibraryDir.getParentFile().getParentFile();
}
dontUseQtJambiFrameworks = false;
break loop1;
}
}
if(libNoFw!=null) {
File f = new File(path, libNoFw);
if (f.exists()) {
loadQtJambiFromLibraryPath = true;
jambiLibraryDir = f.getParentFile();
dontUseQtJambiFrameworks = true;
break loop1;
}
}
}while(iter.hasNext());
}
}else {
replacements.clear();
String libFormat = qtjambiLibraryName(null, "QtJambi", false, LibraryBundle.Configuration.Release, replacements, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion, QtJambi_LibraryUtilities.qtJambiPatch);
String libFormatNoFw = null;
if(operatingSystem==OperatingSystem.MacOS) {
if(dontUseQtJambiFrameworks==null) {
libFormatNoFw = qtjambiLibraryName(null, "QtJambi", true, LibraryBundle.Configuration.Release, replacements, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion, QtJambi_LibraryUtilities.qtJambiPatch);
}else if(dontUseQtJambiFrameworks) {
libFormat = null;
libFormatNoFw = qtjambiLibraryName(null, "QtJambi", true, LibraryBundle.Configuration.Release, replacements, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion, QtJambi_LibraryUtilities.qtJambiPatch);
}
}
loop1: for (String path : libraryPaths) {
Iterator iter = replacements.iterator();
do {
String lib = null;
String libNoFw = null;
if(!iter.hasNext()) {
lib = libFormat;
libNoFw = libFormatNoFw;
}else {
if(libFormat!=null)
lib = String.format(libFormat, iter.next());
if(libFormatNoFw!=null)
libNoFw = String.format(libFormatNoFw, iter.next());
}
if(lib!=null) {
File f = new File(path, lib);
if (f.exists()) {
loadQtJambiFromLibraryPath = true;
jambiLibraryDir = f.getParentFile();
if(operatingSystem==OperatingSystem.MacOS) {
jambiLibraryDir = jambiLibraryDir.getParentFile().getParentFile();
}
configuration = LibraryBundle.Configuration.Release;
dontUseQtJambiFrameworks = false;
break loop1;
}
}else if(libNoFw!=null) {
File f = new File(path, libNoFw);
if (f.exists()) {
loadQtJambiFromLibraryPath = true;
jambiLibraryDir = f.getParentFile();
configuration = LibraryBundle.Configuration.Release;
dontUseQtJambiFrameworks = true;
break loop1;
}
}
}while(iter.hasNext());
}
if(!loadQtJambiFromLibraryPath) {
replacements.clear();
libFormat = qtjambiLibraryName(null, "QtJambi", false, LibraryBundle.Configuration.Debug, replacements, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion, QtJambi_LibraryUtilities.qtJambiPatch);
libFormatNoFw = null;
if(operatingSystem==OperatingSystem.MacOS) {
if(dontUseQtJambiFrameworks==null) {
libFormatNoFw = qtjambiLibraryName(null, "QtJambi", true, LibraryBundle.Configuration.Debug, replacements, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion, QtJambi_LibraryUtilities.qtJambiPatch);
}else if(dontUseQtJambiFrameworks) {
libFormat = null;
libFormatNoFw = qtjambiLibraryName(null, "QtJambi", true, LibraryBundle.Configuration.Debug, replacements, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion, QtJambi_LibraryUtilities.qtJambiPatch);
}
}
loop1: for (String path : libraryPaths) {
Iterator iter = replacements.iterator();
do {
String lib = null;
String libNoFw = null;
if(!iter.hasNext()) {
lib = libFormat;
libNoFw = libFormatNoFw;
}else {
if(libFormat!=null)
lib = String.format(libFormat, iter.next());
if(libFormatNoFw!=null)
libNoFw = String.format(libFormatNoFw, iter.next());
}
if(lib!=null) {
File f = new File(path, lib);
if (f.exists()) {
loadQtJambiFromLibraryPath = true;
jambiLibraryDir = f.getParentFile();
if(operatingSystem==OperatingSystem.MacOS) {
jambiLibraryDir = jambiLibraryDir.getParentFile().getParentFile();
}
configuration = LibraryBundle.Configuration.Debug;
dontUseQtJambiFrameworks = false;
break loop1;
}
}
if(libNoFw!=null) {
File f = new File(path, libNoFw);
if (f.exists()) {
loadQtJambiFromLibraryPath = true;
jambiLibraryDir = f.getParentFile();
configuration = LibraryBundle.Configuration.Debug;
dontUseQtJambiFrameworks = true;
break loop1;
}
}
}while(iter.hasNext());
}
}
if(configuration==null)
configuration = LibraryBundle.Configuration.Release;
}
replacements.clear();
String libFormat = qtLibraryName("Qt", "Core", null, null, configuration, replacements, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion, -1);
loop1: for (String path : libraryPaths) {
Iterator iter = replacements.iterator();
do {
String lib;
if(!iter.hasNext()) {
lib = libFormat;
}else {
lib = String.format(libFormat, iter.next());
}
File f = new File(path, lib);
if(operatingSystem.isUnixLike()) {
// libQt5Core.so.5 could point to libQt5Core.so.5.x with x < qtMinorVersion
if(f.exists() && (
lib.endsWith(".so."+QtJambi_LibraryUtilities.qtMajorVersion)
|| lib.endsWith(".so")
) && Files.isSymbolicLink(f.toPath())) {
try {
File link = f.toPath().toRealPath().toFile();
if(lib.endsWith(".so")) {
if(!link.getName().startsWith(lib+"."+QtJambi_LibraryUtilities.qtMajorVersion+"."+QtJambi_LibraryUtilities.qtMinorVersion)) {
f = null;
continue;
}
}else {
if(!link.getName().startsWith(lib+"."+QtJambi_LibraryUtilities.qtMinorVersion)) {
f = null;
continue;
}
}
} catch (IOException e) {
}
}
}
if (f!=null && f.exists()) {
loadQtFromLibraryPath = true;
break loop1;
}
}while(iter.hasNext());
}
}
if(!loadQtJambiFromLibraryPath || !loadQtFromLibraryPath){
File tmpDir = new File(System.getProperty("java.io.tmpdir"));
String deploymentdir = System.getProperty("io.qt.deploymentdir", "");
boolean keepDeployment = Boolean.getBoolean("io.qt.keep-temp-deployment");
String dirprefix = "v";
if(deploymentdir!=null
&& !deploymentdir.isEmpty()
&& !"tmp".equalsIgnoreCase(deploymentdir)
&& !"temp".equalsIgnoreCase(deploymentdir)) {
keepDeployment = true;
if("user".equalsIgnoreCase(deploymentdir)) {
switch (operatingSystem) {
case Windows:
tmpDir = new File(System.getProperty("user.home"), "AppData\\Local\\QtJambi");
break;
case MacOS:
tmpDir = new File(System.getProperty("user.home"), "Library/Application Support/QtJambi");
break;
default:
if(operatingSystem.isUnixLike())
tmpDir = new File(System.getProperty("user.home"), ".local/share/QtJambi");
break;
}
}else if("common".equalsIgnoreCase(deploymentdir)) {
switch (operatingSystem) {
case Windows:
tmpDir = new File("C:\\ProgramData\\QtJambi");
break;
case MacOS:
tmpDir = new File("/Library/Application Support/QtJambi");
break;
default:
if(operatingSystem.isUnixLike())
tmpDir = new File("/usr/local/share/QtJambi");
break;
}
}else {
File deploymentDir = new File(deploymentdir);
if(!deploymentDir.isAbsolute()) {
deploymentDir = new File(System.getProperty("user.home"), deploymentdir);
}
if(!deploymentDir.getName().toLowerCase().startsWith("qtjambi"))
dirprefix = "QtJambi";
tmpDir = deploymentDir;
}
jambiDeploymentDir = new File(tmpDir, dirprefix + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch);
}else {
if(keepDeployment) {
switch (operatingSystem) {
default:
if(operatingSystem.isUnixLike()) {
if(!tmpDir.getAbsolutePath().startsWith(System.getProperty("user.home"))) {
jambiDeploymentDir = new File(tmpDir, "QtJambi" + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch + "_" + System.getProperty("user.name"));
break;
}
}
jambiDeploymentDir = new File(tmpDir, "QtJambi" + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch);
break;
}
}else {
if(RetroHelper.processName()!=null && !RetroHelper.processName().isEmpty()){
jambiDeploymentDir = new File(tmpDir, "QtJambi" + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch + "_" + RetroHelper.processName());
try{
Preferences preferences = Preferences.userNodeForPackage(LibraryUtility.class);
Preferences pids = preferences.node("qtjambi.pids");
pids.put(RetroHelper.processName(), jambiDeploymentDir.getAbsolutePath());
}catch(Throwable t) {}
}else {
jambiDeploymentDir = new File(tmpDir, "QtJambi" + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch + "_" + System.getProperty("user.name"));
}
}
}
deleteTmpDeployment = !keepDeployment;
if(deleteTmpDeployment)
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->"Requires deletion of tmp deployment directory "+jambiDeploymentDir.getAbsolutePath()+" during program termination.");
if(Boolean.getBoolean("io.qt.provide-headers")) {
tmpDir = new File(System.getProperty("java.io.tmpdir"));
String headersdir = System.getProperty("io.qt.headersdir", "");
dirprefix = "v";
if(headersdir!=null
&& !headersdir.isEmpty()
&& !"tmp".equalsIgnoreCase(headersdir)
&& !"temp".equalsIgnoreCase(headersdir)) {
if("user".equalsIgnoreCase(headersdir)) {
switch (operatingSystem) {
case Windows:
tmpDir = new File(System.getProperty("user.home"), "AppData\\Local\\QtJambi");
break;
case MacOS:
tmpDir = new File(System.getProperty("user.home"), "Library/Application Support/QtJambi");
break;
default:
if(operatingSystem.isUnixLike())
tmpDir = new File(System.getProperty("user.home"), ".local/share/QtJambi");
break;
}
}else if("common".equalsIgnoreCase(headersdir)) {
switch (operatingSystem) {
case Windows:
tmpDir = new File("C:\\ProgramData\\QtJambi");
break;
case MacOS:
tmpDir = new File("/Library/Application Support/QtJambi");
break;
default:
if(operatingSystem.isUnixLike())
tmpDir = new File("/usr/local/share/QtJambi");
break;
}
}else {
File deploymentDir = new File(headersdir);
if(!deploymentDir.isAbsolute()) {
deploymentDir = new File(System.getProperty("user.home"), headersdir);
}
if(!deploymentDir.getName().toLowerCase().startsWith("qtjambi"))
dirprefix = "QtJambi";
tmpDir = deploymentDir;
}
jambiHeadersDir = new File(tmpDir, dirprefix + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch);
}else if("tmp".equalsIgnoreCase(headersdir)
|| "temp".equalsIgnoreCase(headersdir)){
if(deleteTmpDeployment) {
switch (operatingSystem) {
default:
if(operatingSystem.isUnixLike()) {
if(!tmpDir.getAbsolutePath().startsWith(System.getProperty("user.home"))) {
jambiHeadersDir = new File(tmpDir, "QtJambi" + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch + "_" + System.getProperty("user.name"));
break;
}
}
jambiHeadersDir = new File(tmpDir, "QtJambi" + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch);
break;
}
}else {
if(RetroHelper.processName()!=null && !RetroHelper.processName().isEmpty()){
jambiHeadersDir = new File(tmpDir, "QtJambi" + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch + "_" + RetroHelper.processName());
try{
Preferences preferences = Preferences.userNodeForPackage(LibraryUtility.class);
Preferences pids = preferences.node("qtjambi.pids");
pids.put(RetroHelper.processName(), jambiDeploymentDir.getAbsolutePath());
}catch(Throwable t) {}
}else {
jambiHeadersDir = new File(tmpDir, "QtJambi" + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch + "_" + System.getProperty("user.name"));
}
}
}else {
jambiHeadersDir = null;
}
}else {
jambiHeadersDir = null;
}
if(debugInfoDeployment) {
tmpDir = new File(System.getProperty("java.io.tmpdir"));
String sourcesdir = System.getProperty("io.qt.sourcesdir", "");
dirprefix = "v";
if(sourcesdir!=null
&& !sourcesdir.isEmpty()
&& !"tmp".equalsIgnoreCase(sourcesdir)
&& !"temp".equalsIgnoreCase(sourcesdir)) {
if("user".equalsIgnoreCase(sourcesdir)) {
switch (operatingSystem) {
case Windows:
tmpDir = new File(System.getProperty("user.home"), "AppData\\Local\\QtJambi");
break;
case MacOS:
tmpDir = new File(System.getProperty("user.home"), "Library/Application Support/QtJambi");
break;
default:
if(operatingSystem.isUnixLike())
tmpDir = new File(System.getProperty("user.home"), ".local/share/QtJambi");
break;
}
}else if("common".equalsIgnoreCase(sourcesdir)) {
switch (operatingSystem) {
case Windows:
tmpDir = new File("C:\\ProgramData\\QtJambi");
break;
case MacOS:
tmpDir = new File("/Library/Application Support/QtJambi");
break;
default:
if(operatingSystem.isUnixLike())
tmpDir = new File("/usr/local/share/QtJambi");
break;
}
}else {
File deploymentDir = new File(sourcesdir);
if(!deploymentDir.isAbsolute()) {
deploymentDir = new File(System.getProperty("user.home"), sourcesdir);
}
if(!deploymentDir.getName().toLowerCase().startsWith("qtjambi"))
dirprefix = "QtJambi";
tmpDir = deploymentDir;
}
jambiSourcesDir = new File(tmpDir, dirprefix + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch);
}else if("tmp".equalsIgnoreCase(sourcesdir)
|| "temp".equalsIgnoreCase(sourcesdir)){
if(deleteTmpDeployment) {
switch (operatingSystem) {
default:
if(operatingSystem.isUnixLike()) {
if(!tmpDir.getAbsolutePath().startsWith(System.getProperty("user.home"))) {
jambiSourcesDir = new File(tmpDir, "QtJambi" + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch + "_" + System.getProperty("user.name"));
break;
}
}
jambiSourcesDir = new File(tmpDir, "QtJambi" + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch);
break;
}
}else {
if(RetroHelper.processName()!=null && !RetroHelper.processName().isEmpty()){
jambiSourcesDir = new File(tmpDir, "QtJambi" + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch + "_" + RetroHelper.processName());
try{
Preferences preferences = Preferences.userNodeForPackage(LibraryUtility.class);
Preferences pids = preferences.node("qtjambi.pids");
pids.put(RetroHelper.processName(), jambiDeploymentDir.getAbsolutePath());
}catch(Throwable t) {}
}else {
jambiSourcesDir = new File(tmpDir, "QtJambi" + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch + "_" + System.getProperty("user.name"));
}
}
}else {
jambiSourcesDir = null;
}
}else {
jambiSourcesDir = null;
}
boolean maybeMavenRepo = false;
boolean maybeGradleCache = false;
{
File _jambiJarDir = null;
URL _classURL = LibraryUtility.class.getResource(LibraryUtility.class.getSimpleName()+".class");
if(_classURL!=null) {
int index;
String classURL = _classURL.toString();
File jarFile = null;
if(classURL.startsWith("jar:file:") && (index = classURL.indexOf("!/"))>0) {
String jarFileURL = classURL.substring(4, index);
try {
jarFile = new File(CoreUtility.createURL(jarFileURL).toURI());
} catch (URISyntaxException | MalformedURLException e) {
}
}else {
try {
URLConnection connection = _classURL.openConnection();
if(connection instanceof JarURLConnection) {
jarFile = new File(((JarURLConnection) connection).getJarFile().getName());
}
} catch (Throwable e) {
}
}
if(jarFile!=null && jarFile.exists()) {
_jambiJarDir = jarFile.getParentFile();
try {
if(_jambiJarDir.getName().equals(QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch)
&& _jambiJarDir.getParentFile().getName().equals("qtjambi")) {
maybeMavenRepo = true;
}
} catch (Throwable e) {
}
if(!maybeMavenRepo) {
try {
if(_jambiJarDir.getParentFile().getName().equals(QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + "." + QtJambi_LibraryUtilities.qtJambiPatch)
&& _jambiJarDir.getParentFile().getParentFile().getName().equals("qtjambi")) {
maybeGradleCache = true;
}
} catch (Throwable e) {
}
}
}
}
jambiJarDir = _jambiJarDir;
}
ClassLoader loader = classLoader();
Enumeration specsFound = Collections.emptyEnumeration();
try {
specsFound = loader.getResources("META-INF/qtjambi-deployment.xml");
} catch (IOException e) {
Logger.getLogger("io.qt.internal").log(Level.WARNING, "", e);
}
dontSearchDeploymentSpec = specsFound.hasMoreElements();
Map debuginfosByURL = Collections.emptyMap();
if(dontSearchDeploymentSpec && !"debug".equals(System.getProperty("io.qt.debug")) && debugInfoDeployment) {
debuginfosByURL = new HashMap<>();
List foundURLs = new ArrayList<>();
Map> urlsByFileName = new TreeMap<>();
while (specsFound.hasMoreElements()) {
URL _url = specsFound.nextElement();
String url = _url.toString();
if(url.startsWith("jar:file:") && url.endsWith(DEPLOY_XML)) {
url = url.substring(4, url.length()-DEPLOY_XML.length());
File jar = new File(url);
Function> pairFactory = n->new QPair<>(null, null);
if(jar.getName().contains("-native-")) {
QPair pair = urlsByFileName.computeIfAbsent(jar.getName(), pairFactory);
pair.first = _url;
foundURLs.add(_url);
}else if(jar.getName().contains("-debuginfo-")) {
QPair pair = urlsByFileName.computeIfAbsent(jar.getName().replace("-debuginfo-", "-native-"), pairFactory);
pair.second = _url;
}
}
}
for(QPair pair : urlsByFileName.values()) {
if(pair.first!=null) {
if(pair.second!=null) {
debuginfosByURL.put(pair.first, pair.second);
}else {
String url = pair.first.toString();
url = url.substring(4, url.length()-DEPLOY_XML.length());
File jar = new File(url);
File debuginfo = new File(jar.getParentFile(), jar.getName().replace("-native-", "-debuginfo-"));
if(!debuginfo.exists()) {
debuginfo = new File(jar.getParent().replace("-native-", "-debuginfo-"), jar.getName().replace("-native-", "-debuginfo-"));
}
if(!debuginfo.exists()) {
debuginfo = new File(jar.getParent().replace(File.separator+"native"+File.separator, File.separator+"debuginfo"+File.separator), jar.getName().replace("-native-", "-debuginfo-"));
}
if(debuginfo.exists()) {
debuginfosByURL.put(pair.first, CoreUtility.createURL("jar:"+debuginfo.toURI()+DEPLOY_XML));
}
}
}
}
specsFound = Collections.enumeration(foundURLs);
}
if(!dontSearchDeploymentSpec && jambiJarDir!=null) {
boolean _isNativeSubdir = false;
boolean _isDebuginfoSubdir = false;
boolean _isMavenRepo = false;
boolean _isGradleCache = false;
List foundURLs = new ArrayList<>();
boolean isDebug = false;
debuginfosByURL = new HashMap<>();
if(!loadQtJambiFromLibraryPath) {
String version = String.format("%1$s.%2$s.%3$s", QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion, QtJambi_LibraryUtilities.qtJambiPatch);
String nativeModule = String.format("qtjambi-native-%1$s", osArchName);
String debuginfoModule = String.format("qtjambi-debuginfo-%1$s", osArchName);
if("debug".equals(System.getProperty("io.qt.debug"))) {
File nativeFile = null;
if(maybeMavenRepo) {
try {
File jarDir = new File(new File(jambiJarDir.getParentFile().getParentFile(), String.format("%1$s-debug", nativeModule)), version);
nativeFile = new File(jarDir, String.format("%1$s-debug-%2$s.jar", nativeModule, version));
_isMavenRepo = nativeFile.exists();
} catch (Throwable e) {}
}
if(maybeGradleCache) {
try {
File jarDir = new File(new File(jambiJarDir.getParentFile().getParentFile().getParentFile(), String.format("%1$s-debug", nativeModule)), version);
for(File subdir : jarDir.listFiles()) {
if(subdir.isDirectory()) {
nativeFile = new File(subdir, String.format("%1$s-debug-%2$s.jar", nativeModule, version));
if(nativeFile.exists())
break;
}
}
_isGradleCache = nativeFile.exists();
} catch (Throwable e) {}
}
if(nativeFile==null || !nativeFile.exists()) {
nativeFile = new File(jambiJarDir, String.format("%1$s-debug-%2$s.jar", nativeModule, version));
if(!nativeFile.exists()) {
nativeFile = new File(new File(jambiJarDir, "native"), String.format("%1$s-debug-%2$s.jar", nativeModule, version));
_isNativeSubdir = nativeFile.exists();
}
}
if(nativeFile.exists()) {
isDebug = true;
try {
foundURLs.add(CoreUtility.createURL("jar:"+nativeFile.toURI()+DEPLOY_XML));
} catch (IOException e) {
}
}
}else {
File nativeFile = null;
if(maybeMavenRepo) {
try {
File jarDir = new File(new File(jambiJarDir.getParentFile().getParentFile(), nativeModule), version);
nativeFile = new File(jarDir, String.format("%1$s-%2$s.jar", nativeModule, version));
_isMavenRepo = nativeFile.exists();
} catch (Throwable e) {}
}
if(maybeGradleCache) {
try {
File jarDir = new File(new File(jambiJarDir.getParentFile().getParentFile().getParentFile(), nativeModule), version);
for(File subdir : jarDir.listFiles()) {
if(subdir.isDirectory()) {
nativeFile = new File(subdir, String.format("%1$s-%2$s.jar", nativeModule, version));
if(nativeFile.exists())
break;
}
}
_isGradleCache = nativeFile.exists();
} catch (Throwable e) {}
}
if(nativeFile==null || !nativeFile.exists()) {
nativeFile = new File(jambiJarDir, String.format("%1$s-%2$s.jar", nativeModule, version));
if(!nativeFile.exists()) {
nativeFile = new File(new File(jambiJarDir, "native"), String.format("%1$s-%2$s.jar", nativeModule, version));
_isNativeSubdir = nativeFile.exists();
}
}
if(nativeFile.exists()) {
try {
URL url = CoreUtility.createURL("jar:"+nativeFile.toURI()+DEPLOY_XML);
foundURLs.add(url);
if(debugInfoDeployment) {
File debuginfoFile = null;
if(_isMavenRepo) {
try {
File jarDir = new File(new File(jambiJarDir.getParentFile().getParentFile(), debuginfoModule), version);
debuginfoFile = new File(jarDir, String.format("%1$s-%2$s.jar", debuginfoModule, version));
} catch (Throwable e) {}
} else if(_isGradleCache) {
try {
File jarDir = new File(new File(jambiJarDir.getParentFile().getParentFile().getParentFile(), debuginfoModule), version);
for(File subdir : jarDir.listFiles()) {
if(subdir.isDirectory()) {
debuginfoFile = new File(subdir, String.format("%1$s-%2$s.jar", debuginfoModule, version));
if(debuginfoFile.exists())
break;
}
}
} catch (Throwable e) {}
}
if(debuginfoFile==null || !debuginfoFile.exists()) {
debuginfoFile = new File(jambiJarDir, String.format("%1$s-%2$s.jar", debuginfoModule, version));
if(!debuginfoFile.exists()) {
debuginfoFile = new File(new File(jambiJarDir, "debuginfo"), String.format("%1$s-%2$s.jar", debuginfoModule, version));
_isDebuginfoSubdir = debuginfoFile.exists();
}
}
if(debuginfoFile.exists()) {
try {
debuginfosByURL.put(url, CoreUtility.createURL("jar:"+debuginfoFile.toURI()+DEPLOY_XML));
} catch (IOException e) {
}
}
}
} catch (IOException e) {
}
}else if(!"release".equals(System.getProperty("io.qt.debug"))){
nativeFile = null;
if(maybeMavenRepo) {
try {
File jarDir = new File(new File(jambiJarDir.getParentFile().getParentFile(), String.format("%1$s-debug", nativeModule)), version);
nativeFile = new File(jarDir, String.format("%1$s-debug-%2$s.jar", nativeModule, version));
_isMavenRepo = nativeFile.exists();
} catch (Throwable e) {}
}
if(maybeGradleCache) {
try {
File jarDir = new File(new File(jambiJarDir.getParentFile().getParentFile().getParentFile(), String.format("%1$s-debug", nativeModule)), version);
for(File subdir : jarDir.listFiles()) {
if(subdir.isDirectory()) {
nativeFile = new File(subdir, String.format("%1$s-debug-%2$s.jar", nativeModule, version));
if(nativeFile.exists())
break;
}
}
_isGradleCache = nativeFile.exists();
} catch (Throwable e) {}
}
if(nativeFile==null || !nativeFile.exists()) {
nativeFile = new File(jambiJarDir, String.format("%1$s-debug-%2$s.jar", nativeModule, version));
if(!nativeFile.exists()) {
nativeFile = new File(new File(jambiJarDir, "native"), String.format("%1$s-debug-%2$s.jar", nativeModule, version));
_isNativeSubdir = nativeFile.exists();
}
}
if(nativeFile.exists()) {
isDebug = true;
try {
foundURLs.add(CoreUtility.createURL("jar:"+nativeFile.toURI()+DEPLOY_XML));
} catch (IOException e) {
}
}
}
}
final String suffix;
if(isDebug) {
suffix = String.format("-native-%1$s-debug", osArchName);
}else {
suffix = String.format("-native-%1$s", osArchName);
}
final String versionedSuffix = String.format("%1$s-%2$s.%3$s.", suffix, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion);
if(_isMavenRepo) {
try {
for(File dir : jambiJarDir.getParentFile().getParentFile().listFiles()) {
if(dir.isDirectory() && dir.getName().startsWith("qtjambi-plugin-") && dir.getName().endsWith(suffix)) {
dir = new File(dir, version);
for(File f : dir.listFiles()) {
if(f.getName().startsWith("qtjambi-plugin-") && f.getName().contains(versionedSuffix)) {
try {
URL url = CoreUtility.createURL("jar:"+f.toURI()+DEPLOY_XML);
foundURLs.add(url);
if(!isDebug && debugInfoDeployment) {
String name = f.getName().replace("-native-", "-debuginfo-");
File debuginfoFile = new File(f.getParentFile(), name);
if(debuginfoFile.exists()) {
debuginfosByURL.put(url, CoreUtility.createURL("jar:"+debuginfoFile.toURI()+DEPLOY_XML));
}
}
} catch (IOException e) {
}
}
}
}
}
} catch (Throwable e) {}
}
if(_isGradleCache) {
try {
for(File dir : jambiJarDir.getParentFile().getParentFile().getParentFile().getParentFile().listFiles()) {
if(dir.isDirectory() && dir.getName().startsWith("qtjambi-plugin-") && dir.getName().endsWith(suffix)) {
dir = new File(dir, version);
for(File subdir : dir.listFiles()) {
if(subdir.isDirectory()) {
for(File f : subdir.listFiles()) {
if(f.getName().startsWith("qtjambi-plugin-") && f.getName().contains(versionedSuffix)) {
try {
URL url = CoreUtility.createURL("jar:"+f.toURI()+DEPLOY_XML);
foundURLs.add(url);
if(!isDebug && debugInfoDeployment) {
String name = f.getName().replace("-native-", "-debuginfo-");
File debuginfoFile = new File(f.getParentFile(), name);
if(debuginfoFile.exists()) {
debuginfosByURL.put(url, CoreUtility.createURL("jar:"+debuginfoFile.toURI()+DEPLOY_XML));
}
}
} catch (IOException e) {
}
}
}
}
}
}
}
} catch (Throwable e) {}
}
try {
for(File f : (_isNativeSubdir ? new File(jambiJarDir, "native") : jambiJarDir).listFiles()) {
if(f.getName().startsWith("qtjambi-plugin-") && f.getName().contains(versionedSuffix)) {
try {
URL url = CoreUtility.createURL("jar:"+f.toURI()+DEPLOY_XML);
foundURLs.add(url);
if(!isDebug && debugInfoDeployment) {
String name = f.getName().replace("-native-", "-debuginfo-");
File debuginfoFile = new File((_isNativeSubdir ? new File(jambiJarDir, "debuginfo") : jambiJarDir), name);
if(debuginfoFile.exists()) {
debuginfosByURL.put(url, CoreUtility.createURL("jar:"+debuginfoFile.toURI()+DEPLOY_XML));
}
}
} catch (IOException e) {
}
}
}
} catch (Throwable e) {}
}
if(!loadQtFromLibraryPath) {
int qtPatchVersion = -1;
String libPrefix = String.format("qt-lib-core-native-%1$s%2$s-%3$s.%4$s.", osArchName, isDebug && operatingSystem==OperatingSystem.Windows ? "-debug" : "", QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion);
for(File lib : (_isNativeSubdir ? new File(jambiJarDir, "native") : jambiJarDir).listFiles()) {
if(lib.isFile() && lib.getName().startsWith(libPrefix) && lib.getName().endsWith(".jar")) {
String version = lib.getName().substring(libPrefix.length(), lib.getName().length()-4);
int v = Integer.parseInt(version);
if(v>qtPatchVersion)
qtPatchVersion = v;
}
}
if(qtPatchVersion>=0) {
String libSuffix = String.format("-native-%1$s%2$s-%3$s.%4$s.%5$s.jar", osArchName, isDebug && operatingSystem==OperatingSystem.Windows ? "-debug" : "", QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion, qtPatchVersion);
for(File lib : (_isNativeSubdir ? new File(jambiJarDir, "native") : jambiJarDir).listFiles()) {
if(lib.isFile() && lib.getName().startsWith("qt-") && lib.getName().endsWith(libSuffix)) {
try {
URL url = CoreUtility.createURL("jar:"+lib.toURI()+DEPLOY_XML);
foundURLs.add(url);
if(!isDebug && debugInfoDeployment) {
String name = lib.getName().replace("-native-", "-debuginfo-");
File debuginfoFile = new File((_isNativeSubdir ? new File(jambiJarDir, "debuginfo") : jambiJarDir), name);
if(debuginfoFile.exists()) {
debuginfosByURL.put(url, CoreUtility.createURL("jar:"+debuginfoFile.toURI()+DEPLOY_XML));
}
}
} catch (IOException e) {
}
}
}
}
}
if(!foundURLs.isEmpty())
specsFound = Collections.enumeration(foundURLs);
isMavenRepo = _isMavenRepo;
isGradleCache = _isGradleCache;
isNativeSubdir = _isNativeSubdir;
isDebuginfoSubdir = _isDebuginfoSubdir;
}else {
isMavenRepo = false;
isGradleCache = false;
isNativeSubdir = false;
isDebuginfoSubdir = false;
}
while (specsFound.hasMoreElements()) {
URL url = specsFound.nextElement();
if(loadedNativeDeploymentUrls.contains(url))
continue;
loadedNativeDeploymentUrls.add(url);
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->String.format("Found %1$s", url.getFile().replace(DEPLOY_XML, "")));
LibraryBundle spec = null;
String protocol = url.getProtocol();
if (protocol.equals("jar")) {
// Using toExternalForm() will convert a space character into %20 which breaks java.lang.File use of the resulting path.
String eform = url.toExternalForm();
// Try to decide the name of the .jar file to have a
// reference point for later..
int start = 4; //"jar:".length();
int end = eform.indexOf("!/", start);
// eform has the "jar:url!/entry" format
if (end != -1) {
try {
URL jarUrl = CoreUtility.createURL(eform.substring(start, end));
String jarName = new File(jarUrl.getFile()).getName();
try {
URL debuginfoURL = debuginfosByURL.get(url);
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->debuginfoURL==null ? String.format("Extracting libraries from %1$s", url.getFile().replace(DEPLOY_XML, "")) : String.format("Extracting libraries from %1$s and %2$s", url.getFile().replace(DEPLOY_XML, ""), debuginfoURL.getFile().replace(DEPLOY_XML, "")));
spec = prepareNativeDeployment(url, debuginfoURL, jarName, null);
} catch (ParserConfigurationException | SAXException | ZipException e) {
Logger.getLogger("io.qt.internal").log(Level.WARNING, String.format("Unable to load native libraries from %1$s: %2$s", (jarName==null ? jarUrl : jarName), e.getMessage()), e);
} catch (IOException e) {
}
} catch (MalformedURLException e) {
Logger.getLogger("io.qt.internal").log(Level.WARNING, "", e);
}
}
} else if (protocol.equals("file")) {
// No unpack since we presume we are already unpacked
try {
URL debuginfoURL = debuginfosByURL.get(url);
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->debuginfoURL==null ? String.format("Extracting libraries from %1$s", url.getFile().replace(DEPLOY_XML, "")) : String.format("Extracting libraries from %1$s and %2$s", url.getFile().replace(DEPLOY_XML, ""), debuginfoURL.getFile().replace(DEPLOY_XML, "")));
spec = prepareNativeDeployment(url, debuginfoURL, null, Boolean.FALSE);
} catch (ParserConfigurationException | SAXException | ZipException e) {
Logger.getLogger("io.qt.internal").log(Level.WARNING, String.format("Unable to load native libraries from %1$s: %2$s", url, e.getMessage()), e);
} catch (IOException e) {
}
}
if(spec != null) {
if("qtjambi".equals(spec.module()))
nativeDeployments.add(0, spec);
else
nativeDeployments.add(spec);
}
}
if(!nativeDeployments.isEmpty()) {
LibraryBundle qtjambiSpec = nativeDeployments.get(0);
configuration = qtjambiSpec.configuration();
switch (operatingSystem) {
case Windows:
qtJambiLibraryPath = new File(qtjambiSpec.extractionDir(), "bin").getAbsolutePath();
break;
default:
qtJambiLibraryPath = new File(qtjambiSpec.extractionDir(), "lib").getAbsolutePath();
break;
}
for(LibraryBundle spec : nativeDeployments) {
if(spec.compiler()!=null && qtjambiSpec.compiler()!=null && !spec.compiler().equals(qtjambiSpec.compiler())) {
if(operatingSystem==OperatingSystem.Windows) {
throw new WrongBuildException(String.format("Native deployments of different builts: %1$s (%2$s) and %3$s (%4$s)",
qtjambiSpec.module(), qtjambiSpec.compiler(),
spec.module(), spec.compiler()));
}
}else if(!spec.configuration().equals(qtjambiSpec.configuration())) {
if(operatingSystem==OperatingSystem.Windows || spec.module()==null || !spec.module().startsWith("qt.")) {
throw new WrongConfigurationException(String.format("Native deployments of different configurations: %1$s and %2$s", qtjambiSpec.configuration(), spec.configuration()));
}
}
}
}else if(configuration==null){
configuration = decideConfiguration();
}
}else {
if(configuration==null)
configuration = decideConfiguration();
deleteTmpDeployment = false;
dontSearchDeploymentSpec = true;
jambiDeploymentDir = jambiLibraryDir;
jambiSourcesDir = null;
jambiHeadersDir = null;
jambiJarDir = null;
isMavenRepo = false;
isGradleCache = false;
isNativeSubdir = false;
isDebuginfoSubdir = false;
}
} catch (RuntimeException | Error e) {
Logger.getLogger("io.qt.internal").log(Level.WARNING, "", e);
throw e;
} catch (Throwable e) {
Logger.getLogger("io.qt.internal").log(Level.WARNING, "", e);
throw new ExceptionInInitializerError(e);
}
}
private static void clearAndDelete(File directory) {
if(directory!=null) {
if(directory.isDirectory()) {
for(File file : directory.listFiles()) {
if(file.getName().equals(".") || file.getName().equals(".."))
continue;
if(file.isDirectory() && !Files.isSymbolicLink(file.toPath())) {
clearAndDelete(file);
}else {
file.delete();
}
}
}
directory.delete();
}
}
private static List resolveSystemLibraries(List tmpSystemLibrariesList) {
if(tmpSystemLibrariesList == null)
return null;
List resolvedList = new ArrayList();
for(String original : tmpSystemLibrariesList) {
String s = original;
if(File.separatorChar!='/') // convert "/" into "\"
s = s.replace('/', File.separatorChar);
String resolvedPath = null;
synchronized(loadedNativeDeploymentUrls) {
for(LibraryBundle deploymentSpec : nativeDeployments) {
File f = new File(deploymentSpec.extractionDir(), s);
if(f.isFile()) {
resolvedPath = s;
break;
}
File libDir = new File(deploymentSpec.extractionDir(), "lib");
f = new File(libDir, s);
if(f.isFile()) {
resolvedPath = "lib" + File.separator + s;
break;
}
File binDir = new File(deploymentSpec.extractionDir(), "bin");
f = new File(binDir, s);
if(f.isFile()) {
resolvedPath = "bin" + File.separator + s;
break;
}
}
}
if(resolvedPath != null)
resolvedList.add(resolvedPath);
else
System.err.println("IGNORED version.properties qtjambi.system.libraries entry \"" + original + "\": file could not be found");
}
return Collections.unmodifiableList(resolvedList);
}
static LibraryBundle.Configuration configuration() {
return configuration;
}
private static LibraryBundle.Configuration decideConfiguration() {
LibraryBundle.Configuration configuration = null;
String debugString = System.getProperty("io.qt.debug");
try {
if(debugString != null) {
// This was added to allow unit tests to specify Configuration (as no MANIFEST.MF is available)
if(LibraryBundle.Configuration.Release.toString().compareToIgnoreCase(debugString) == 0)
configuration = LibraryBundle.Configuration.Release;
else if(LibraryBundle.Configuration.Debug.toString().compareToIgnoreCase(debugString) == 0)
configuration = LibraryBundle.Configuration.Debug;
if(configuration == null) {
Boolean booleanValue = Boolean.valueOf(debugString);
if((booleanValue != null && booleanValue.booleanValue()) || debugString.length() == 0) {
configuration = LibraryBundle.Configuration.Debug;
java.util.logging.Logger.getLogger("io.qt.internal").log(java.util.logging.Level.WARNING, "-Dio.qt.debug=" + Boolean.TRUE + "; is set instead of =debug.");
}
}
}
} catch(Exception e) {
java.util.logging.Logger.getLogger("io.qt.internal").log(java.util.logging.Level.SEVERE, "-Dio.qt.debug=" + Boolean.FALSE + "; is assumed default", e);
// only because Configuration.Release is assumed
}
if(configuration==null)
configuration = LibraryBundle.Configuration.Release;
return configuration;
}
static File jambiDeploymentDir() {
return jambiDeploymentDir;
}
@NativeAccess
@QtUninvokable
private static void extractContainerAccessLib(String library) {
if(!noNativeDeployment && !useStaticLibs) {
String libraryPlatformName;
switch (operatingSystem) {
case Windows:
libraryPlatformName = "plugins/containeraccess/" + (configuration == LibraryBundle.Configuration.Debug && !isMinGWBuilt
? library + "d.dll" // "QtFood4.dll"
: library + ".dll");
break;
case IOS:
case MacOS:
libraryPlatformName = "plugins/containeraccess/lib" + library + ".dylib";
break;
case Android:
switch (architecture) {
case arm:
libraryPlatformName = "libplugins_containeraccess_" + library + "_armeabi-v7a.so";
break;
case arm64:
libraryPlatformName = "libplugins_containeraccess_" + library + "_arm64-v8a.so";
break;
case x86:
libraryPlatformName = "libplugins_containeraccess_" + library + "_x86.so";
break;
case x86_64:
libraryPlatformName = "libplugins_containeraccess_" + library + "_x86_64.so";
break;
default:
libraryPlatformName = "libplugins_containeraccess_" + library;
break;
}
return; // never extract on android
default:
if(operatingSystem.isUnixLike()) {
libraryPlatformName = "plugins/containeraccess/lib" + library + ".so";
break;
}
return;
}
LibraryBundle.deploy(libraryPlatformName);
}
}
@NativeAccess
@QtUninvokable
static void clear() {
synchronized(loadedNativeDeploymentUrls) {
nativeDeployments.clear();
}
if(deleteTmpDeployment) {
Preferences preferences = Preferences.userNodeForPackage(LibraryUtility.class);
if(jambiDeploymentDir.exists() && jambiDeploymentDir.isDirectory()) {
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->"Deleting tmp deployment directory "+jambiDeploymentDir.getAbsolutePath()+".");
clearAndDelete(jambiDeploymentDir);
if(jambiDeploymentDir.exists() && jambiDeploymentDir.isDirectory()) {
Logger.getLogger("io.qt.internal").log(Level.FINEST, "Preparing pending deletion...");
String dirs = preferences.get("qtjambi.previous.deployment.dir", null);
if(dirs!=null && !dirs.isEmpty()) {
preferences.put("qtjambi.previous.deployment.dir", dirs + File.pathSeparator + jambiDeploymentDir.getAbsolutePath());
}else {
preferences.put("qtjambi.previous.deployment.dir", jambiDeploymentDir.getAbsolutePath());
}
}
}else {
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->"Tmp deployment directory "+jambiDeploymentDir.getAbsolutePath()+" does not exist.");
}
try{
Preferences pids = preferences.node("qtjambi.pids");
pids.remove(RetroHelper.processName());
if(pids.keys().length==0) {
preferences.remove("qtjambi.pids");
}
}catch(Throwable t) {}
}
}
@NativeAccess
private static List pluginPaths() {
pluginLibraries.removeIf( l -> {
try {
l.extract();
}catch(Throwable t) {
java.util.logging.Logger.getLogger("io.qt.internal").log(java.util.logging.Level.SEVERE, "Error while extracting library", t);
}
if(isQmlLoaded.get()) {
try {
l.extractQml();
}catch(Throwable t) {
java.util.logging.Logger.getLogger("io.qt.internal").log(java.util.logging.Level.SEVERE, "Error while extracting library", t);
}
}
return true;
});
List paths = new ArrayList();
synchronized(loadedNativeDeploymentUrls) {
for (LibraryBundle spec : nativeDeployments) {
File root = spec.extractionDir();
if(spec.hasPluginPaths()) {
String p = new File(root, "plugins").getAbsolutePath();
if(!paths.contains(p))
paths.add(p);
}
}
}
return paths;
}
static void loadLibrary(String library) {
String libraryPlatformName;
if(useStaticLibs) {
libraryPlatformName = library;
}else {
switch (operatingSystem) {
case Windows:
libraryPlatformName = library + ".dll";
break;
case IOS:
case MacOS:
{
Availability availability = getLibraryAvailability(library, library + ".framework/" + library, Collections.emptyList());
if(availability.isAvailable()) {
libraryPlatformName = library + ".framework/" + library;
}else {
availability = getLibraryAvailability(library, "lib" + library + ".dylib", Collections.emptyList());
if(availability.isAvailable()) {
libraryPlatformName = "lib" + library + ".dylib";
}else {
libraryPlatformName = "lib" + library + ".jnilib";
}
}
}
break;
case Android:
switch (architecture) {
case arm:
libraryPlatformName = library + "_armeabi-v7a";
break;
case arm64:
libraryPlatformName = library + "_arm64-v8a";
break;
case x86:
libraryPlatformName = library + "_x86";
break;
case x86_64:
libraryPlatformName = library + "_x86_64";
break;
default:
libraryPlatformName = library;
break;
}
// libraryPlatformName = "lib" + libraryPlatformName + ".so";
break;
default:
if(operatingSystem.isUnixLike()) {
libraryPlatformName = "lib" + library + ".so";
break;
}
return;
}
}
loadNativeLibrary(Object.class, getLibraryAvailability(library, libraryPlatformName, Collections.emptyList()), null);
}
static void loadQtJambiLibrary() {
try{
loadSystemLibraries();
loadUtilityLibrary("icudata", LibraryUtility.ICU_VERSION, LibraryRequirementMode.Optional);
loadUtilityLibrary("icuuc", LibraryUtility.ICU_VERSION, LibraryRequirementMode.Optional);
loadUtilityLibrary("icui18n", LibraryUtility.ICU_VERSION, LibraryRequirementMode.Optional);
} catch (Throwable e) {
java.util.logging.Logger.getLogger("io.qt.internal").log(java.util.logging.Level.SEVERE, "Error while loading system library", e);
}
Availability availability = getLibraryAvailability("Qt", "Core", LIBINFIX, null, configuration, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion);
if(!availability.isAvailable()) {
if(configuration==LibraryBundle.Configuration.Release) {
// try to run qtjambi with debug libraries instead
configuration=LibraryBundle.Configuration.Debug;
Availability _availability = getLibraryAvailability("Qt", "Core", LIBINFIX, null, configuration, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion);
if(_availability.isAvailable())
availability = _availability;
}else {
if(dontUseQtFrameworks==null) {
dontUseQtFrameworks = Boolean.TRUE;
Availability _availability = getLibraryAvailability("Qt", "Core", LIBINFIX, null, configuration, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion);
if(!_availability.isAvailable()) {
configuration=LibraryBundle.Configuration.Debug;
_availability = getLibraryAvailability("Qt", "Core", LIBINFIX, null, configuration, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion);
if(_availability.isAvailable())
availability = _availability;
}
}else {
if(!isMinGWBuilt && operatingSystem==OperatingSystem.Windows) {
Availability stdCAvailability = getLibraryAvailability(null, "libstdc++-6", null, null, LibraryBundle.Configuration.Release, null);
if(stdCAvailability.entry!=null){
isMinGWBuilt = true;
}else if(stdCAvailability.file!=null){
isMinGWBuilt = new File(stdCAvailability.file.getParentFile(), "Qt"+QtJambi_LibraryUtilities.qtMajorVersion+"Core.dll").isFile();
}
if(isMinGWBuilt) {
availability = getLibraryAvailability("Qt", "Core", LIBINFIX, null, configuration, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion);
}
}
}
}
}
if(!availability.isAvailable() && operatingSystem.isUnixLike() && !System.getProperties().containsKey("io.qt.library-path-override")) {
String key = QtJambi_LibraryUtilities.qtMajorVersion+"."+QtJambi_LibraryUtilities.qtMinorVersion;
String qtlibpath;
Preferences preferences = Preferences.userNodeForPackage(LibraryUtility.class);
Preferences pathsPref = preferences.node("qt.library.path");
qtlibpath = pathsPref.get(key, "");
boolean setPref = false;
if(!qtlibpath.isEmpty()){
File libDir = new File(qtlibpath);
if(!libDir.exists()) {
qtlibpath = "";
pathsPref.remove(key);
}
}
if(qtlibpath.isEmpty()) {
String exeName = "qmake";
if(Integer.compare(QtJambi_LibraryUtilities.qtMajorVersion, 5)==0) {
exeName += "-qt5";
}else {
exeName += QtJambi_LibraryUtilities.qtMajorVersion;
}
try {
Process process = Runtime.getRuntime().exec(new String[]{exeName, "-query", "QT_INSTALL_LIBS"});
byte[] data;
try(InputStream input = process.getInputStream()){
process.waitFor();
data = input.readAllBytes();
}
if(data!=null) {
qtlibpath = new String(data);
setPref = true;
}
} catch (Exception e) {
}
}
if(!qtlibpath.isEmpty()){
File libDir = new File(qtlibpath);
if(libDir.exists()) {
String libPaths = System.getProperty("java.library.path");
if(libPaths==null)
libPaths = libDir.getAbsolutePath();
else
libPaths += File.pathSeparator + libDir.getAbsolutePath();
System.setProperty("java.library.path", libPaths);
if(setPref) {
pathsPref.put(key, libDir.getAbsolutePath());
try {
pathsPref.sync();
preferences.sync();
} catch (Exception e) {
}
}
}
}
}
if(jambiSourcesDir!=null) {
switch (operatingSystem) {
case MacOS:
Logger.getLogger("io.qt.internal").log(Level.INFO, ()->String.format("Call debugger command: set substitute-path ../../../../../sources/ %1$s/", new File(jambiSourcesDir, "sources").getAbsolutePath()));
break;
case Windows:
if(!isMinGWBuilt) {
Logger.getLogger("io.qt.internal").log(Level.INFO, ()->String.format("Call debugger command: srcpath %1$s", new File(jambiSourcesDir, "sources").getAbsolutePath()));
break;
}
default:
Logger.getLogger("io.qt.internal").log(Level.INFO, ()->String.format("Call debugger command: set substitute-path ../sources/ %1$s/", new File(jambiSourcesDir, "sources").getAbsolutePath()));
break;
}
}
java.io.File coreLib;
try{
coreLib = loadNativeLibrary(Object.class, availability, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion);
if(coreLib!=null)
qtLibraryPath = coreLib.getParentFile().getAbsolutePath();
} catch(UnsatisfiedLinkError t) {
LibraryUtility.analyzeUnsatisfiedLinkError(t, availability.file, true);
throw t;
}finally {
if(dontUseQtFrameworks==null) {
dontUseQtFrameworks = Boolean.FALSE;
}
}
try{
java.io.File qtjambiLib = loadQtJambiLibrary(LibraryUtility.class, null, "QtJambi", QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion, QtJambi_LibraryUtilities.qtJambiPatch);
if(LibraryUtility.operatingSystem!=OperatingSystem.Android) {
List paths = new ArrayList<>();
String path;
switch(LibraryUtility.operatingSystem) {
case MacOS:
path = QtUtilities.getenv("DYLD_LIBRARY_PATH");
String path2 = QtUtilities.getenv("DYLD_FRAMEWORK_PATH");
List paths2 = new ArrayList<>();
String dir;
if(qtjambiLib.getParentFile().getName().equals(""+QtJambi_LibraryUtilities.qtJambiPatch)
&& qtjambiLib.getParentFile().getParentFile().getName().equals("Versions")
&& qtjambiLib.getParentFile().getParentFile().getParentFile().getName().endsWith(".framework")) {
dir = qtjambiLib.getParentFile().getParentFile().getParentFile().getParentFile().getAbsolutePath();
paths2.remove(dir);
paths2.add(0, dir);
if(debugInfoDeployment) {
String sources = new File(qtjambiLib.getParentFile().getParentFile().getParentFile().getParentFile().getParentFile(), "sources").getAbsolutePath();
QtUtilities.putenv("SOURCE_PATH", sources);
QtUtilities.putenv("SRCROOT", sources);
}
}else {
dir = qtjambiLib.getParentFile().getAbsolutePath();
}
paths.remove(dir);
paths.add(0, dir);
if(coreLib.getParentFile().getName().equals(""+QtJambi_LibraryUtilities.qtJambiPatch)
&& coreLib.getParentFile().getParentFile().getName().equals("Versions")
&& coreLib.getParentFile().getParentFile().getParentFile().getName().endsWith(".framework")) {
dir = coreLib.getParentFile().getParentFile().getParentFile().getParentFile().getAbsolutePath();
paths2.remove(dir);
paths2.add(0, dir);
}else {
dir = coreLib.getParentFile().getAbsolutePath();
}
paths.remove(dir);
paths.add(0, dir);
if(path2!=null && !path2.isEmpty()) {
for(String p : path2.split("\\"+java.io.File.pathSeparator)) {
if(!paths2.contains(p))
paths2.add(0, p);
}
}
path2 = String.join(java.io.File.pathSeparator, paths2);
QtUtilities.putenv("DYLD_FRAMEWORK_PATH", path2);
if(debugInfoDeployment) {
QtUtilities.putenv("DWARF_DSYM_FOLDER_PATH", path2);
}
break;
case Windows:
path = QtUtilities.getenv("PATH");
paths.remove(qtjambiLib.getParentFile().getAbsolutePath());
paths.add(0, qtjambiLib.getParentFile().getAbsolutePath());
paths.remove(coreLib.getParentFile().getAbsolutePath());
paths.add(0, coreLib.getParentFile().getAbsolutePath());
break;
default:
path = QtUtilities.getenv("LD_LIBRARY_PATH");
paths.remove(qtjambiLib.getParentFile().getAbsolutePath());
paths.add(0, qtjambiLib.getParentFile().getAbsolutePath());
paths.remove(coreLib.getParentFile().getAbsolutePath());
paths.add(0, coreLib.getParentFile().getAbsolutePath());
break;
}
if(path!=null && !path.isEmpty()) {
for(String p : path.split("\\"+java.io.File.pathSeparator)) {
if(!paths.contains(p))
paths.add(p);
}
}
path = String.join(java.io.File.pathSeparator, paths);
switch(LibraryUtility.operatingSystem) {
case MacOS:
QtUtilities.putenv("DYLD_LIBRARY_PATH", path);
break;
case Windows:
QtUtilities.putenv("PATH", path);
// System.out.println("setting PATH="+path);
// System.out.println("testing PATH="+System.getenv("PATH"));
break;
default:
QtUtilities.putenv("LD_LIBRARY_PATH", path);
if(debugInfoDeployment) {
QtUtilities.putenv("DEBUGINFODIR", path);
String sources = new File(qtjambiLib.getParentFile().getParentFile(), "sources").getAbsolutePath();
QtUtilities.putenv("SOURCE_PATH", sources);
QtUtilities.putenv("SRCROOT", sources);
}
break;
}
}
} catch(UnsatisfiedLinkError t) {
LibraryUtility.analyzeUnsatisfiedLinkError(t, coreLib, false);
throw t;
}
}
static void loadQtJambiLibrary(Class> callerClass, String library) {
LibVersion libVersion = getLibVersion(callerClass);
try {
loadQtJambiLibrary(callerClass, "QtJambi", library, libVersion.qtMajorVersion, libVersion.qtMinorVersion, libVersion.qtJambiPatch);
} catch (RuntimeException | Error e) {
if(libVersion.qtMajorVersion!=QtJambi_LibraryUtilities.qtMajorVersion
|| libVersion.qtMinorVersion!=QtJambi_LibraryUtilities.qtMinorVersion
|| libVersion.qtJambiPatch!=QtJambi_LibraryUtilities.qtJambiPatch) {
throw new LinkageError("Cannot combine QtJambi" + library + " "
+ libVersion.qtMajorVersion + "."
+ libVersion.qtMinorVersion + "."
+ libVersion.qtJambiPatch + " with QtJambi "
+ QtJambi_LibraryUtilities.qtMajorVersion + "."
+ QtJambi_LibraryUtilities.qtMinorVersion + "."
+ QtJambi_LibraryUtilities.qtJambiPatch + ".", e);
}
throw e;
}
}
static void loadJambiLibrary(Class> callerClass, String library) {
LibVersion libVersion = getLibVersion(callerClass);
try {
loadQtJambiLibrary(callerClass, null, library, libVersion.qtMajorVersion, libVersion.qtMinorVersion, libVersion.qtJambiPatch);
} catch (RuntimeException | Error e) {
if(libVersion.qtMajorVersion!=QtJambi_LibraryUtilities.qtMajorVersion
|| libVersion.qtMinorVersion!=QtJambi_LibraryUtilities.qtMinorVersion
|| libVersion.qtJambiPatch!=QtJambi_LibraryUtilities.qtJambiPatch) {
throw new LinkageError("Cannot combine " + library + " "
+ libVersion.qtMajorVersion + "."
+ libVersion.qtMinorVersion + "."
+ libVersion.qtJambiPatch + " with QtJambi "
+ QtJambi_LibraryUtilities.qtMajorVersion + "."
+ QtJambi_LibraryUtilities.qtMinorVersion + "."
+ QtJambi_LibraryUtilities.qtJambiPatch + ".", e);
}
throw e;
}
}
private static File loadQtJambiLibrary(Class> callerClass, String prefix, String library, int... versionArray) {
Availability availability = getQtJambiLibraryAvailability(prefix, library, null, null, configuration, versionArray);
if(!availability.isAvailable()
&& versionArray!=null
&& versionArray.length==3
&& !dontSearchDeploymentSpec
&& !noNativeDeployment) {
String className = callerClass.getName();
int idx = className.lastIndexOf('.');
className = className.substring(idx+1);
URL _classURL = callerClass.getResource(className+".class");
if(_classURL!=null) {
int index;
String classURL = _classURL.toString();
File jarFile = null;
if(classURL.startsWith("jar:file:") && (index = classURL.indexOf("!/"))>0) {
String jarFileURL = classURL.substring(4, index);
try {
jarFile = new File(CoreUtility.createURL(jarFileURL).toURI());
} catch (URISyntaxException | MalformedURLException e) {
}
}else {
try {
URLConnection connection = _classURL.openConnection();
if(connection instanceof JarURLConnection) {
jarFile = new File(((JarURLConnection) connection).getJarFile().getName());
}
} catch (Throwable e) {
}
}
if(jarFile!=null && jarFile.exists()) {
final File directory;
if(isNativeSubdir) {
directory = new File(jarFile.getParentFile(), "native");
}else {
directory = jarFile.getParentFile();
}
final File debuginfoDirectory;
if(isDebuginfoSubdir) {
debuginfoDirectory = new File(jarFile.getParentFile(), "debuginfo");
}else {
debuginfoDirectory = jarFile.getParentFile();
}
String fileName = jarFile.getName();
String qtjambiVersion = String.format("-%1$s.%2$s.%3$s", versionArray[0], versionArray[1], versionArray[2]);
index = fileName.indexOf(qtjambiVersion+".jar");
if(index>0) {
String moduleName = fileName.substring(0, index);
String infix = "-native-" + osArchName;
String dinfix = "-debuginfo-" + osArchName;
if(configuration==LibraryBundle.Configuration.Debug)
infix += "-debug";
final File _directory;
final File _debuginfoDirectory;
if(isGradleCache) {
File __directory = directory;
File dir = new File(new File(directory.getParentFile().getParentFile().getParentFile(), moduleName + infix), String.format("%1$s.%2$s.%3$s", versionArray[0], versionArray[1], versionArray[2]));
if(dir.isDirectory()) {
for(File subdir : dir.listFiles()) {
if(subdir.isDirectory() && new File(subdir, moduleName + infix + qtjambiVersion+".jar").exists()) {
__directory = subdir;
break;
}
}
}
File __debuginfoDirectory = directory;
dir = new File(new File(directory.getParentFile().getParentFile().getParentFile(), moduleName + dinfix), String.format("%1$s.%2$s.%3$s", versionArray[0], versionArray[1], versionArray[2]));
if(dir.isDirectory()) {
for(File subdir : dir.listFiles()) {
if(subdir.isDirectory() && new File(subdir, moduleName + dinfix + qtjambiVersion+".jar").exists()) {
__debuginfoDirectory = subdir;
break;
}
}
}
_directory = __directory;
_debuginfoDirectory = __debuginfoDirectory;
}else if(isMavenRepo) {
_directory = new File(new File(directory.getParentFile().getParentFile(), moduleName + infix), String.format("%1$s.%2$s.%3$s", versionArray[0], versionArray[1], versionArray[2]));
_debuginfoDirectory = new File(new File(directory.getParentFile().getParentFile(), moduleName + dinfix), String.format("%1$s.%2$s.%3$s", versionArray[0], versionArray[1], versionArray[2]));
}else {
_directory = directory;
_debuginfoDirectory = debuginfoDirectory;
}
final File nativeFile = new File(_directory, moduleName + infix + qtjambiVersion+".jar");
if(nativeFile.exists()) {
try {
URL nativeFileURL = CoreUtility.createURL("jar:"+nativeFile.toURI()+DEPLOY_XML);
LibraryBundle deployment = null;
synchronized(loadedNativeDeploymentUrls) {
if(!loadedNativeDeploymentUrls.contains(nativeFileURL)) {
loadedNativeDeploymentUrls.add(nativeFileURL);
URL debuginfoFileURL = null;
File debuginfoFile = null;
if(debugInfoDeployment && configuration!=LibraryBundle.Configuration.Debug) {
debuginfoFile = new File(_debuginfoDirectory, moduleName + dinfix + qtjambiVersion+".jar");
if(debuginfoFile.exists()) {
debuginfoFileURL = CoreUtility.createURL("jar:"+debuginfoFile.toURI()+DEPLOY_XML);
}else {
debuginfoFile = null;
}
}
File _debuginfoFile = debuginfoFile;
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->_debuginfoFile==null ? String.format("Extracting libraries from %1$s", nativeFile.getAbsolutePath()) : String.format("Extracting libraries from %1$s and %2$s", nativeFile.getAbsolutePath(), _debuginfoFile.getAbsolutePath()));
deployment = prepareNativeDeployment(nativeFileURL, debuginfoFileURL, nativeFile.getName(), null);
if(deployment != null
&& String.format("%1$s.%2$s.%3$s", versionArray[0], versionArray[1], versionArray[2]).equals(deployment.version())
&& deployment.configuration()==configuration
&& (nativeDeployments.isEmpty() || deployment.compiler().equals(nativeDeployments.get(0).compiler()))) {
if("qtjambi".equals(deployment.module()))
nativeDeployments.add(0, deployment);
else
nativeDeployments.add(deployment);
if(qtJambiLibraryPath==null) {
switch (operatingSystem) {
case Windows:
qtJambiLibraryPath = new File(deployment.extractionDir(), "bin").getAbsolutePath();
break;
default:
qtJambiLibraryPath = new File(deployment.extractionDir(), "lib").getAbsolutePath();
break;
}
}
}else {
deployment = null;
}
}
}
if(deployment!=null) {
availability = getQtJambiLibraryAvailability(prefix, library, null, null, configuration, versionArray);
}
} catch (ParserConfigurationException | SAXException | ZipException e) {
Logger.getLogger("io.qt.internal").log(Level.WARNING, String.format("Unable to load native libraries from %1$s: %2$s", nativeFile.getName(), e.getMessage()), e);
} catch (IOException e) {
}
}
}
}
}
}
return loadNativeLibrary(callerClass, availability, versionArray);
}
static void loadQtLibrary(Class> callerClass, String library, LibraryRequirementMode libraryRequirementMode, String...platforms) {
if(platforms!=null && platforms.length>0) {
boolean skip = true;
for(String platform : platforms) {
if(osArchName.startsWith(platform)) {
skip = false;
break;
}
}
if(skip)
return;
}
LibVersion libVersion = getLibVersion(callerClass);
Availability availability = getLibraryAvailability("Qt", library, LIBINFIX, null, configuration, libVersion.qtMajorVersion, libVersion.qtMinorVersion);
switch(libraryRequirementMode) {
case ProvideOnly:
availability.extractLibrary();
return;
case Optional:
if(!availability.isAvailable()) {
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->String.format("Library unavailable: %1$s", library));
return;
}
break;
default:
break;
}
try {
switch(library) {
case "Qml":
isQmlLoaded.set(true);
LibraryBundle.deployQml();
break;
}
loadNativeLibrary(Object.class, availability, libVersion.qtMajorVersion, libVersion.qtMinorVersion);
} catch (RuntimeException | Error e) {
switch(libraryRequirementMode) {
case Optional:
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->e.getMessage());
return;
default:
break;
}
if(libVersion.qtMajorVersion!=QtJambi_LibraryUtilities.qtMajorVersion
|| libVersion.qtMinorVersion!=QtJambi_LibraryUtilities.qtMinorVersion) {
throw new LinkageError("Cannot combine Qt" + library + " " + libVersion.qtMajorVersion + "." + libVersion.qtMinorVersion + " with Qt " + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + ".", e);
}
throw e;
}
}
static void loadUtilityLibrary(String library, String version, LibraryRequirementMode libraryRequirementMode, String...platforms) {
if(platforms!=null && platforms.length>0) {
boolean skip = true;
for(String platform : platforms) {
if(osArchName.startsWith(platform)) {
skip = false;
break;
}
}
if(skip)
return;
}
Availability availability = getLibraryAvailability(null, library, LIBINFIX, version, configuration, null);
switch(libraryRequirementMode) {
case ProvideOnly:
availability.extractLibrary();
return;
case Optional:
if(!availability.isAvailable()) {
if(library.startsWith("icu")) {
if(LIBINFIX==null || LIBINFIX.isEmpty()) {
availability = getLibraryAvailability(null, library, null, "", configuration, null);
}else {
availability = getLibraryAvailability(null, library, null, version, configuration, null);
if(!availability.isAvailable())
availability = getLibraryAvailability(null, library, null, "", configuration, null);
}
}
return;
}
break;
default:
break;
}
loadNativeLibrary(Object.class, availability, null);
}
private static LibVersion getLibVersion(Class> callerClass) {
return qtJambiVersionsByClass.computeIfAbsent(callerClass, cls -> {
if(callerClass.getSimpleName().equals("QtJambi_LibraryUtilities")) {
try {
int qtMajorVersion = ReflectionUtility.readField(null, callerClass, "qtMajorVersion", int.class);
int qtMinorVersion = ReflectionUtility.readField(null, callerClass, "qtMinorVersion", int.class);
int qtJambiPatch = ReflectionUtility.readField(null, callerClass, "qtJambiPatch", int.class);
return new LibVersion(qtMajorVersion, qtMinorVersion, qtJambiPatch);
}catch(Throwable t) {}
URL _classURL = callerClass.getResource(callerClass.getSimpleName()+".class");
if(_classURL!=null) {
int index;
String classURL = _classURL.toString();
File jarFile = null;
if(classURL.startsWith("jar:file:") && (index = classURL.indexOf("!/"))>0) {
String jarFileURL = classURL.substring(4, index);
try {
jarFile = new File(CoreUtility.createURL(jarFileURL).toURI());
} catch (URISyntaxException | MalformedURLException e) {
}
}else {
try {
URLConnection connection = _classURL.openConnection();
if(connection instanceof JarURLConnection) {
jarFile = new File(((JarURLConnection) connection).getJarFile().getName());
}
} catch (Throwable e) {
}
}
if(jarFile!=null && jarFile.exists()) {
String fileName = jarFile.getName();
if(fileName.endsWith(".jar") && (index = fileName.lastIndexOf('-'))>0) {
fileName = fileName.substring(index+1);
String[] versionString = fileName.split("\\.");
return new LibVersion(Integer.parseInt(versionString[0]), Integer.parseInt(versionString[1]), Integer.parseInt(versionString[2]));
}
}
}
}
return new LibVersion(QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion, QtJambi_LibraryUtilities.qtJambiPatch);
});
}
public static boolean isMinGWBuilt() {
return isMinGWBuilt;
}
static boolean isAvailableQtLibrary(Class> callerClass, String library) {
LibVersion version = getLibVersion(callerClass);
return getLibraryAvailability("Qt", library, LIBINFIX, null, configuration, version.qtMajorVersion, version.qtMinorVersion).isAvailable();
}
static Availability getQtLibraryAvailability(Class> callerClass, String library) {
LibVersion version = getLibVersion(callerClass);
return getLibraryAvailability("Qt", library, LIBINFIX, null, configuration, version.qtMajorVersion, version.qtMinorVersion);
}
static boolean isAvailableLibrary(String library, String version) {
if(version!=null && !version.isEmpty()) {
String[] parts = version.split("\\.");
int[] iparts = new int[parts.length];
for (int i = 0; i < iparts.length; i++) {
try {
iparts[i] = Integer.parseInt(parts[i]);
} catch (Exception e) {
iparts = null;
break;
}
}
if(iparts!=null)
return getLibraryAvailability(null, library, null, null, configuration, iparts).isAvailable();
}
return getLibraryAvailability(null, library, null, version, configuration, null).isAvailable();
}
private static class Availability{
public Availability(String libraryRawName, File file, String libFormat, List replacements) {
super();
this.libraryRawName = libraryRawName;
this.file = file;
this.entry = null;
this.libFormat = libFormat;
this.replacements = replacements;
}
public Availability(String libraryRawName, String libFormat, List replacements) {
super();
this.libraryRawName = libraryRawName;
this.file = null;
this.entry = null;
this.libFormat = libFormat;
this.replacements = replacements;
}
public Availability(String libraryRawName, Library entry, File file, String libFormat, Iterable replacements) {
super();
this.libraryRawName = libraryRawName;
this.file = file;
this.entry = entry;
this.libFormat = libFormat;
this.replacements = replacements;
}
final String libraryRawName;
final File file;
final Library entry;
final Iterable replacements;
final String libFormat;
boolean isAvailable() {
return (file!=null && file.exists()) || entry!=null;
}
void extractLibrary() {
if(entry!=null && entry.isExtracting() && !entry.isLoaded()) {
try {
entry.extract();
} catch (Throwable e1) {
throw new QLibraryNotLoadedError("Unable to extract library "+entry.getName(), e1);
}
}
}
}
private static Availability getQtJambiLibraryAvailability(String qtprefix, String library, String libInfix, String versionStrg, LibraryBundle.Configuration configuration, int... version) {
List replacements = new ArrayList<>();
String libFormat = qtjambiLibraryName(qtprefix, library, dontUseQtJambiFrameworks!=null && dontUseQtJambiFrameworks, configuration, replacements, version);
Availability av = getLibraryAvailability(qtprefix==null ? library : qtprefix+library, libFormat, replacements);
if(!av.isAvailable() && operatingSystem==OperatingSystem.MacOS && dontUseQtJambiFrameworks!=null && dontUseQtJambiFrameworks) {
libFormat = qtjambiLibraryName(qtprefix, library, false, configuration, replacements, version);
Availability av2 = getLibraryAvailability(qtprefix==null ? library : qtprefix+library, libFormat, replacements);
if(av2.isAvailable())
av = av2;
}
return av;
}
private static Availability getLibraryAvailability(String qtprefix, String library, String libInfix, String versionStrg, LibraryBundle.Configuration configuration, int... version) {
List replacements = new ArrayList<>();
String libFormat = qtLibraryName(qtprefix, library, libInfix, versionStrg, configuration, replacements, version); // "QtDBus" => "libQtDBus.so.4"
return getLibraryAvailability(qtprefix==null ? library : qtprefix+library, libFormat, replacements);
}
private static List computeLibraryPaths(){
String libPaths = System.getProperty("io.qt.library-path-override");
String libPaths2 = null;
if (libPaths == null || libPaths.length() == 0) {
libPaths = System.getProperty("java.library.path");
}
List libraryPaths = new ArrayList<>();
if (libPaths != null)
libraryPaths.addAll(Arrays.asList(libPaths.split("\\"+File.pathSeparator)));
switch (operatingSystem) {
case Windows:
libPaths = System.getenv("PATH");
break;
case MacOS:
libPaths = System.getenv("DYLD_FRAMEWORK_PATH");
libPaths2 = System.getenv("DYLD_LIBRARY_PATH");
break;
default:
libPaths = System.getenv("LD_LIBRARY_PATH");
}
if (libPaths != null)
libraryPaths.addAll(ldLibraryPaths.computeIfAbsent(libPaths, LibraryUtility::splitPath));
if (libPaths2 != null)
libraryPaths.addAll(ldLibraryPaths.computeIfAbsent(libPaths2, LibraryUtility::splitPath));
synchronized(loadedNativeDeploymentUrls) {
if(qtJambiLibraryPath!=null)
libraryPaths.add(qtJambiLibraryPath);
}
switch (operatingSystem) {
case Windows:
libraryPaths.add(new File(jambiDeploymentDir, "bin").getAbsolutePath());
if(jambiJarDir!=null && new File(jambiJarDir, "bin").isDirectory())
libraryPaths.add(new File(jambiJarDir, "bin").getAbsolutePath());
break;
default:
libraryPaths.add(new File(jambiDeploymentDir, "lib").getAbsolutePath());
if(jambiJarDir!=null && new File(jambiJarDir, "lib").isDirectory())
libraryPaths.add(new File(jambiJarDir, "lib").getAbsolutePath());
break;
}
if (System.getProperties().containsKey("io.qt.library-path-override")) {
// reporter.report(" - using 'io.qt.library-path-override'");
libraryPaths.addAll(javaLibraryPathOverrides.computeIfAbsent(System.getProperty("io.qt.library-path-override"), LibraryUtility::splitPath));
} else {
// reporter.report(" - using 'java.library.path'");
libraryPaths.addAll(javaLibraryPaths.computeIfAbsent(System.getProperty("java.library.path"), LibraryUtility::splitPath));
}
return mergeJniLibdir(libraryPaths);
}
private static Availability getLibraryAvailability(String libraryRawName, String libFormat, List replacements) {
if(operatingSystem!=OperatingSystem.Android
&& !useStaticLibs) {
List libraryPaths = null;
// search active deploymentSpec for existance of library
Iterator iter = replacements.iterator();
do {
String lib;
if(!iter.hasNext()) {
lib = libFormat;
}else {
lib = String.format(libFormat, iter.next());
}
if(!noNativeDeployment) {
Library entry = LibraryBundle.findLibrary(lib);
if(entry!=null) {
return new Availability(libraryRawName, entry, entry.extractionPath(), libFormat, replacements);
}
}
if(libraryPaths==null)
libraryPaths = computeLibraryPaths();
for (String path : libraryPaths) {
File f = new File(path, lib);
if (f.exists()) {
return new Availability(libraryRawName, f, libFormat, replacements);
}
}
}while(iter.hasNext());
}else {
String libraryFilePath = libraryFilePath();
File dir = new File(libraryFilePath).getParentFile();
Iterator iter = replacements.iterator();
do {
String lib;
if(!iter.hasNext()) {
lib = libFormat;
}else {
lib = String.format(libFormat, iter.next());
}
File f = new File(dir, "lib"+lib+".so");
if (f.exists()) {
return new Availability(libraryRawName, f, libFormat, replacements);
}
}while(iter.hasNext());
}
return new Availability(libraryRawName, libFormat, replacements);
}
private static native String libraryFilePath();
/**
* Returns a classloader for current context...
* @return The classloader
*/
private static ClassLoader classLoader() {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
if (loader == null) {
loader = LibraryUtility.class.getClassLoader();
assert(loader != null);
}
return loader;
}
private static File loadNativeLibrary(Class> callerClass, Availability availability, int... versionArray) {
try {
if(loadedLibraries.contains(availability.libFormat))
return null;
if(operatingSystem==OperatingSystem.Android || useStaticLibs) {
ClassLoader callerClassLoader = callerClass.getClassLoader();
if(callerClassLoader==LibraryUtility.class.getClassLoader()
|| callerClassLoader==Object.class.getClassLoader()) {
Runtime.getRuntime().loadLibrary(availability.libFormat);
}else {
libraryLoader.accept(callerClass, availability.libFormat);
}
loadedLibraries.add(availability.libFormat);
return null;
}else {
if(availability.entry!=null && availability.entry.isLoaded()) {
File result = availability.entry.extractionPath();
if(operatingSystem==OperatingSystem.MacOS && (dontUseQtFrameworks==null || !dontUseQtFrameworks) && qtLibraryPath==null
&& !result.getName().endsWith(".dylib") && !result.getName().endsWith(".jnilib")) {
result = result.getParentFile().getParentFile().getParentFile();
}
return result;
}
ClassLoader callerClassLoader = callerClass.getClassLoader();
boolean qmlLoaded = isQmlLoaded.get();
File libFile = availability.file;
if(libFile==null && availability.entry!=null) {
libFile = availability.entry.extractionPath();
}
if(libFile==null) {
List fileNames = new ArrayList<>();
Iterator iter = availability.replacements.iterator();
do {
if(!iter.hasNext()) {
fileNames.add(availability.libFormat);
}else {
fileNames.add(String.format(availability.libFormat, iter.next()));
}
}while(iter.hasNext());
String message;
if(availability.entry!=null) {
message = String.format("Library %1$s (%2$s) was not found in %3$s", availability.libraryRawName, String.join(", ", fileNames), availability.entry.source());
}else {
if (System.getProperties().containsKey("io.qt.library-path-override")) {
message = String.format("Library %1$s (%2$s) was not found in 'io.qt.library-path-override=%3$s%4$s%5$s'", availability.libraryRawName, String.join(", ", fileNames), System.getProperty("io.qt.library-path-override"), File.pathSeparator, jambiDeploymentDir);
}else {
message = String.format("Library %1$s (%2$s) was not found in 'java.library.path=%3$s%4$s%5$s'", availability.libraryRawName, String.join(", ", fileNames), System.getProperty("java.library.path"), File.pathSeparator, jambiDeploymentDir);
}
}
throw new QLibraryNotFoundError(message);
}
if(availability.entry!=null && (availability.entry.isExtracting() || (qmlLoaded && availability.entry.isQmlExtracting()))) { // might be just writing
if(availability.entry.isExtracting()) {
try {
availability.entry.extract();
} catch (Throwable e1) {
throw new QLibraryNotLoadedError("Unable to extract library "+availability.entry.getName(), e1);
}
}
if(qmlLoaded && availability.entry.isQmlExtracting()) {
try {
availability.entry.extractQml();
} catch (Throwable e1) {
throw new QLibraryNotLoadedError("Unable to extract library "+availability.entry.getName(), e1);
}
}
}
if(libFile.exists()) {
File _libFile = libFile;
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->String.format("Loading library: '%1$s' from location: %2$s", availability.libraryRawName, _libFile.getAbsolutePath()));
if(callerClassLoader==LibraryUtility.class.getClassLoader()
|| callerClassLoader==Object.class.getClassLoader()) {
Runtime.getRuntime().load(availability.file.getAbsolutePath());
}else {
libraryLoader.accept(callerClass, availability.file.getAbsolutePath());
}
loadedLibraries.add(availability.libFormat);
File result = libFile;
if(operatingSystem==OperatingSystem.MacOS && (dontUseQtFrameworks==null || !dontUseQtFrameworks) && qtLibraryPath==null
&& !result.getName().endsWith(".dylib") && !result.getName().endsWith(".jnilib")) {
result = result.getParentFile().getParentFile().getParentFile();
}
if(availability.entry!=null)
availability.entry.setLoaded(true);
return result;
}else {
String message;
if(availability.entry!=null) {
message = String.format("Library %1$s (%2$s) was not found in %3$s", availability.libraryRawName, libFile.getName(), availability.entry.source());
}else {
if (System.getProperties().containsKey("io.qt.library-path-override")) {
message = String.format("Library %1$s (%2$s) was not found in 'io.qt.library-path-override=%3$s'", availability.libraryRawName, libFile.getName(), System.getProperty("io.qt.library-path-override"));
}else {
message = String.format("Library %1$s (%2$s) was not found in 'java.library.path=%3$s'", availability.libraryRawName, libFile.getName(), System.getProperty("java.library.path"));
}
}
throw new QLibraryNotFoundError(message);
}
}
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
if(e.getMessage().isEmpty()) {
throw new QLibraryNotLoadedError("Loading library failed.", e);
}else {
throw new QLibraryNotLoadedError(e.getMessage(), e);
}
}
}
private static List splitPath(String path){
if(path!=null) {
return Arrays.asList(path.split("\\"+File.pathSeparator));
}else {
return Collections.emptyList();
}
}
private static class ExecutorThread extends Thread implements Executor{
private final List commands = new LinkedList<>();
private boolean noMoreExpected = false;
private ExecutorThread(String module) {
super();
this.setDaemon(true);
this.setName("QtJambi_LibraryExtractor_"+module);
}
@Override
public void execute(Runnable command) {
synchronized(this.commands) {
this.commands.add(command);
this.commands.notifyAll();
}
}
@Override
public void run() {
try {
while(true) {
Runnable first;
synchronized(this.commands) {
if(this.commands.isEmpty()) {
if(noMoreExpected)
break;
this.commands.wait();
if(!this.commands.isEmpty()) {
first = this.commands.remove(0);
}else {
if(noMoreExpected)
break;
continue;
}
}else {
first = this.commands.remove(0);
}
}
try {
first.run();
} catch (Throwable e) {
if(e instanceof InterruptedException)
throw e;
java.util.logging.Logger.getLogger("io.qt.internal").log(java.util.logging.Level.SEVERE, "", e);
}
}
} catch (InterruptedException e) {
}
}
void setNoMoreExpected() {
synchronized(this.commands) {
noMoreExpected = true;
this.commands.notifyAll();
}
}
}
private static Library.ExtractionFunction getLibraryExtractor(String urlBase, String libName, File outFile, boolean executable, boolean isDebug, boolean isQtLib, int... version){
String qtLibName = null;
if(isQtLib) {
qtLibName = outFile.getName();
switch(operatingSystem) {
case Android:
String prefix = "libQt"+QtJambi_LibraryUtilities.qtMajorVersion;
if(qtLibName.startsWith(prefix)) {
qtLibName = qtLibName.substring(prefix.length());
String suffix;
switch (architecture) {
case arm:
suffix = "_armeabi-v7a.so";
break;
case arm64:
suffix = "_arm64-v8a.so";
break;
case x86:
suffix = "_x86.so";
break;
case x86_64:
suffix = "_x86_64.so";
break;
default:
suffix = ".so";
break;
}
if(qtLibName.endsWith(suffix)) {
qtLibName = qtLibName.substring(0, qtLibName.length() - suffix.length());
}else {
qtLibName = null;
}
}
break;
case MacOS:
if(!libName.contains(".framework/")) {
prefix = "libQt"+QtJambi_LibraryUtilities.qtMajorVersion;
int dot = qtLibName.indexOf('.');
if(dot>0) {
qtLibName = qtLibName.substring(0, dot);
}else {
qtLibName = null;
}
}
break;
case Windows:
prefix = "Qt"+QtJambi_LibraryUtilities.qtMajorVersion;
String suffix = isDebug ? "d.dll" : ".dll";
if(qtLibName.endsWith(suffix)) {
qtLibName = qtLibName.substring(0, qtLibName.length() - suffix.length());
}else {
qtLibName = null;
}
break;
default:
if(operatingSystem.isUnixLike()) {
prefix = "libQt"+QtJambi_LibraryUtilities.qtMajorVersion;
int dot = qtLibName.indexOf('.');
if(dot>0) {
qtLibName = qtLibName.substring(0, dot);
}else {
qtLibName = null;
}
}
break;
}
}
List dependencies = qtLibName==null ? Collections.emptyList() : QtJambi_LibraryUtilities.dependencies.getOrDefault("Qt"+qtLibName, Collections.emptyList());
return ()->{
URL entryURL = CoreUtility.createURL(urlBase+libName);
try(InputStream in = entryURL.openStream()){
File outFileDir = outFile.getParentFile();
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->String.format(" - copying '%1$s' to %2$s", libName, outFile.getAbsolutePath()));
if (!outFileDir.exists()) {
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->String.format(" - creating directory: %1$s", outFileDir.getAbsolutePath()));
outFileDir.mkdirs();
}
Files.copy(in, outFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
if(executable)
outFile.setExecutable(true);
if(!dependencies.isEmpty()) {
boolean qmlExtract = isQmlLoaded.get();
for(Dependency dep : dependencies) {
List replacements = new ArrayList<>();
String libFormat = qtLibraryName("Qt", dep.library, LIBINFIX, null, configuration, replacements, version);
Iterator iter = replacements.iterator();
do {
String library;
if(!iter.hasNext()) {
library = libFormat;
}else {
library = String.format(libFormat, iter.next());
}
Library qmlLibrary = LibraryBundle.findLibrary(library);
if(qmlLibrary!=null) {
if(!qmlLibrary.isLoaded() && qmlLibrary.isExtracting()) {
qmlLibrary.extract();
if(qmlExtract && qmlLibrary.isQmlExtracting()) {
qmlLibrary.extractQml();
}
}
break;
}
}while(iter.hasNext());
}
}
} catch (Exception e1) {
return;
}
};
}
private static LibraryBundle prepareNativeDeployment(URL deploymentSpec, URL debuginfoFileURL, String jarName, Boolean shouldUnpack) throws ParserConfigurationException, SAXException, IOException{
LibraryBundle spec = null;
try {
spec = LibraryBundle.read(deploymentSpec);
} catch (java.util.zip.ZipException e1) {
} catch (SpecificationException e1) {
Logger.getLogger("io.qt.internal").warning(String.format("Unable to load native libraries from %1$s: %2$s", (jarName==null ? deploymentSpec : jarName), e1.getMessage()));
}
if (spec==null)
return null;
LibraryBundle debuginfoSpec = null;
if(debuginfoFileURL!=null) {
try {
debuginfoSpec = LibraryBundle.read(debuginfoFileURL);
if(!debuginfoSpec.isDebuginfo()
|| !spec.compiler().equals(debuginfoSpec.compiler())
|| !spec.configuration().equals(debuginfoSpec.configuration())
|| !spec.module().equals(debuginfoSpec.module())
|| !spec.version().equals(debuginfoSpec.version()))
debuginfoSpec = null;
} catch (java.util.zip.ZipException e1) {
} catch (SpecificationException e1) {
Logger.getLogger("io.qt.internal").warning(String.format("Unable to load native libraries from %1$s: %2$s", (jarName==null ? deploymentSpec : jarName), e1.getMessage()));
}
}
boolean isDebug = spec.configuration()==LibraryBundle.Configuration.Debug;
boolean isQtLib = spec.module()!=null && spec.module().startsWith("qt.lib.");
boolean isQtQml = spec.module()!=null && spec.module().startsWith("qt.qml.");
boolean isQtPlugin = spec.module()!=null && spec.module().startsWith("qt.plugin.");
final File tmpDir;
if((isQtLib || isQtQml || isQtPlugin) && !deleteTmpDeployment) {
tmpDir = new File(jambiDeploymentDir, "Qt"+spec.version());
}else {
tmpDir = jambiDeploymentDir;
}
spec.setExtractionDir(tmpDir);
if(debuginfoSpec!=null)
debuginfoSpec.setExtractionDir(tmpDir);
File dummyFile = null;
if(shouldUnpack == null) {
if(spec.module()!=null) {
shouldUnpack = Boolean.TRUE;
}else {
// If the dir exists and contains .dummy, sanity check the contents...
dummyFile = new File(tmpDir, ".dummy");
if(spec.module()!=null)
dummyFile = new File(tmpDir, "."+spec.module()+".dummy");
if (dummyFile.exists()) {
Logger.getLogger("io.qt.internal").log(Level.FINEST, " - cache directory exists");
shouldUnpack = Boolean.FALSE;
} else {
shouldUnpack = Boolean.TRUE;
}
}
}
if (shouldUnpack.booleanValue()) {
Logger.getLogger("io.qt.internal").log(Level.FINEST, " - prepare library extraction...");
LibVersion specVersion;
try {
String[] versionString = spec.version().split("\\.");
specVersion = new LibVersion(Integer.parseInt(versionString[0]), Integer.parseInt(versionString[1]), Integer.parseInt(versionString[2]));
} catch (Exception e2) {
specVersion = new LibVersion(QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion, QtJambi_LibraryUtilities.qtJambiPatch);
}
List qmlExtractionFunctions = new LinkedList<>();
List utilExtractionFunctions = new LinkedList<>();
{
String urlBase = deploymentSpec.toString();
int idx = urlBase.indexOf("!/");
if(idx>0) {
urlBase = urlBase.substring(0, idx+2);
}
for(QPair pair : spec.files()) {
final File outFile;
if(pair.first.startsWith("sources/")) {
if(jambiSourcesDir!=null) {
outFile = new File(jambiSourcesDir, pair.first.replace('/', File.separatorChar));
}else {
continue;
}
}else if(pair.first.startsWith("include/")
|| (operatingSystem==OperatingSystem.MacOS && pair.first.contains("/Headers/"))) {
if(jambiHeadersDir!=null) {
outFile = new File(jambiHeadersDir, pair.first.replace('/', File.separatorChar));
}else {
continue;
}
}else {
outFile = new File(tmpDir, pair.first.replace('/', File.separatorChar));
}
if(!outFile.exists()) {
Library.ExtractionFunction extractor = getLibraryExtractor(urlBase, pair.first, outFile, Boolean.TRUE.equals(pair.second), isDebug, false, specVersion.qtMajorVersion, specVersion.qtMinorVersion, specVersion.qtJambiPatch);
if(pair.first.startsWith("qml/")) {
qmlExtractionFunctions.add(extractor);
}else {
utilExtractionFunctions.add(extractor);
}
}
}
}
if(debuginfoSpec!=null) {
String debuginfoUrlBase = debuginfoFileURL.toString();
int idx = debuginfoUrlBase.indexOf("!/");
if(idx>0) {
debuginfoUrlBase = debuginfoUrlBase.substring(0, idx+2);
}
for(QPair pair : debuginfoSpec.files()) {
final File outFile;
if(pair.first.startsWith("sources/")) {
if(jambiSourcesDir!=null) {
outFile = new File(jambiSourcesDir, pair.first.replace('/', File.separatorChar));
}else {
continue;
}
}else if(pair.first.startsWith("include/")
|| (operatingSystem==OperatingSystem.MacOS && pair.first.contains("/Headers/"))) {
if(jambiHeadersDir!=null) {
outFile = new File(jambiHeadersDir, pair.first.replace('/', File.separatorChar));
}else {
continue;
}
}else {
outFile = new File(tmpDir, pair.first.replace('/', File.separatorChar));
}
if(!outFile.exists()) {
Library.ExtractionFunction extractor = getLibraryExtractor(debuginfoUrlBase, pair.first, outFile, Boolean.TRUE.equals(pair.second), isDebug, false, specVersion.qtMajorVersion, specVersion.qtMinorVersion, specVersion.qtJambiPatch);
if(pair.first.startsWith("qml/")) {
qmlExtractionFunctions.add(extractor);
}else {
utilExtractionFunctions.add(extractor);
}
}
}
}
Map entries = new TreeMap<>();
ExecutorThread executor = null;
if("qtjambi".equals(spec.module()) || "qt.lib.core".equals(spec.module())) {
executor = new ExecutorThread(spec.module());
executor.start();
}
if(isQtLib && "qt.lib.core".equals(spec.module()) && !spec.isDebuginfo()) {
switch(operatingSystem) {
case Android:
case IOS:
case MacOS:
break;
default:
File qtConf = new File(jambiDeploymentDir, "qtconf.jar");
if((jambiDeploymentDir.isDirectory() || jambiDeploymentDir.mkdirs()) && !qtConf.exists()) {
File binFolder = new File(jambiDeploymentDir, "bin");
binFolder.mkdirs();
File confFile = new File(binFolder, "qt.conf");
try {
try(PrintStream out = new PrintStream(new FileOutputStream(confFile), false, "UTF-8")){
out.println("[Paths]");
out.print("Prefix=");
if(File.separatorChar=='/')
out.println(jambiDeploymentDir.getAbsolutePath());
else
out.println(jambiDeploymentDir.getAbsolutePath().replace(File.separator, "/"));
out.println("Libraries=lib");
out.println(operatingSystem==OperatingSystem.Windows ? "LibraryExecutables=bin" : "LibraryExecutables=libexec");
out.println("Binaries=bin");
out.println("Plugins=plugins");
out.println("QmlImports=qml");
out.println("Translations=translations");
out.println("ArchData=.");
out.println("Data=.");
out.println("Headers=include");
out.flush();
}
}catch(Throwable t){
confFile.delete();
}
if(confFile.exists()) {
if(operatingSystem!=OperatingSystem.Windows) {
File libFolder = new File(jambiDeploymentDir, "libexec");
libFolder.mkdirs();
Files.copy(confFile.toPath(), new File(libFolder, "qt.conf").toPath());
}
try(JarOutputStream confJar = new JarOutputStream(new FileOutputStream(qtConf))){
confJar.putNextEntry(new ZipEntry("qt/etc/qt.conf"));
Files.copy(confFile.toPath(), confJar);
confJar.closeEntry();
}
}
}
if(qtConf.exists()) {
ResourceUtility.addSearchPath(qtConf.toURI().toURL());
}
break;
}
}
try {
List libraries = spec.libraries();
if(debuginfoSpec!=null) {
libraries = new ArrayList<>(libraries);
libraries.addAll(debuginfoSpec.libraries());
}
Library library = null;
if(isQtLib) {
if(!libraries.isEmpty() && !(libraries.get(0) instanceof Symlink)) {
libraries = new ArrayList<>(libraries);
library = libraries.remove(0);
entries.put(library.getName(), library);
String libName = library.getName();
String urlBase = library.source().toString();
int idx = urlBase.indexOf("!/");
if(idx>0) {
urlBase = urlBase.substring(0, idx+2);
}
File outFile = new File(tmpDir, libName.replace('/', File.separatorChar));
if(!outFile.exists()) {
Library.ExtractionFunction extractLibrary = getLibraryExtractor(urlBase, libName, outFile, false, isDebug, isQtLib, specVersion.qtMajorVersion, specVersion.qtMinorVersion, specVersion.qtJambiPatch);
if(executor!=null) {
CompletableFuture future = CompletableFuture.supplyAsync(()->{
try {
extractLibrary.extract();
Library.ExtractionFunction first = null;
while(true) {
synchronized(utilExtractionFunctions) {
utilExtractionFunctions.remove(first);
if(utilExtractionFunctions.isEmpty())
break;
else
first = utilExtractionFunctions.get(0);
}
first.extract();
}
} catch (Throwable e1) {
return e1;
}
return null;
}, executor);
library.addExtractionFunction(()->{
Throwable exn = future.get();
if(exn!=null)
throw exn;
});
}else {
library.addExtractionFunction(extractLibrary);
library.addExtractionFunctions(utilExtractionFunctions);
}
List qmlLibraries = library.bundle().qmlLibraries();
if(!qmlLibraries.isEmpty()) {
library.addQmlExtractionFunction(()->{
for(String lib : qmlLibraries) {
if(lib.startsWith("lib/") || lib.startsWith("bin/"))
lib = lib.substring(4);
Library qmlLibrary = LibraryBundle.findLibrary(lib);
if(qmlLibrary!=null && !qmlLibrary.isLoaded() && qmlLibrary.isExtracting()) {
qmlLibrary.extract();
if(qmlLibrary.isQmlExtracting()) {
qmlLibrary.extractQml();
}
}
}
});
}
library.addQmlExtractionFunctions(qmlExtractionFunctions);
}
}
}
for (Library e : libraries) {
entries.put(e.getName(), e);
if(!(e instanceof Symlink)) {
String libName = e.getName();
final File outFile;
if(libName.startsWith("sources/")) {
if(jambiSourcesDir!=null) {
outFile = new File(jambiSourcesDir, libName.replace('/', File.separatorChar));
}else {
continue;
}
}else if(libName.startsWith("include/")
|| (operatingSystem==OperatingSystem.MacOS && libName.contains("/Headers/"))) {
if(jambiHeadersDir!=null) {
outFile = new File(jambiHeadersDir, libName.replace('/', File.separatorChar));
}else {
continue;
}
}else {
outFile = new File(tmpDir, libName.replace('/', File.separatorChar));
}
if(!outFile.exists()) {
boolean isQtJambiPlugin = e.bundle().module()!=null && e.bundle().module().startsWith("qtjambi.plugin.") && libName.startsWith("plugins/");
if(isQtJambiPlugin || isQtPlugin || isQtQml) {
outFile.getParentFile().mkdirs();
pluginLibraries.add(e);
}
String urlBase = e.source().toString();
int idx = urlBase.indexOf("!/");
if(idx>0) {
urlBase = urlBase.substring(0, idx+2);
}
Library.ExtractionFunction extractLibrary = getLibraryExtractor(urlBase, libName, outFile, false, isDebug,
libName.startsWith("lib/libQt"+QtJambi_LibraryUtilities.qtMajorVersion)
|| (libName.startsWith("lib/Qt") && !libName.startsWith("lib/QtJambi"))
|| libName.startsWith("bin/Qt"+QtJambi_LibraryUtilities.qtMajorVersion),
specVersion.qtMajorVersion, specVersion.qtMinorVersion, specVersion.qtJambiPatch);
if(library!=null) {
if(libName.startsWith("qml/")) {
library.addQmlExtractionFunction(extractLibrary);
}else {
library.addExtractionFunction(extractLibrary);
}
e.addExtractionFunction(extractLibrary);
}else if(executor!=null
&& ((!libName.contains("QtJambiGui")
&& !libName.contains("QtJambiWidgets")
&& !(libName.startsWith("include/")
|| (operatingSystem==OperatingSystem.MacOS && libName.contains("/Headers/"))))
|| isQtJambiPlugin)) {
CompletableFuture future = CompletableFuture.supplyAsync(()->{
try {
extractLibrary.extract();
Library.ExtractionFunction first = null;
while(true) {
synchronized(utilExtractionFunctions) {
utilExtractionFunctions.remove(first);
if(utilExtractionFunctions.isEmpty())
break;
else
first = utilExtractionFunctions.get(0);
}
first.extract();
}
} catch (Throwable e1) {
return e1;
}
return null;
}, executor);
e.addExtractionFunction(()->{
Throwable exn = future.get();
if(exn!=null)
throw exn;
});
}else {
e.addExtractionFunction(extractLibrary);
e.addExtractionFunctions(utilExtractionFunctions);
}
e.addQmlExtractionFunctions(qmlExtractionFunctions);
}
}
}
List shiftedLinks = new ArrayList<>();
for (Library e : libraries) {
if(e instanceof Symlink) {
final File outFile;
if(e.getName().startsWith("sources/")) {
if(jambiSourcesDir!=null) {
outFile = new File(jambiSourcesDir, e.getName().replace('/', File.separatorChar));
}else {
continue;
}
}else if(e.getName().startsWith("include/")
|| (operatingSystem==OperatingSystem.MacOS && e.getName().contains("/Headers/"))) {
if(jambiHeadersDir!=null) {
outFile = new File(jambiHeadersDir, e.getName().replace('/', File.separatorChar));
}else {
continue;
}
}else {
outFile = new File(tmpDir, e.getName().replace('/', File.separatorChar));
}
if(!outFile.exists()) {
Symlink s = (Symlink)e;
File outFileDir = outFile.getParentFile();
File target = new File(tmpDir, s.getTarget().replace('/', File.separatorChar));
if(target.exists()) {
if (!outFileDir.exists()) {
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->String.format(" - creating directory: %1$s", outFileDir.getAbsolutePath()));
outFileDir.mkdirs();
}
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->String.format(" - creating symbolic link %1$s", outFile.getAbsolutePath()));
Files.createSymbolicLink(outFile.toPath(), outFile.getParentFile().toPath().relativize(target.toPath()));
}else {
Library.ExtractionFunction linker = ()->{
if(!outFile.exists()) {
if (!outFileDir.exists()) {
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->String.format(" - creating directory: %1$s", outFileDir.getAbsolutePath()));
outFileDir.mkdirs();
}
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->String.format(" - creating symbolic link %1$s", outFile.getAbsolutePath()));
Files.createSymbolicLink(outFile.toPath(), outFile.getParentFile().toPath().relativize(target.toPath()));
}
s.extractQml();
};
if(e.getName().startsWith("qml/")) {
if(library!=null) {
library.addQmlExtractionFunction(linker);
}else {
Library linkedEntry = entries.get(s.getTarget());
if(linkedEntry!=null) {
linkedEntry.addQmlExtractionFunction(linker);
}else {
shiftedLinks.add(s);
}
}
}else {
if(library!=null) {
library.addExtractionFunction(linker);
}else {
Library linkedEntry = entries.get(s.getTarget());
if(linkedEntry!=null) {
linkedEntry.addExtractionFunction(linker);
}else {
shiftedLinks.add(s);
}
}
}
}
}
}
}
if(!shiftedLinks.isEmpty()) {
List _shiftedLinks = new ArrayList<>();
while(!shiftedLinks.isEmpty()) {
_shiftedLinks.clear();
for (Symlink s : shiftedLinks) {
final File outFile;
if(s.getName().startsWith("sources/")) {
if(jambiSourcesDir!=null) {
outFile = new File(jambiSourcesDir, s.getName().replace('/', File.separatorChar));
}else {
continue;
}
}else if(s.getName().startsWith("include/")
|| (operatingSystem==OperatingSystem.MacOS && s.getName().contains("/Headers/"))) {
if(jambiHeadersDir!=null) {
outFile = new File(jambiHeadersDir, s.getName().replace('/', File.separatorChar));
}else {
continue;
}
}else {
outFile = new File(tmpDir, s.getName().replace('/', File.separatorChar));
}
if(!outFile.exists()) {
File outFileDir = outFile.getParentFile();
File target = new File(tmpDir, s.getTarget().replace('/', File.separatorChar));
if(target.exists()) {
if (!outFileDir.exists()) {
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->String.format(" - creating directory: %1$s", outFileDir.getAbsolutePath()));
outFileDir.mkdirs();
}
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->String.format(" - creating symbolic link %1$s", outFile.getAbsolutePath()));
Files.createSymbolicLink(outFile.toPath(), outFile.getParentFile().toPath().relativize(target.toPath()));
}else {
Library linkedEntry = entries.get(s.getTarget());
if(linkedEntry!=null) {
Library.ExtractionFunction linker = ()->{
if(!outFile.exists()) {
if (!outFileDir.exists()) {
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->String.format(" - creating directory: %1$s", outFileDir.getAbsolutePath()));
outFileDir.mkdirs();
}
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->String.format(" - creating symbolic link %1$s", outFile.getAbsolutePath()));
Files.createSymbolicLink(outFile.toPath(), outFile.getParentFile().toPath().relativize(target.toPath()));
}
s.extractQml();
};
if(s.getName().startsWith("qml/")) {
linkedEntry.addQmlExtractionFunction(linker);
}else {
linkedEntry.addExtractionFunction(linker);
}
}else {
_shiftedLinks.add(s);
}
}
}
}
if(_shiftedLinks.size()==shiftedLinks.size()) {
break;
}
shiftedLinks.clear();
shiftedLinks.addAll(_shiftedLinks);
}
}
if (dummyFile != null && !dummyFile.createNewFile()) {
throw new SpecificationException("Can't create dummy file in cache directory");
}
spec.setExtractionDir(tmpDir);
if(debuginfoSpec!=null) {
debuginfoSpec.setExtractionDir(tmpDir);
}
}finally {
if(executor!=null)
executor.setNoMoreExpected();
}
} else if(spec.extractionDir() == null) {
String path = deploymentSpec.getPath();
int i = path.lastIndexOf('/'); // URL path
if(i >= 0)
path = path.substring(0, i);
spec.setExtractionDir(new File(path));
if(debuginfoSpec!=null) {
debuginfoSpec.setExtractionDir(new File(path));
}
// spec.setExtractionUrl(CoreUtility.createURL(deploymentSpec, path));
}
return spec;
}
private static boolean loadSystemLibrary(String name) {
Logger.getLogger("io.qt.internal").log(Level.FINEST, ()->String.format(" - trying to load: %1$s", name));
File foundFile = null;
synchronized(loadedNativeDeploymentUrls) {
for(LibraryBundle deploymentSpec : nativeDeployments) {
File f = new File(deploymentSpec.extractionDir(), name);
if(f.isFile()) {
foundFile = f;
break;
}
}
}
if(foundFile == null)
return false;
Runtime rt = Runtime.getRuntime();
rt.load(foundFile.getAbsolutePath());
Logger.getLogger("io.qt.internal").log(Level.FINEST, " - ok!");
return true;
}
private static String qtjambiLibraryName(String prefix, String lib, boolean dontUseQtJambiFrameworks, LibraryBundle.Configuration configuration, List replacements, int... version) {
if(prefix!=null)
lib = prefix + lib;
replacements.clear();
if(useStaticLibs) {
return lib;
}else {
switch (operatingSystem) {
case Windows: {
if(version.length>0)
replacements.add(""+version[0]);
replacements.add("");
if (configuration == LibraryBundle.Configuration.Debug)
lib += "d";
return lib + "%1$s.dll"; // "foobar1.dll"
}
case IOS:
case MacOS:
if(dontUseQtJambiFrameworks) {
switch(version.length) {
default:
replacements.add("."+version[0]);
replacements.add("."+version[0]+"."+version[1]);
replacements.add("."+version[0]+"."+version[1]+"."+version[2]);
replacements.add("");
break;
case 2:
replacements.add("."+version[0]);
replacements.add("."+version[0]+"."+version[1]);
replacements.add("");
break;
case 1:
replacements.add("."+version[0]);
case 0:
replacements.add("");
break;
}
if (configuration == LibraryBundle.Configuration.Debug)
lib += "_debug";
return "lib" + lib + "%1$s.jnilib";
}else {
switch(version.length) {
default:
replacements.add(""+version[0]);
case 0:
replacements.add("6");
break;
}
if (configuration == LibraryBundle.Configuration.Debug)
lib += "_debug";
return lib + ".framework/Versions/%1$s/"+lib;
}
case Android:
if (configuration == LibraryBundle.Configuration.Debug)
switch(architecture) {
case arm:
return lib + "_debug_armeabi-v7a";
case arm64:
return lib + "_debug_arm64-v8a";
case x86_64:
return lib + "_debug_x86_64";
default:
return lib + "_debug_x86";
}
else
switch(architecture) {
case arm:
return lib + "_armeabi-v7a";
case arm64:
return lib + "_arm64-v8a";
case x86_64:
return lib + "_x86_64";
default:
return lib + "_x86";
}
default:
if(operatingSystem.isUnixLike()) {
switch(version.length) {
default:
replacements.add("."+version[0]+"."+version[1]+"."+version[2]);
case 2:
replacements.add("."+version[0]+"."+version[1]);
case 1:
replacements.add("."+version[0]);
case 0:
replacements.add("");
break;
}
if (configuration == LibraryBundle.Configuration.Debug)
return "lib" + lib + "_debug.so%1$s";
else return "lib" + lib + ".so%1$s";
}
break;
}
throw new RuntimeException("Unreachable statement");
}
}
private static String qtLibraryName(String qtprefix, String libraryName, String libInfix, String versionStrg, LibraryBundle.Configuration configuration, List replacements, int... version) {
if(libInfix==null)
libInfix = "";
if(qtprefix==null)
qtprefix = "";
boolean isQt = "Qt".equals(qtprefix);
String qtXlibName;
if(version!=null && version.length>0 && isQt){
qtXlibName = qtprefix + version[0] + libraryName + libInfix;
}else {
qtXlibName = qtprefix + libraryName + libInfix;
}
String prefix = qtXlibName.startsWith("lib") ? "" : "lib";
replacements.clear();
if(useStaticLibs) {
return qtXlibName;
}else {
switch (operatingSystem) {
case Windows:
if(version!=null && version.length>0 && !isQt){
replacements.add(""+version[0]);
replacements.add("");
return configuration == LibraryBundle.Configuration.Debug && !isMinGWBuilt
? qtXlibName + "d%1$s.dll" // "QtFood4.dll"
: qtXlibName + "%1$s.dll"; // "QtFoo4.dll"
}else {
return configuration == LibraryBundle.Configuration.Debug && !isMinGWBuilt
? qtXlibName + "d.dll" // "QtFood4.dll"
: qtXlibName + ".dll"; // "QtFoo4.dll"
}
case IOS:
case MacOS:
if(dontUseQtFrameworks==null || !dontUseQtFrameworks) {
if(version!=null && version.length>0 && version[0]<6)
return String.format("%1$s%2$s.framework/Versions/%3$s/%1$s%2$s", qtprefix, libraryName, version[0]);
else
return String.format("%1$s%2$s.framework/Versions/A/%1$s%2$s", qtprefix, libraryName);
}else {
if(version!=null) {
switch(version.length) {
default:
replacements.add("."+version[0]+"."+version[1]+"."+version[2]);
case 2:
replacements.add("."+version[0]+"."+version[1]);
case 1:
replacements.add("."+version[0]);
case 0:
replacements.add("");
break;
}
}else if(versionStrg!=null && !versionStrg.isEmpty()) {
List parts = new ArrayList<>();
parts.addAll(Arrays.asList(versionStrg.split("\\.")));
while(!parts.isEmpty()) {
replacements.add("."+String.join(".", parts));
parts.remove(parts.size()-1);
}
replacements.add("");
}else {
replacements.add("");
}
if(version!=null && version.length>1 && version[0]>=5 && version[1]>=14) {
return prefix + qtXlibName + "%1$s.dylib";
}else {
return configuration == LibraryBundle.Configuration.Debug
? prefix + qtXlibName + "_debug%1$s.dylib" // "libQtFoo_debug.4.dylib"
: prefix + qtXlibName + "%1$s.dylib"; // "libQtFoo.4.dylib"
}
}
case Android:
switch (architecture) {
case arm:
return qtXlibName + "_armeabi-v7a";
case arm64:
return qtXlibName + "_arm64-v8a";
case x86:
return qtXlibName + "_x86";
case x86_64:
return qtXlibName + "_x86_64";
default:
break;
}
// Linux doesn't have a dedicated "debug" library since 4.2
return prefix + qtXlibName + ".so"; // "libQtFoo.so.4"
default:
if(operatingSystem.isUnixLike()) {
if(version!=null) {
switch(version.length) {
default:
replacements.add("."+version[0]+"."+version[1]+"."+version[2]);
case 2:
replacements.add("."+version[0]+"."+version[1]);
case 1:
replacements.add("."+version[0]);
case 0:
replacements.add("");
break;
}
}else if(versionStrg!=null && !versionStrg.isEmpty()) {
List parts = new ArrayList<>();
parts.addAll(Arrays.asList(versionStrg.split("\\.")));
while(!parts.isEmpty()) {
replacements.add("."+String.join(".", parts));
parts.remove(parts.size()-1);
}
replacements.add("");
}else {
replacements.add("");
}
return prefix + qtXlibName + ".so%1$s";
}
break;
}
}
throw new RuntimeException("Unreachable statement");
}
@NativeAccess
private static boolean isNativeDeployment() {
synchronized(loadedNativeDeploymentUrls) {
return !nativeDeployments.isEmpty();
}
}
private static List mergeJniLibdir(List middle) {
if((jniLibdirBeforeList != null && !jniLibdirBeforeList.isEmpty())
|| (jniLibdirList != null && !jniLibdirList.isEmpty())) {
List newList = new ArrayList();
if(jniLibdirBeforeList != null)
newList.addAll(jniLibdirBeforeList);
newList.addAll(middle);
if(jniLibdirList != null)
newList.addAll(jniLibdirList);
middle = newList;
}
Set set = new TreeSet<>();
for (int i = 0; i < middle.size(); ++i) {
String a = middle.get(i);
if(!set.contains(a))
set.add(a);
else {
middle.remove(i);
--i;
}
}
return middle;
}
static void loadSystemLibraries() {
if (systemLibrariesList != null) {
for (String s : systemLibrariesList) {
LibraryUtility.loadSystemLibrary(s);
}
}
}
static String qtJambiLibraryPath() {
synchronized(loadedNativeDeploymentUrls) {
return qtJambiLibraryPath;
}
}
static String qtLibraryPath() {
return qtLibraryPath;
}
static String osArchName() {
return osArchName;
}
private static int findSequence(int offset, byte[] array, byte[] sequence) {
for (int i = offset; i < array.length - sequence.length + 1; i++) {
boolean found = true;
for (int j = 0; j < sequence.length; j++) {
if (array[i + j] != sequence[j]) {
found = false;
break;
}
}
if (found) {
return i;
}
}
return -1;
}
private static void analyzeUnsatisfiedLinkError(UnsatisfiedLinkError t, java.io.File coreLib, boolean atCoreLoad) {
final String architecture;
if(LibraryUtility.architecture==Architecture.x86) {
architecture = "i386-"+ByteOrder.nativeOrder().toString().toLowerCase();
}else {
architecture = LibraryUtility.architecture.name()+"-"+ByteOrder.nativeOrder().toString().toLowerCase();
}
final boolean canUniversalBuild = LibraryUtility.operatingSystem==OperatingSystem.MacOS
|| LibraryUtility.operatingSystem==OperatingSystem.IOS;
final List qtLibraryBuilds = new ArrayList<>();
final List qtjambiLibraryBuilds = new ArrayList<>();
try {
byte[] coredata = Files.readAllBytes(coreLib.toPath());
int idx = 0;
while(true) {
idx = findSequence(idx, coredata, String.format("Qt %1$s.%2$s.", QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion).getBytes(StandardCharsets.US_ASCII));
if(idx<0)
break;
ByteArrayOutputStream os = new ByteArrayOutputStream();
int parenCount = 0;
for(int i=idx; i':
case (byte)'-':
case (byte)'_':
case (byte)' ':
case (byte)';':
case (byte)'.':
os.write(c);
break;
default:
if(Character.isLetterOrDigit(c)) {
os.write(c);
}else {
i = coredata.length;
}
break;
}
}
String qtLibraryBuild = new String(os.toByteArray(), StandardCharsets.US_ASCII);
if(qtLibraryBuild.contains("build; by")) {
qtLibraryBuilds.add(qtLibraryBuild);
if(!canUniversalBuild)
break;
}
idx += 5;
}
}catch(Throwable t2){}
if(!atCoreLoad){
Availability availability = getQtJambiLibraryAvailability(null, "QtJambi", null, null, configuration, QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion, QtJambi_LibraryUtilities.qtJambiPatch);
if(availability.file!=null) {
try {
byte[] jambidata = Files.readAllBytes(availability.file.toPath());
int idx = 0;
while(true) {
idx = findSequence(idx, jambidata, String.format("QtJambi %1$s.%2$s.", QtJambi_LibraryUtilities.qtMajorVersion, QtJambi_LibraryUtilities.qtMinorVersion).getBytes(StandardCharsets.US_ASCII));
if(idx<0)
break;
ByteArrayOutputStream os = new ByteArrayOutputStream();
int parenCount = 0;
for(int i=idx; i':
case (byte)'-':
case (byte)'_':
case (byte)' ':
case (byte)';':
case (byte)'.':
os.write(c);
break;
default:
if(Character.isLetterOrDigit(c)) {
os.write(c);
}else {
i = jambidata.length;
}
break;
}
}
String qtjambiLibraryBuild = new String(os.toByteArray(), StandardCharsets.US_ASCII);
if(qtjambiLibraryBuild.contains("build; by")) {
qtjambiLibraryBuilds.add(qtjambiLibraryBuild);
if(!canUniversalBuild)
break;
}
idx += 10;
}
}catch(Throwable t2){}
}
}else { // error when loading QtCore:
String qtLibraryBuild = null;
switch(qtLibraryBuilds.size()) {
case 0: throw t;
case 1:
qtLibraryBuild = qtLibraryBuilds.get(0);
if(qtLibraryBuild.contains(architecture)) {
throw t;
}
throw new LinkageError("Cannot load " + qtLibraryBuild + " on " + architecture + " architecture.", t);
default:
for(String s : qtLibraryBuilds) {
if(s.contains(architecture)) {
qtLibraryBuild = s;
break;
}
}
if(qtLibraryBuild==null) {
throw new LinkageError("Cannot load one of " + qtLibraryBuilds + " on " + architecture + " architecture.", t);
}else {
throw new LinkageError("Cannot load " + qtLibraryBuild + " on " + architecture + " architecture.", t);
}
}
}
if(!qtLibraryBuilds.isEmpty() && !qtjambiLibraryBuilds.isEmpty()) {
String qtLibraryBuild = qtLibraryBuilds.get(0);
String qtjambiLibraryBuild = qtjambiLibraryBuilds.get(0);
switch(LibraryUtility.operatingSystem) {
case Windows:
if(LibraryUtility.architecture==Architecture.x86_64 && qtjambiLibraryBuild.contains(architecture)) {
if(qtjambiLibraryBuild.contains("MSVC") && qtLibraryBuild.contains("GCC")) {
throw new LinkageError("Cannot run " + qtjambiLibraryBuild + " with " + qtLibraryBuild + ". Please install and use Qt (MSVC x64) instead.", t);
}else if(qtjambiLibraryBuild.contains("GCC") && qtLibraryBuild.contains("MSVC")){
throw new LinkageError("Cannot run " + qtjambiLibraryBuild + " with " + qtLibraryBuild + ". Please install and use Qt (MinGW x64) instead.", t);
}
}else if(LibraryUtility.architecture==Architecture.x86 && qtjambiLibraryBuild.contains(architecture)) {
if(qtjambiLibraryBuild.contains("MSVC") && qtLibraryBuild.contains("GCC")) {
throw new LinkageError("Cannot run " + qtjambiLibraryBuild + " with " + qtLibraryBuild + ". Please install and use Qt (MSVC x86) instead.", t);
}else if(qtjambiLibraryBuild.contains("GCC") && qtLibraryBuild.contains("MSVC")){
throw new LinkageError("Cannot run " + qtjambiLibraryBuild + " with " + qtLibraryBuild + ". Please install and use Qt (MinGW x86) instead.", t);
}
}
default:
if(qtLibraryBuilds.size()==1) {
if(qtjambiLibraryBuilds.size()==1) {
throw new LinkageError("Cannot run " + qtjambiLibraryBuild + " with " + qtLibraryBuild + ".", t);
}else {
throw new LinkageError("Cannot run one of " + qtjambiLibraryBuilds + " with " + qtLibraryBuild + ".", t);
}
}else {
if(qtjambiLibraryBuilds.size()==1) {
throw new LinkageError("Cannot run " + qtjambiLibraryBuild + " with one of " + qtLibraryBuilds + ".", t);
}else {
throw new LinkageError("Cannot run one of " + qtjambiLibraryBuilds + " with one of " + qtLibraryBuilds + ".", t);
}
}
}
}else {
switch(LibraryUtility.operatingSystem) {
case MacOS:
if(coreLib!=null) {
java.io.File prl = new java.io.File(coreLib.getParentFile(), "Resources/QtCore.prl");
if(prl.exists()) {
Properties prlProp = new Properties();
try(java.io.FileInputStream inStream = new java.io.FileInputStream(prl)){
prlProp.load(inStream);
} catch(Throwable t2) {}
String version = prlProp.getProperty("QMAKE_PRL_VERSION", "");
if(!version.isEmpty()) {
if(!version.startsWith(QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + ".")) {
throw new LinkageError("Cannot combine QtJambi " + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + " with Qt " + version + ".", t);
}
}
}
}
break;
case Windows:
if(coreLib!=null) {
java.io.File prl = new java.io.File(coreLib.getParentFile(), "Qt"+QtJambi_LibraryUtilities.qtMajorVersion+"Core.prl");
if(!prl.exists()) {
prl = new java.io.File(coreLib.getParentFile().getParentFile(), "lib\\Qt"+QtJambi_LibraryUtilities.qtMajorVersion+"Core.prl");
}
if(prl.exists()) {
Properties prlProp = new Properties();
try(java.io.FileInputStream inStream = new java.io.FileInputStream(prl)){
prlProp.load(inStream);
} catch(Throwable t2) {}
String version = prlProp.getProperty("QMAKE_PRL_VERSION", "");
if(!version.isEmpty()) {
if(!version.startsWith(QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + ".")) {
throw new LinkageError("Cannot combine QtJambi " + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + " with Qt " + version + ".", t);
}
}
}
if(new java.io.File(coreLib.getParentFile(), "libstdc++-6.dll").exists() || LibraryUtility.isMinGWBuilt()) {
throw new LinkageError("Cannot combine msvc-based QtJambi with mingw-based Qt library. Please install and use Qt (MSVC x64) instead.", t);
}else {
throw new LinkageError("Cannot combine mingw-based QtJambi with msvc-based Qt library. Please install and use Qt (MinGW x64) instead.", t);
}
}
break;
default:
if(coreLib!=null) {
java.io.File prl = new java.io.File(coreLib.getParentFile(), "Qt"+QtJambi_LibraryUtilities.qtMajorVersion+"Core.prl");
if(prl.exists()) {
Properties prlProp = new Properties();
try(java.io.FileInputStream inStream = new java.io.FileInputStream(prl)){
prlProp.load(inStream);
} catch(Throwable t2) {}
String version = prlProp.getProperty("QMAKE_PRL_VERSION", "");
if(!version.isEmpty()) {
if(!version.startsWith(QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + ".")) {
throw new LinkageError("Cannot combine QtJambi " + QtJambi_LibraryUtilities.qtMajorVersion + "." + QtJambi_LibraryUtilities.qtMinorVersion + " with Qt " + version + ".", t);
}
}
}
}
break;
}
}
throw t;
}
}