diff options
author | Alexandru Croitor <alexandru.croitor@qt.io> | 2017-11-09 18:00:46 +0100 |
---|---|---|
committer | Joerg Bornemann <joerg.bornemann@qt.io> | 2019-09-07 20:02:45 +0200 |
commit | 4ac872639ed0dd3ae6627e05bdda821f7d128500 (patch) | |
tree | fad45ce520ed8a394c99074505fed54591f68dca /src/corelib | |
parent | 9a0ef07f154676cfc3b54b2b1042233b27fe8de9 (diff) |
Make Qt relocatable
[ChangeLog][QtCore] Qt installations on the host system can now be
relocated, i.e. moved to other directories.
Add a new feature 'relocatable' that's by default enabled for
non-static builds
- on platforms where libdl is available,
- on macOS when configured with -framework,
- on Windows.
If the feature is enabled, the directory where plugins, translations
and other assets are loaded from is determined by the location of
libQt5Core.so and the lib dir (bin dir on Windows) relative to the
prefix.
For static builds, the feature 'relocatable' is off by default. It can
be turned on manually by passing -feature-relocatable to configure. In
that case, QLibraryInfo::location(QLibraryInfo::TranslationsPaths) and
friends will return paths rooted in the user application's directory.
The installed and relocated qmake determines properties like
QT_INSTALL_PREFIX and QT_HOST_PREFIX from the location of the qmake
executable and the host bin dir relative to the host prefix. This is
now always done, independent of the 'relocatable' feature.
Note that qmake is currently only relocatable within an environment
that has the same layout as the original build machine due to absolute
paths to the original prefix in .prl, .pc and .la files.
This will be addressed in a separate patch.
Task-number: QTBUG-15234
Change-Id: I7319e2856d8fe17f277082d71216442f52580633
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'src/corelib')
-rw-r--r-- | src/corelib/configure.json | 20 | ||||
-rw-r--r-- | src/corelib/global/qlibraryinfo.cpp | 208 |
2 files changed, 175 insertions, 53 deletions
diff --git a/src/corelib/configure.json b/src/corelib/configure.json index f7b6d41534..ae360239c6 100644 --- a/src/corelib/configure.json +++ b/src/corelib/configure.json @@ -158,21 +158,6 @@ "-latomic" ] }, - "libdl": { - "label": "dlopen()", - "test": { - "main": [ - "dlclose(dlopen(0, 0));", - "dlsym(RTLD_DEFAULT, 0);", - "dlerror();" - ] - }, - "headers": "dlfcn.h", - "sources": [ - "", - "-ldl" - ] - }, "librt": { "label": "clock_gettime()", "test": { @@ -612,11 +597,6 @@ "condition": "features.clock-gettime && tests.clock-monotonic", "output": [ "feature" ] }, - "dlopen": { - "label": "dlopen()", - "condition": "config.unix && libs.libdl", - "output": [ "privateFeature" ] - }, "doubleconversion": { "label": "DoubleConversion", "output": [ "privateFeature", "feature" ] diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp index 4c1b04f05e..c7357f9d13 100644 --- a/src/corelib/global/qlibraryinfo.cpp +++ b/src/corelib/global/qlibraryinfo.cpp @@ -55,15 +55,24 @@ QT_END_NAMESPACE # include "qcoreapplication.h" #endif +#ifndef QT_BUILD_QMAKE_BOOTSTRAP +# include "private/qglobal_p.h" +# include "qconfig.cpp" +#endif + #ifdef Q_OS_DARWIN # include "private/qcore_mac_p.h" -#endif +#endif // Q_OS_DARWIN -#ifndef QT_BUILD_QMAKE_BOOTSTRAP -# include "qconfig.cpp" +#include "archdetect.cpp" + +#if !defined(QT_BUILD_QMAKE) && QT_CONFIG(relocatable) && QT_CONFIG(dlopen) && !QT_CONFIG(framework) +# include <dlfcn.h> #endif -#include "archdetect.cpp" +#if !defined(QT_BUILD_QMAKE) && QT_CONFIG(relocatable) && defined(Q_OS_WIN) +# include <qt_windows.h> +#endif QT_BEGIN_NAMESPACE @@ -453,6 +462,160 @@ void QLibraryInfo::sysrootify(QString *path) } #endif // QT_BUILD_QMAKE +#ifndef QT_BUILD_QMAKE +static QString prefixFromAppDirHelper() +{ + QString appDir; + + if (QCoreApplication::instance()) { +#ifdef Q_OS_DARWIN + CFBundleRef bundleRef = CFBundleGetMainBundle(); + if (bundleRef) { + QCFType<CFURLRef> urlRef = CFBundleCopyBundleURL(bundleRef); + if (urlRef) { + QCFString path = CFURLCopyFileSystemPath(urlRef, kCFURLPOSIXPathStyle); +#ifdef Q_OS_MACOS + QString bundleContentsDir = QString(path) + QLatin1String("/Contents/"); + if (QDir(bundleContentsDir).exists()) + return QDir::cleanPath(bundleContentsDir); +#else + return QDir::cleanPath(QString(path)); // iOS +#endif // Q_OS_MACOS + } + } +#endif // Q_OS_DARWIN + // We make the prefix path absolute to the executable's directory. + appDir = QCoreApplication::applicationDirPath(); + } else { + appDir = QDir::currentPath(); + } + + return appDir; +} +#endif + +#if !defined(QT_BUILD_QMAKE) && QT_CONFIG(relocatable) +static QString prefixFromQtCoreLibraryHelper(const QString &qtCoreLibraryPath) +{ + const QString qtCoreLibrary = QDir::fromNativeSeparators(qtCoreLibraryPath); + const QString libDir = QFileInfo(qtCoreLibrary).absolutePath(); + const QString prefixDir = libDir + QLatin1Char('/') + + QLatin1String(QT_CONFIGURE_LIBLOCATION_TO_PREFIX_PATH); + return QDir::cleanPath(prefixDir); +} + +#if defined(Q_OS_WIN) +#if defined(Q_OS_WINRT) +EXTERN_C IMAGE_DOS_HEADER __ImageBase; +static HMODULE getWindowsModuleHandle() +{ + return reinterpret_cast<HMODULE>(&__ImageBase); +} +#else // Q_OS_WINRT +static HMODULE getWindowsModuleHandle() +{ + HMODULE hModule = NULL; + GetModuleHandleEx( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCTSTR)&QLibraryInfo::isDebugBuild, &hModule); + return hModule; +} +#endif // !Q_OS_WINRT +#endif // Q_OS_WIN + +static QString getRelocatablePrefix() +{ + QString prefixPath; + + // For static builds, the prefix will be the app directory. + // For regular builds, the prefix will be relative to the location of the QtCore shared library. +#if defined(QT_STATIC) + prefixPath = prefixFromAppDirHelper(); +#elif defined(Q_OS_DARWIN) && QT_CONFIG(framework) + CFBundleRef qtCoreBundle = CFBundleGetBundleWithIdentifier( + CFSTR("org.qt-project.QtCore")); + Q_ASSERT(qtCoreBundle); + + QCFType<CFURLRef> qtCorePath = CFBundleCopyBundleURL(qtCoreBundle); + Q_ASSERT(qtCorePath); + + QCFType<CFURLRef> qtCorePathAbsolute = CFURLCopyAbsoluteURL(qtCorePath); + Q_ASSERT(qtCorePathAbsolute); + + QCFType<CFURLRef> libDirCFPath = CFURLCreateCopyDeletingLastPathComponent(NULL, qtCorePathAbsolute); + + const QCFString libDirCFString = CFURLCopyFileSystemPath(libDirCFPath, kCFURLPOSIXPathStyle); + + const QString prefixDir = QString(libDirCFString) + QLatin1Char('/') + + QLatin1String(QT_CONFIGURE_LIBLOCATION_TO_PREFIX_PATH); + + prefixPath = QDir::cleanPath(prefixDir); +#elif QT_CONFIG(dlopen) + Dl_info info; + int result = dladdr(reinterpret_cast<void *>(&QLibraryInfo::isDebugBuild), &info); + if (result > 0 && info.dli_fname) + prefixPath = prefixFromQtCoreLibraryHelper(QString::fromLatin1(info.dli_fname)); +#elif defined(Q_OS_WIN) + HMODULE hModule = getWindowsModuleHandle(); + const int kBufferSize = 4096; + wchar_t buffer[kBufferSize]; + const int pathSize = GetModuleFileName(hModule, buffer, kBufferSize); + if (pathSize > 0) + prefixPath = prefixFromQtCoreLibraryHelper(QString::fromWCharArray(buffer, pathSize)); +#else +#error "The chosen platform / config does not support querying for a dynamic prefix." +#endif + + Q_ASSERT_X(!prefixPath.isEmpty(), "getRelocatablePrefix", + "Failed to find the Qt prefix path."); + return prefixPath; +} +#endif + +#if defined(QT_BUILD_QMAKE) && !defined(QT_BUILD_QMAKE_BOOTSTRAP) +QString qmake_abslocation(); + +static QString getPrefixFromHostBinDir(const char *hostBinDirToPrefixPath) +{ + const QFileInfo qmfi = QFileInfo(qmake_abslocation()).canonicalFilePath(); + return QDir::cleanPath(qmfi.absolutePath() + QLatin1Char('/') + + QLatin1String(hostBinDirToPrefixPath)); +} + +static QString getExtPrefixFromHostBinDir() +{ + return getPrefixFromHostBinDir(QT_CONFIGURE_HOSTBINDIR_TO_EXTPREFIX_PATH); +} + +static QString getHostPrefixFromHostBinDir() +{ + return getPrefixFromHostBinDir(QT_CONFIGURE_HOSTBINDIR_TO_HOSTPREFIX_PATH); +} +#endif + +#ifndef QT_BUILD_QMAKE_BOOTSTRAP +static const char *getPrefix( +#ifdef QT_BUILD_QMAKE + QLibraryInfo::PathGroup group +#endif + ) +{ +#if defined(QT_BUILD_QMAKE) +# if QT_CONFIGURE_CROSSBUILD + if (group == QLibraryInfo::DevicePaths) + return QT_CONFIGURE_PREFIX_PATH; +# endif + static QByteArray extPrefixPath = getExtPrefixFromHostBinDir().toLatin1(); + return extPrefixPath.constData(); +#elif QT_CONFIG(relocatable) + static QByteArray prefixPath = getRelocatablePrefix().toLatin1(); + return prefixPath.constData(); +#else + return QT_CONFIGURE_PREFIX_PATH; +#endif +} +#endif // QT_BUILD_QMAKE_BOOTSTRAP + /*! Returns the location specified by \a loc. */ @@ -564,12 +727,11 @@ QLibraryInfo::rawLocation(LibraryLocation loc, PathGroup group) if (!fromConf) { const char * volatile path = 0; if (loc == PrefixPath) { - path = -# ifdef QT_BUILD_QMAKE - (group != DevicePaths) ? - QT_CONFIGURE_EXT_PREFIX_PATH : -# endif - QT_CONFIGURE_PREFIX_PATH; + path = getPrefix( +#ifdef QT_BUILD_QMAKE + group +#endif + ); } else if (unsigned(loc) <= sizeof(qt_configure_str_offsets)/sizeof(qt_configure_str_offsets[0])) { path = qt_configure_strs + qt_configure_str_offsets[loc - 1]; #ifndef Q_OS_WIN // On Windows we use the registry @@ -578,7 +740,8 @@ QLibraryInfo::rawLocation(LibraryLocation loc, PathGroup group) #endif # ifdef QT_BUILD_QMAKE } else if (loc == HostPrefixPath) { - path = QT_CONFIGURE_HOST_PREFIX_PATH; + static const QByteArray hostPrefixPath = getHostPrefixFromHostBinDir().toLatin1(); + path = hostPrefixPath.constData(); # endif } @@ -612,28 +775,7 @@ QLibraryInfo::rawLocation(LibraryLocation loc, PathGroup group) } #else if (loc == PrefixPath) { - if (QCoreApplication::instance()) { -#ifdef Q_OS_DARWIN - CFBundleRef bundleRef = CFBundleGetMainBundle(); - if (bundleRef) { - QCFType<CFURLRef> urlRef = CFBundleCopyBundleURL(bundleRef); - if (urlRef) { - QCFString path = CFURLCopyFileSystemPath(urlRef, kCFURLPOSIXPathStyle); -#ifdef Q_OS_OSX - QString bundleContentsDir = QString(path) + QLatin1String("/Contents/"); - if (QDir(bundleContentsDir).exists()) - return QDir::cleanPath(bundleContentsDir + ret); -#else - return QDir::cleanPath(QString(path) + QLatin1Char('/') + ret); // iOS -#endif // Q_OS_OSX - } - } -#endif // Q_OS_DARWIN - // We make the prefix path absolute to the executable's directory. - baseDir = QCoreApplication::applicationDirPath(); - } else { - baseDir = QDir::currentPath(); - } + baseDir = prefixFromAppDirHelper(); } else { // we make any other path absolute to the prefix directory baseDir = location(PrefixPath); |