From 72aa77ced7a0ac9bc9a9c6953ab31508f6b5e186 Mon Sep 17 00:00:00 2001 From: Alessandro Portale Date: Wed, 11 May 2022 13:26:57 +0200 Subject: [PATCH] QtSupport: Add support to register Qt versions via qtpaths qmake is a "build tool", and it is also a "query tool" when called with parameter "-query". Qt Creator, so far, assumes that building and querying with a Qt installation are done with one and the same tool: qmake. This change adds the ability to register a Qt version vie either qmake or qtpaths and still build with qmake, if that is installed (which is not anymore mandatory from Qt 6 on). 1) Distinguish between Qt query tool and qmake build tool: Add QtVersion::queryToolFilePath() to the existing QtVersion::qmakeFilePath(), and use queryToolFilePath in most "query" related code, and qmakeFilePath when building with qmake (e.g. in QmakeProjectManager). Also, a couple of functions and variables were renamed from *qmake* to *queryTool* in order to express that the affected code is about querying/managing Qt versions rather than about building with qmake. 2) Support manual Qt Version adding by qtpaths via file dialog This change adds qtpaths to the "Add" Qt Version file picker filter. After selection, "qtpaths -query" is executed for testing purposes. If that fails, (e.g. because it is an older Qt version), qmake is instead chosen, silently. Task-number: QTCREATORBUG-22175 Task-number: QTCREATORBUG-25546 Change-Id: I4d9c1e7eec7d5ae7c5a8d2e1a1ed95addff69966 Reviewed-by: Reviewed-by: hjk Reviewed-by: Qt CI Bot --- share/qtcreator/translations/qtcreator_cs.ts | 8 +- share/qtcreator/translations/qtcreator_da.ts | 8 +- share/qtcreator/translations/qtcreator_de.ts | 8 +- share/qtcreator/translations/qtcreator_fr.ts | 8 +- share/qtcreator/translations/qtcreator_hr.ts | 4 +- share/qtcreator/translations/qtcreator_ja.ts | 8 +- share/qtcreator/translations/qtcreator_pl.ts | 8 +- share/qtcreator/translations/qtcreator_ru.ts | 8 +- share/qtcreator/translations/qtcreator_sl.ts | 8 +- share/qtcreator/translations/qtcreator_uk.ts | 8 +- .../qtcreator/translations/qtcreator_zh_CN.ts | 8 +- .../qtcreator/translations/qtcreator_zh_TW.ts | 8 +- src/libs/utils/buildablehelperlibrary.cpp | 192 +++++++++--------- src/libs/utils/buildablehelperlibrary.h | 11 +- .../cmakekitinformation.cpp | 2 +- src/plugins/docker/kitdetector.cpp | 10 +- .../qbsprojectmanager/qbsprofilemanager.cpp | 2 +- .../qmakeprojectmanager/qmakeproject.cpp | 3 + src/plugins/qmakeprojectmanager/qmakestep.cpp | 8 +- .../designercore/instances/puppetcreator.cpp | 2 +- src/plugins/qtsupport/baseqtversion.cpp | 144 +++++++------ src/plugins/qtsupport/baseqtversion.h | 2 + src/plugins/qtsupport/qtkitinformation.cpp | 5 + src/plugins/qtsupport/qtoptionspage.cpp | 71 ++++--- src/plugins/qtsupport/qtprojectimporter.cpp | 10 +- src/plugins/qtsupport/qtversionfactory.h | 2 +- src/plugins/qtsupport/qtversioninfo.ui | 4 +- src/plugins/qtsupport/qtversionmanager.cpp | 45 ++-- 28 files changed, 338 insertions(+), 267 deletions(-) diff --git a/share/qtcreator/translations/qtcreator_cs.ts b/share/qtcreator/translations/qtcreator_cs.ts index b7e0efc99e3..086b2dfea0b 100644 --- a/share/qtcreator/translations/qtcreator_cs.ts +++ b/share/qtcreator/translations/qtcreator_cs.ts @@ -32121,16 +32121,16 @@ Spustil jste Qemu? Qt %1 (%2) - qmake does not exist or is not executable - Soubor qmake neexistuje nebo není spustitelný + %1 does not exist or is not executable + Soubor %1 neexistuje nebo není spustitelný Qt version is not properly installed, please run make install Verze Qt není správně nainstalována. Proveďte, prosím, příkaz "make install" - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Cestu ke spustitelným souborům instalace Qt se nepodařilo určit. Možná je cesta k qmake chybná? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Cestu ke spustitelným souborům instalace Qt se nepodařilo určit. Možná je cesta k %1 chybná? The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_da.ts b/share/qtcreator/translations/qtcreator_da.ts index 4cfd3841b60..21dd2fad82d 100644 --- a/share/qtcreator/translations/qtcreator_da.ts +++ b/share/qtcreator/translations/qtcreator_da.ts @@ -35382,16 +35382,16 @@ For flere detaljer, se /etc/sysctl.d/10-ptrace.conf Ingen qmake-sti sat - qmake does not exist or is not executable - qmake findes ikke eller er ikke eksekverbar + %1 does not exist or is not executable + %1 findes ikke eller er ikke eksekverbar Qt version is not properly installed, please run make install Qt version er ikke ordentligt installeret, kør venligst make install - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Kunne ikke beslutte stien til binærene af Qt installationen, måske er qmake-stien forkert? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Kunne ikke beslutte stien til binærene af Qt installationen, måske er %1-stien forkert? The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_de.ts b/share/qtcreator/translations/qtcreator_de.ts index 8013bb9b20e..49d5e966da9 100644 --- a/share/qtcreator/translations/qtcreator_de.ts +++ b/share/qtcreator/translations/qtcreator_de.ts @@ -10113,8 +10113,8 @@ Dies ist unabhängig vom Wert der Eigenschaft "visible" in QML.Es ist keine qmake-Pfad gesetzt - qmake does not exist or is not executable - Die qmake-Datei existiert nicht oder ist nicht ausführbar + %1 does not exist or is not executable + Die %1-Datei existiert nicht oder ist nicht ausführbar Qt version has no name @@ -10141,8 +10141,8 @@ Dies ist unabhängig vom Wert der Eigenschaft "visible" in QML.Die Qt-Version ist nicht richtig installiert, führen Sie bitte make install aus - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Der Pfad zu den ausführbaren Dateien der Qt-Installation konnte nicht bestimmt werden, möglicherweise ist der Pfad zu qmake falsch? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Der Pfad zu den ausführbaren Dateien der Qt-Installation konnte nicht bestimmt werden, möglicherweise ist der Pfad zu %1 falsch? The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_fr.ts b/share/qtcreator/translations/qtcreator_fr.ts index ec42c365792..2e632c21390 100644 --- a/share/qtcreator/translations/qtcreator_fr.ts +++ b/share/qtcreator/translations/qtcreator_fr.ts @@ -33604,8 +33604,8 @@ Nous allons essayer de travailler avec cela mais vous pourrez rencontrer des pro Chemin de qmake non spécifié - qmake does not exist or is not executable - qmake n'existe pas ou n'est pas exécutable + %1 does not exist or is not executable + %1 n'existe pas ou n'est pas exécutable Qt version has no name @@ -33633,8 +33633,8 @@ Nous allons essayer de travailler avec cela mais vous pourrez rencontrer des pro La version de Qt n'est pas correctement installée, veuillez exécuter make install - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Impossible de déterminer le chemin vers les programmes de Qt, peut-être que le chemin vers qmake est faux ? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Impossible de déterminer le chemin vers les programmes de Qt, peut-être que le chemin vers %1 est faux ? The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_hr.ts b/share/qtcreator/translations/qtcreator_hr.ts index e7a14f06a9c..c60ce6f3c59 100644 --- a/share/qtcreator/translations/qtcreator_hr.ts +++ b/share/qtcreator/translations/qtcreator_hr.ts @@ -41628,7 +41628,7 @@ Saving failed. - qmake does not exist or is not executable + %1 does not exist or is not executable @@ -41636,7 +41636,7 @@ Saving failed. - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? diff --git a/share/qtcreator/translations/qtcreator_ja.ts b/share/qtcreator/translations/qtcreator_ja.ts index c65ccab4138..017e3ebc611 100644 --- a/share/qtcreator/translations/qtcreator_ja.ts +++ b/share/qtcreator/translations/qtcreator_ja.ts @@ -26108,16 +26108,16 @@ Do you want to save the data first? qmake のパスが設定されていません - qmake does not exist or is not executable - qmake が存在しないか実行可能ではありません + %1 does not exist or is not executable + %1 が存在しないか実行可能ではありません Qt version is not properly installed, please run make install Qt バージョンが正しくインストールされていません。make install を実行してください - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Qt インストール先のパスが特定できませんでした。qmake のパスが間違っていませんか? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Qt インストール先のパスが特定できませんでした。%1 のパスが間違っていませんか? The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_pl.ts b/share/qtcreator/translations/qtcreator_pl.ts index 4bd2dfddc49..288c0333976 100644 --- a/share/qtcreator/translations/qtcreator_pl.ts +++ b/share/qtcreator/translations/qtcreator_pl.ts @@ -12382,8 +12382,8 @@ Dla projektów CMake, upewnij się, że zmienna QML_IMPORT_PATH jest obecna w CM Nie ustawiono ścieżki do qmake - qmake does not exist or is not executable - Brak qmake lub nie jest on plikiem wykonywalnym + %1 does not exist or is not executable + Brak %1 lub nie jest on plikiem wykonywalnym Qt version has no name @@ -12410,8 +12410,8 @@ Dla projektów CMake, upewnij się, że zmienna QML_IMPORT_PATH jest obecna w CM Wersja Qt zainstalowana niepoprawnie, uruchom komendę: make install - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Nie można określić ścieżki do plików binarnych instalacji Qt. Sprawdź ścieżkę do qmake. + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Nie można określić ścieżki do plików binarnych instalacji Qt. Sprawdź ścieżkę do %1. The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_ru.ts b/share/qtcreator/translations/qtcreator_ru.ts index ca1e07984e3..038c8b7d562 100644 --- a/share/qtcreator/translations/qtcreator_ru.ts +++ b/share/qtcreator/translations/qtcreator_ru.ts @@ -45319,8 +45319,8 @@ For more details, see /etc/sysctl.d/10-ptrace.conf Путь к qmake не указан - qmake does not exist or is not executable - qmake отсутствует или не запускается + %1 does not exist or is not executable + %1 отсутствует или не запускается Qt version has no name @@ -45347,8 +45347,8 @@ For more details, see /etc/sysctl.d/10-ptrace.conf Профиль Qt не установлен, пожалуйста выполните make install - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Не удалось определить путь к утилитам Qt. Может путь к qmake неверен? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Не удалось определить путь к утилитам Qt. Может путь к %1 неверен? The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_sl.ts b/share/qtcreator/translations/qtcreator_sl.ts index ca8f7d46bb3..ed9bf70dfb4 100644 --- a/share/qtcreator/translations/qtcreator_sl.ts +++ b/share/qtcreator/translations/qtcreator_sl.ts @@ -21803,8 +21803,8 @@ Projekte programov QML izvede pregledovalnik QML in jih ni potrebno zgraditi. - qmake does not exist or is not executable - Datoteka »qmake« ne obstaja ali pa ni izvršljiva + %1 does not exist or is not executable + Datoteka »%1« ne obstaja ali pa ni izvršljiva @@ -21813,8 +21813,8 @@ Projekte programov QML izvede pregledovalnik QML in jih ni potrebno zgraditi. - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Ni bilo moč ugotoviti poti do programov namestitve Qt. Morda je pot do qmake napačna? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Ni bilo moč ugotoviti poti do programov namestitve Qt. Morda je pot do %1 napačna? diff --git a/share/qtcreator/translations/qtcreator_uk.ts b/share/qtcreator/translations/qtcreator_uk.ts index 7ed2f44691f..b3c68dd579c 100644 --- a/share/qtcreator/translations/qtcreator_uk.ts +++ b/share/qtcreator/translations/qtcreator_uk.ts @@ -22711,8 +22711,8 @@ For more details, see /etc/sysctl.d/10-ptrace.conf Шлях до qmake не задано - qmake does not exist or is not executable - qmake не існує або не є виконуваним модулем + %1 does not exist or is not executable + %1 не існує або не є виконуваним модулем ABI detection failed: Make sure to use a matching compiler when building. @@ -22763,8 +22763,8 @@ For more details, see /etc/sysctl.d/10-ptrace.conf Версія Qt не встановлена як слід, будь ласка, запустіть make install - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - Не вдалось визначити шлях до виконуваних модулів встановлення Qt, можливо, шлях до qmake помилковий? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + Не вдалось визначити шлях до виконуваних модулів встановлення Qt, можливо, шлях до %1 помилковий? The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_zh_CN.ts b/share/qtcreator/translations/qtcreator_zh_CN.ts index 8e6d46bd95c..64fa094ec3c 100644 --- a/share/qtcreator/translations/qtcreator_zh_CN.ts +++ b/share/qtcreator/translations/qtcreator_zh_CN.ts @@ -27948,8 +27948,8 @@ Did you start Qemu? 没有设置qmake路径 - qmake does not exist or is not executable - qmake不存在或者不可执行 + %1 does not exist or is not executable + %1不存在或者不可执行 Qt version has no name @@ -27976,8 +27976,8 @@ Did you start Qemu? Qt没有被正确安装,请运行make install - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - 无法确定Qt安装的二进制所在的路径,或许qmake的路径设置出现了错误? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + 无法确定Qt安装的二进制所在的路径,或许%1的路径设置出现了错误? The default mkspec symlink is broken. diff --git a/share/qtcreator/translations/qtcreator_zh_TW.ts b/share/qtcreator/translations/qtcreator_zh_TW.ts index 253df6a4499..3aa3e0d04ff 100644 --- a/share/qtcreator/translations/qtcreator_zh_TW.ts +++ b/share/qtcreator/translations/qtcreator_zh_TW.ts @@ -15636,8 +15636,8 @@ Requires <b>Qt 4.7.4</b> or newer. 沒有設定 qmake 路徑 - qmake does not exist or is not executable - qmake 不存在或無法執行 + %1 does not exist or is not executable + %1 不存在或無法執行 Qt version has no name @@ -15664,8 +15664,8 @@ Requires <b>Qt 4.7.4</b> or newer. Qt 沒有被正確安裝,請執行 make install - Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong? - 無法決定 Qt 安裝版的路徑。也許是 qmake 的路徑設定有錯誤? + Could not determine the path to the binaries of the Qt installation, maybe the %1 path is wrong? + 無法決定 Qt 安裝版的路徑。也許是 %1 的路徑設定有錯誤? The default mkspec symlink is broken. diff --git a/src/libs/utils/buildablehelperlibrary.cpp b/src/libs/utils/buildablehelperlibrary.cpp index c1030fd5e78..0ad53e52a73 100644 --- a/src/libs/utils/buildablehelperlibrary.cpp +++ b/src/libs/utils/buildablehelperlibrary.cpp @@ -41,7 +41,61 @@ bool BuildableHelperLibrary::isQtChooser(const FilePath &filePath) return filePath.symLinkTarget().endsWith("/qtchooser"); } -FilePath BuildableHelperLibrary::qtChooserToQmakePath(const FilePath &qtChooser) +static const QStringList &queryToolNames() +{ + static const QStringList names = {"qmake", "qtpaths"}; + return names; +} + +static bool isQueryTool(FilePath path) +{ + if (path.isEmpty()) + return false; + if (BuildableHelperLibrary::isQtChooser(path)) + path = BuildableHelperLibrary::qtChooserToQueryToolPath(path.symLinkTarget()); + if (!path.exists()) + return false; + QtcProcess proc; + proc.setCommand({path, {"-query"}}); + proc.runBlocking(); + if (proc.result() != ProcessResult::FinishedWithSuccess) + return false; + const QString output = proc.stdOut(); + // Exemplary output of "[qmake|qtpaths] -query": + // QT_SYSROOT:... + // QT_INSTALL_PREFIX:... + // [...] + // QT_VERSION:6.4.0 + return output.contains("QT_VERSION:"); +} + +static FilePath findQueryToolInDir(const FilePath &dir) +{ + if (dir.isEmpty()) + return {}; + + for (const QString &queryTool : queryToolNames()) { + FilePath queryToolPath = dir.pathAppended(queryTool).withExecutableSuffix(); + if (queryToolPath.exists()) { + if (isQueryTool(queryToolPath)) + return queryToolPath; + } + + // Prefer qmake-qt5 to qmake-qt4 by sorting the filenames in reverse order. + const FilePaths candidates = dir.dirEntries( + {BuildableHelperLibrary::possibleQtQueryTools(queryTool), QDir::Files}, + QDir::Name | QDir::Reversed); + for (const FilePath &candidate : candidates) { + if (candidate == queryToolPath) + continue; + if (isQueryTool(candidate)) + return candidate; + } + } + return {}; +} + +FilePath BuildableHelperLibrary::qtChooserToQueryToolPath(const FilePath &qtChooser) { const QString toolDir = QLatin1String("QTTOOLDIR=\""); QtcProcess proc; @@ -51,6 +105,10 @@ FilePath BuildableHelperLibrary::qtChooserToQmakePath(const FilePath &qtChooser) if (proc.result() != ProcessResult::FinishedWithSuccess) return {}; const QString output = proc.stdOut(); + // Exemplary output of "qtchooser -print-env": + // QT_SELECT="default" + // QTTOOLDIR="/usr/lib/qt5/bin" + // QTLIBDIR="/usr/lib/x86_64-linux-gnu" int pos = output.indexOf(toolDir); if (pos == -1) return {}; @@ -59,44 +117,13 @@ FilePath BuildableHelperLibrary::qtChooserToQmakePath(const FilePath &qtChooser) if (end == -1) return {}; - FilePath qmake = qtChooser; - qmake.setPath(output.mid(pos, end - pos) + "/qmake"); - return qmake; -} - -static bool isQmake(FilePath path) -{ - if (path.isEmpty()) - return false; - if (BuildableHelperLibrary::isQtChooser(path)) - path = BuildableHelperLibrary::qtChooserToQmakePath(path.symLinkTarget()); - if (!path.exists()) - return false; - return !BuildableHelperLibrary::qtVersionForQMake(path).isEmpty(); -} - -static FilePath findQmakeInDir(const FilePath &dir) -{ - if (dir.isEmpty()) - return {}; - - FilePath qmakePath = dir.pathAppended("qmake").withExecutableSuffix(); - if (qmakePath.exists()) { - if (isQmake(qmakePath)) - return qmakePath; + FilePath queryToolPath = qtChooser; + for (const QString &queryTool : queryToolNames()) { + queryToolPath.setPath(output.mid(pos, end - pos) + "/" + queryTool); + if (queryToolPath.exists()) + return queryToolPath; } - - // Prefer qmake-qt5 to qmake-qt4 by sorting the filenames in reverse order. - const FilePaths candidates = dir.dirEntries( - {BuildableHelperLibrary::possibleQMakeCommands(), QDir::Files}, - QDir::Name | QDir::Reversed); - for (const FilePath &candidate : candidates) { - if (candidate == qmakePath) - continue; - if (isQmake(candidate)) - return candidate; - } - return {}; + return queryToolPath; } FilePath BuildableHelperLibrary::findSystemQt(const Environment &env) @@ -107,86 +134,55 @@ FilePath BuildableHelperLibrary::findSystemQt(const Environment &env) FilePaths BuildableHelperLibrary::findQtsInEnvironment(const Environment &env, int maxCount) { - FilePaths qmakeList; + FilePaths queryToolList; std::set canonicalEnvPaths; const FilePaths paths = env.path(); for (const FilePath &path : paths) { if (!canonicalEnvPaths.insert(path.toFileInfo().canonicalFilePath()).second) continue; - const FilePath qmake = findQmakeInDir(path); - if (qmake.isEmpty()) + const FilePath queryTool = findQueryToolInDir(path); + if (queryTool.isEmpty()) continue; - qmakeList << qmake; - if (maxCount != -1 && qmakeList.size() == maxCount) + queryToolList << queryTool; + if (maxCount != -1 && queryToolList.size() == maxCount) break; } - return qmakeList; + return queryToolList; } -QString BuildableHelperLibrary::qtVersionForQMake(const FilePath &qmakePath) +QString BuildableHelperLibrary::filterForQtQueryToolsFileDialog() { - if (qmakePath.isEmpty()) - return QString(); - - QtcProcess qmake; - qmake.setTimeoutS(5); - qmake.setCommand({qmakePath, {"--version"}}); - qmake.runBlocking(); - if (qmake.result() != ProcessResult::FinishedWithSuccess) { - qWarning() << qmake.exitMessage(); - return QString(); + QStringList toolFilters; + for (const QString &queryTool : queryToolNames()) { + for (const QString &tool: BuildableHelperLibrary::possibleQtQueryTools(queryTool)) { + QString toolFilter; + if (HostOsInfo::isMacHost()) + // work around QTBUG-7739 that prohibits filters that don't start with * + toolFilter += QLatin1Char('*'); + toolFilter += tool; + if (HostOsInfo::isAnyUnixHost() && !HostOsInfo::isMacHost()) + // kde bug, we need at least one wildcard character + // see QTCREATORBUG-7771 + toolFilter += QLatin1Char('*'); + toolFilters.append(toolFilter); + } } - - const QString output = qmake.allOutput(); - static const QRegularExpression regexp("(QMake version:?)[\\s]*([\\d.]*)", - QRegularExpression::CaseInsensitiveOption); - const QRegularExpressionMatch match = regexp.match(output); - const QString qmakeVersion = match.captured(2); - if (qmakeVersion.startsWith(QLatin1String("2.")) - || qmakeVersion.startsWith(QLatin1String("3."))) { - static const QRegularExpression regexp2("Using Qt version[\\s]*([\\d\\.]*)", - QRegularExpression::CaseInsensitiveOption); - const QRegularExpressionMatch match2 = regexp2.match(output); - const QString version = match2.captured(1); - return version; - } - return QString(); + return queryToolNames().join(", ") + " (" + toolFilters.join(" ") + ")"; } -QString BuildableHelperLibrary::filterForQmakeFileDialog() +QStringList BuildableHelperLibrary::possibleQtQueryTools(const QString &tool) { - QString filter = QLatin1String("qmake ("); - const QStringList commands = possibleQMakeCommands(); - for (int i = 0; i < commands.size(); ++i) { - if (i) - filter += QLatin1Char(' '); - if (HostOsInfo::isMacHost()) - // work around QTBUG-7739 that prohibits filters that don't start with * - filter += QLatin1Char('*'); - filter += commands.at(i); - if (HostOsInfo::isAnyUnixHost() && !HostOsInfo::isMacHost()) - // kde bug, we need at least one wildcard character - // see QTCREATORBUG-7771 - filter += QLatin1Char('*'); - } - filter += QLatin1Char(')'); - return filter; -} - - -QStringList BuildableHelperLibrary::possibleQMakeCommands() -{ - // On Windows it is always "qmake.exe" + // On Windows it is ".exe" or ".bat" // On Unix some distributions renamed qmake with a postfix to avoid clashes // On OS X, Qt 4 binary packages also has renamed qmake. There are also symbolic links that are - // named "qmake", but the file dialog always checks against resolved links (native Cocoa issue) - QStringList commands(HostOsInfo::withExecutableSuffix("qmake*")); + // named , but the file dialog always checks against resolved links (native Cocoa issue) + QStringList tools(HostOsInfo::withExecutableSuffix(tool + "*")); // Qt 6 CMake built targets, such as Android, are dependent on the host installation - // and use a script wrapper around the host qmake executable + // and use a script wrapper around the host queryTool executable if (HostOsInfo::isWindowsHost()) - commands.append("qmake*.bat"); - return commands; + tools.append(tool + "*.bat"); + return tools; } } // namespace Utils diff --git a/src/libs/utils/buildablehelperlibrary.h b/src/libs/utils/buildablehelperlibrary.h index 8b47bef1012..2b07ac009e9 100644 --- a/src/libs/utils/buildablehelperlibrary.h +++ b/src/libs/utils/buildablehelperlibrary.h @@ -45,12 +45,11 @@ public: static FilePath findSystemQt(const Environment &env); static FilePaths findQtsInEnvironment(const Environment &env, int maxCount = -1); static bool isQtChooser(const FilePath &filePath); - static FilePath qtChooserToQmakePath(const FilePath &path); - // return true if the qmake at qmakePath is a Qt (used by QtVersion) - static QString qtVersionForQMake(const FilePath &qmakePath); - // returns something like qmake4, qmake, qmake-qt4 or whatever distributions have chosen (used by QtVersion) - static QStringList possibleQMakeCommands(); - static QString filterForQmakeFileDialog(); + static FilePath qtChooserToQueryToolPath(const FilePath &path); + // returns something like qmake4, qmake, qmake-qt4, qtpaths + // or whatever distributions have chosen (used by QtVersion) + static QStringList possibleQtQueryTools(const QString &tool); + static QString filterForQtQueryToolsFileDialog(); }; } // Utils diff --git a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp index 6c39ddef419..1c4112f882c 100644 --- a/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp +++ b/src/plugins/cmakeprojectmanager/cmakekitinformation.cpp @@ -1196,7 +1196,7 @@ Tasks CMakeConfigurationKitAspect::validate(const Kit *k) const if (!version || !version->isValid()) { addWarning(tr("CMake configuration has a path to a qmake binary set, " "even though the kit has no valid Qt version.")); - } else if (qmakePath != version->qmakeFilePath() && isQt4) { + } else if (qmakePath != version->queryToolFilePath() && isQt4) { addWarning(tr("CMake configuration has a path to a qmake binary set " "that does not match the qmake binary path " "configured in the Qt version.")); diff --git a/src/plugins/docker/kitdetector.cpp b/src/plugins/docker/kitdetector.cpp index 5e0c2e86598..474322a6043 100644 --- a/src/plugins/docker/kitdetector.cpp +++ b/src/plugins/docker/kitdetector.cpp @@ -214,10 +214,10 @@ QtVersions KitDetectorPrivate::autoDetectQtVersions() const QString error; const auto handleQmake = [this, &qtVersions, &error](const FilePath &qmake) { - if (QtVersion *qtVersion = QtVersionFactory::createQtVersionFromQMakePath(qmake, - false, - m_sharedId, - &error)) { + if (QtVersion *qtVersion = QtVersionFactory::createQtVersionFromQueryToolPath(qmake, + false, + m_sharedId, + &error)) { if (qtVersion->isValid()) { if (!Utils::anyOf(qtVersions, [qtVersion](QtVersion* other) { @@ -227,7 +227,7 @@ QtVersions KitDetectorPrivate::autoDetectQtVersions() const qtVersions.append(qtVersion); QtVersionManager::addVersion(qtVersion); emit q->logOutput( - tr("Found \"%1\"").arg(qtVersion->qmakeFilePath().toUserOutput())); + tr("Found \"%1\"").arg(qtVersion->queryToolFilePath().toUserOutput())); } } } diff --git a/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp b/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp index 7909de1981e..0fbfa346816 100644 --- a/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp +++ b/src/plugins/qbsprojectmanager/qbsprofilemanager.cpp @@ -181,7 +181,7 @@ void QbsProfileManager::addProfileFromKit(const ProjectExplorer::Kit *k) data = provider->properties(k, data); } if (const QtSupport::QtVersion * const qt = QtSupport::QtKitAspect::qtVersion(k)) - data.insert("moduleProviders.Qt.qmakeFilePaths", qt->qmakeFilePath().toString()); + data.insert("moduleProviders.Qt.qmakeFilePaths", qt->queryToolFilePath().toString()); if (QbsSettings::qbsVersion() < QVersionNumber({1, 20})) { const QString keyPrefix = "profiles." + name + "."; diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index c77f468ae23..d292f1fb5a5 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -745,6 +745,9 @@ Tasks QmakeProject::projectIssues(const Kit *k) const result.append(createProjectTask(Task::TaskType::Error, tr("Qt version is invalid."))); if (!ToolChainKitAspect::cxxToolChain(k)) result.append(createProjectTask(Task::TaskType::Error, tr("No C++ compiler set in kit."))); + if (!qtFromKit->qmakeFilePath().isExecutableFile()) + result.append(createProjectTask(Task::TaskType::Error, + tr("Qmake was not found or is not executable."))); // A project can be considered part of more than one Qt version, for instance if it is an // example shipped via the installer. diff --git a/src/plugins/qmakeprojectmanager/qmakestep.cpp b/src/plugins/qmakeprojectmanager/qmakestep.cpp index 2502297ef00..2444303cb4d 100644 --- a/src/plugins/qmakeprojectmanager/qmakestep.cpp +++ b/src/plugins/qmakeprojectmanager/qmakestep.cpp @@ -103,7 +103,8 @@ QMakeStep::QMakeStep(BuildStepList *bsl, Id id) auto updateSummary = [this] { QtVersion *qtVersion = QtKitAspect::qtVersion(target()->kit()); if (!qtVersion) - return tr("qmake: No Qt version set. Cannot run qmake."); + return tr("Query tool: No Qt version set. " + "Cannot run neither qmake nor qtpaths."); const QString program = qtVersion->qmakeFilePath().fileName(); return tr("qmake: %1 %2").arg(program, project()->projectFilePath().fileName()); }; @@ -164,7 +165,7 @@ QString QMakeStep::allArguments(const QtVersion *v, ArgumentFlags flags) const QString args = ProcessArgs::joinArgs(arguments); // User arguments ProcessArgs::addArgs(&args, userArguments()); - for (QString arg : qAsConst(m_extraArgs)) + for (const QString &arg : qAsConst(m_extraArgs)) ProcessArgs::addArgs(&args, arg); return (flags & ArgumentFlag::Expand) ? bc->macroExpander()->expand(args) : args; } @@ -214,7 +215,8 @@ bool QMakeStep::init() else workingDirectory = qmakeBc->buildDirectory(); - m_qmakeCommand = CommandLine{qtVersion->qmakeFilePath(), allArguments(qtVersion), CommandLine::Raw}; + m_qmakeCommand = + CommandLine{qtVersion->qmakeFilePath(), allArguments(qtVersion), CommandLine::Raw}; m_runMakeQmake = (qtVersion->qtVersion() >= QtVersionNumber(5, 0 ,0)); // The Makefile is used by qmake and make on the build device, from that diff --git a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp index 38c88a1cee1..6f511472057 100644 --- a/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp +++ b/src/plugins/qmldesigner/designercore/instances/puppetcreator.cpp @@ -610,7 +610,7 @@ QString PuppetCreator::qmakeCommand() const { QtSupport::QtVersion *currentQtVersion = QtSupport::QtKitAspect::qtVersion(m_target->kit()); if (currentQtVersion) - return currentQtVersion->qmakeFilePath().toString(); + return currentQtVersion->queryToolFilePath().toString(); return QString(); } diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp index 1aafe1f575c..324add0e7c6 100644 --- a/src/plugins/qtsupport/baseqtversion.cpp +++ b/src/plugins/qtsupport/baseqtversion.cpp @@ -81,6 +81,7 @@ const char QTVERSIONAUTODETECTED[] = "isAutodetected"; const char QTVERSIONDETECTIONSOURCE[] = "autodetectionSource"; const char QTVERSION_OVERRIDE_FEATURES[] = "overrideFeatures"; const char QTVERSIONQMAKEPATH[] = "QMakePath"; +const char QTVERSIONQUERYTOOLPATH[] = "QueryToolPath"; const char QTVERSIONSOURCEPATH[] = "SourcePath"; const char QTVERSION_ABIS[] = "Abis"; @@ -187,19 +188,19 @@ public: FilePath findHostBinary(HostBinaries binary) const; void updateMkspec(); QHash versionInfo(); - static bool queryQMakeVariables(const FilePath &binary, - const Environment &env, - QHash *versionInfo, - QString *error = nullptr); + static bool queryQtPaths(const FilePath &queryTool, + const Environment &env, + QHash *versionInfo, + QString *error = nullptr); enum PropertyVariant { PropertyVariantDev, PropertyVariantGet, PropertyVariantSrc }; QString qmakeProperty(const QByteArray &name, PropertyVariant variant = PropertyVariantGet); static QString qmakeProperty(const QHash &versionInfo, const QByteArray &name, PropertyVariant variant = PropertyVariantGet); static FilePath mkspecDirectoryFromVersionInfo(const QHash &versionInfo, - const FilePath &qmakeCommand); + const FilePath &queryTool); static FilePath mkspecFromVersionInfo(const QHash &versionInfo, - const FilePath &qmakeCommand); + const FilePath &queryTool); static FilePath sourcePath(const QHash &versionInfo); void setId(int id); // used by the qtversionmanager for legacy restore // and by the qtoptionspage to replace Qt versions @@ -221,7 +222,7 @@ public: bool m_defaultConfigIsDebugAndRelease = true; bool m_frameworkBuild = false; bool m_versionInfoUpToDate = false; - bool m_qmakeIsExecutable = true; + bool m_queryToolIsExecutable = true; QString m_detectionSource; QSet m_overrideFeatures; @@ -233,6 +234,7 @@ public: QHash m_versionInfo; + FilePath m_queryTool; FilePath m_qmakeCommand; FilePath m_rccPath; @@ -346,12 +348,12 @@ QtVersion::~QtVersion() QString QtVersion::defaultUnexpandedDisplayName() const { QString location; - if (qmakeFilePath().isEmpty()) { + if (queryToolFilePath().isEmpty()) { location = QCoreApplication::translate("QtVersion", ""); } else { // Deduce a description from '/foo/qt-folder/[qtbase]/bin/qmake' -> '/foo/qt-folder'. // '/usr' indicates System Qt 4.X on Linux. - for (FilePath dir = qmakeFilePath().parentDir(); !dir.isEmpty(); dir = dir.parentDir()) { + for (FilePath dir = queryToolFilePath().parentDir(); !dir.isEmpty(); dir = dir.parentDir()) { const QString dirName = dir.fileName(); if (dirName == "usr") { // System-installed Qt. location = QCoreApplication::translate("QtVersion", "System"); @@ -725,20 +727,23 @@ void QtVersion::fromMap(const QVariantMap &map) d->m_isAutodetected = map.value(QTVERSIONAUTODETECTED).toBool(); d->m_detectionSource = map.value(QTVERSIONDETECTIONSOURCE).toString(); d->m_overrideFeatures = Utils::Id::fromStringList(map.value(QTVERSION_OVERRIDE_FEATURES).toStringList()); - d->m_qmakeCommand = FilePath::fromVariant(map.value(QTVERSIONQMAKEPATH)); + d->m_queryTool = FilePath::fromVariant(map.value(QTVERSIONQUERYTOOLPATH, + map.value(QTVERSIONQMAKEPATH))); + if (!d->m_queryTool.baseName().contains("qtpaths")) + d->m_qmakeCommand = d->m_queryTool; - FilePath qmake = d->m_qmakeCommand; + FilePath queryTool = d->m_queryTool; // FIXME: Check this is still needed or whether ProcessArgs::splitArg handles it. - QString string = d->m_qmakeCommand.path(); + QString string = d->m_queryTool.path(); if (string.startsWith('~')) string.remove(0, 1).prepend(QDir::homePath()); - qmake.setPath(string); - if (!d->m_qmakeCommand.needsDevice()) { - if (BuildableHelperLibrary::isQtChooser(qmake)) { + queryTool.setPath(string); + if (!d->m_queryTool.needsDevice()) { + if (BuildableHelperLibrary::isQtChooser(queryTool)) { // we don't want to treat qtchooser as a normal qmake // see e.g. QTCREATORBUG-9841, also this lead to users changing what // qtchooser forwards too behind our backs, which will inadvertly lead to bugs - d->m_qmakeCommand = BuildableHelperLibrary::qtChooserToQmakePath(qmake); + d->m_queryTool = BuildableHelperLibrary::qtChooserToQueryToolPath(queryTool); } } @@ -769,6 +774,7 @@ QVariantMap QtVersion::toMap() const result.insert(QTVERSION_OVERRIDE_FEATURES, Utils::Id::toStringList(d->m_overrideFeatures)); result.insert(QTVERSIONQMAKEPATH, qmakeFilePath().toVariant()); + result.insert(QTVERSIONQUERYTOOLPATH, queryToolFilePath().toVariant()); return result; } @@ -779,8 +785,8 @@ bool QtVersion::isValid() const d->updateVersionInfo(); d->updateMkspec(); - return !qmakeFilePath().isEmpty() && d->m_data.installed && !binPath().isEmpty() - && !d->m_mkspecFullPath.isEmpty() && d->m_qmakeIsExecutable; + return !queryToolFilePath().isEmpty() && d->m_data.installed && !binPath().isEmpty() + && !d->m_mkspecFullPath.isEmpty() && d->m_queryToolIsExecutable; } QtVersion::Predicate QtVersion::isValidPredicate(const QtVersion::Predicate &predicate) @@ -794,15 +800,18 @@ QString QtVersion::invalidReason() const { if (displayName().isEmpty()) return QCoreApplication::translate("QtVersion", "Qt version has no name"); - if (qmakeFilePath().isEmpty()) - return QCoreApplication::translate("QtVersion", "No qmake path set"); - if (!d->m_qmakeIsExecutable) - return QCoreApplication::translate("QtVersion", "qmake does not exist or is not executable"); + if (queryToolFilePath().isEmpty()) + return QCoreApplication::translate("QtVersion", "No Qt query tool path set"); + if (!d->m_queryToolIsExecutable) + return QCoreApplication::translate("QtVersion", "%1 does not exist or is not executable") + .arg(queryToolFilePath().baseName()); if (!d->m_data.installed) return QCoreApplication::translate("QtVersion", "Qt version is not properly installed, please run make install"); if (binPath().isEmpty()) return QCoreApplication::translate("QtVersion", - "Could not determine the path to the binaries of the Qt installation, maybe the qmake path is wrong?"); + "Could not determine the path to the binaries of the " + "Qt installation, maybe the %1 path is wrong?") + .arg(queryToolFilePath().baseName()); if (d->m_mkspecUpToDate && d->m_mkspecFullPath.isEmpty()) return QCoreApplication::translate("QtVersion", "The default mkspec symlink is broken."); return QString(); @@ -820,8 +829,20 @@ QStringList QtVersion::warningReason() const return ret; } +FilePath QtVersion::queryToolFilePath() const +{ + return d->m_queryTool; +} + FilePath QtVersion::qmakeFilePath() const { + if (d->m_qmakeCommand.isEmpty() && d->m_queryTool.baseName().contains("qtpaths")) { + // TODO: might need a less lazy implementation + const FilePath qmake = + FilePath::fromString(d->m_queryTool.toString().replace("qtpaths", "qmake")); + if (qmake.exists()) + d->m_qmakeCommand = qmake; + } return d->m_qmakeCommand; } @@ -855,7 +876,7 @@ bool QtVersion::hasAbi(ProjectExplorer::Abi::OS os, ProjectExplorer::Abi::OSFlav bool QtVersion::equals(QtVersion *other) { - if (d->m_qmakeCommand != other->d->m_qmakeCommand) + if (d->m_queryTool != other->d->m_queryTool) return false; if (type() != other->type()) return false; @@ -933,13 +954,15 @@ QString QtVersion::toHtml(bool verbose) const str << "" << abis.at(i).toString() << ""; } } - const OsType osType = d->m_qmakeCommand.osType(); + const OsType osType = d->m_queryTool.osType(); str << "" << QCoreApplication::translate("QtVersion", "Source:") << "" << sourcePath().toUserOutput() << ""; str << "" << QCoreApplication::translate("QtVersion", "mkspec:") << "" << QDir::toNativeSeparators(mkspec()) << ""; str << "" << QCoreApplication::translate("QtVersion", "qmake:") - << "" << d->m_qmakeCommand.toUserOutput() << ""; + << "" << qmakeFilePath().toUserOutput() << ""; + str << "" << QCoreApplication::translate("QtVersion", "Query tool:") + << "" << d->m_queryTool.toUserOutput() << ""; ensureMkSpecParsed(); if (!mkspecPath().isEmpty()) { if (d->m_defaultConfigIsDebug || d->m_defaultConfigIsDebugAndRelease) { @@ -1164,13 +1187,13 @@ void QtVersionPrivate::updateMkspec() return; m_mkspecUpToDate = true; - m_mkspecFullPath = mkspecFromVersionInfo(versionInfo(), m_qmakeCommand); + m_mkspecFullPath = mkspecFromVersionInfo(versionInfo(), m_queryTool); m_mkspec = m_mkspecFullPath; if (m_mkspecFullPath.isEmpty()) return; - FilePath baseMkspecDir = mkspecDirectoryFromVersionInfo(versionInfo(), m_qmakeCommand); + FilePath baseMkspecDir = mkspecDirectoryFromVersionInfo(versionInfo(), m_queryTool); if (m_mkspec.isChildOf(baseMkspecDir)) { m_mkspec = m_mkspec.relativeChildPath(baseMkspecDir); @@ -1197,7 +1220,7 @@ void QtVersion::ensureMkSpecParsed() const QMakeVfs vfs; QMakeGlobals option; applyProperties(&option); - Environment env = d->m_qmakeCommand.deviceEnvironment(); + Environment env = d->m_queryTool.deviceEnvironment(); setupQmakeRunEnvironment(env); option.environment = env.toProcessEnvironment(); ProMessageHandler msgHandler(true); @@ -1310,7 +1333,7 @@ QtVersionNumber QtVersion::qtVersion() const void QtVersionPrivate::updateVersionInfo() { - if (m_versionInfoUpToDate || !m_qmakeIsExecutable || m_isUpdating) + if (m_versionInfoUpToDate || !m_queryToolIsExecutable || m_isUpdating) return; m_isUpdating = true; @@ -1321,16 +1344,16 @@ void QtVersionPrivate::updateVersionInfo() m_data.hasExamples = false; m_data.hasDocumentation = false; - if (!queryQMakeVariables(m_qmakeCommand, q->qmakeRunEnvironment(), &m_versionInfo)) { - m_qmakeIsExecutable = false; + if (!queryQtPaths(m_queryTool, q->qmakeRunEnvironment(), &m_versionInfo)) { + m_queryToolIsExecutable = false; qWarning("Cannot update Qt version information: %s cannot be run.", - qPrintable(m_qmakeCommand.toString())); + qPrintable(m_queryTool.toString())); return; } - m_qmakeIsExecutable = true; + m_queryToolIsExecutable = true; auto fileProperty = [this](const QByteArray &name) { - return FilePath::fromUserInput(qmakeProperty(name)).onDevice(m_qmakeCommand); + return FilePath::fromUserInput(qmakeProperty(name)).onDevice(m_queryTool); }; m_data.prefix = fileProperty("QT_INSTALL_PREFIX"); @@ -1385,8 +1408,8 @@ QHash QtVersionPrivate::versionInfo() } QString QtVersionPrivate::qmakeProperty(const QHash &versionInfo, - const QByteArray &name, - PropertyVariant variant) + const QByteArray &name, + PropertyVariant variant) { QString val = versionInfo .value(ProKey(QString::fromLatin1( @@ -1728,7 +1751,7 @@ void QtVersion::addToEnvironment(const Kit *k, Environment &env) const Environment QtVersion::qmakeRunEnvironment() const { - Environment env = d->m_qmakeCommand.deviceEnvironment(); + Environment env = d->m_queryTool.deviceEnvironment(); setupQmakeRunEnvironment(env); return env; } @@ -1756,7 +1779,7 @@ Tasks QtVersion::reportIssuesImpl(const QString &proFile, const QString &buildDi results.append(BuildSystemTask(Task::Error, msg)); } - FilePath qmake = qmakeFilePath(); + FilePath qmake = queryToolFilePath(); if (!qmake.isExecutableFile()) { //: %1: Path to qmake executable const QString msg = QCoreApplication::translate("QmakeProjectManager::QtVersion", @@ -1816,20 +1839,21 @@ static QByteArray runQmakeQuery(const FilePath &binary, const Environment &env, return process.readAllStandardOutput(); } -bool QtVersionPrivate::queryQMakeVariables(const FilePath &binary, const Environment &env, - QHash *versionInfo, QString *error) +bool QtVersionPrivate::queryQtPaths(const FilePath &queryTool, const Environment &env, + QHash *versionInfo, QString *error) { QString tmp; if (!error) error = &tmp; - if (!binary.isExecutableFile()) { - *error = QCoreApplication::translate("QtVersion", "qmake \"%1\" is not an executable.").arg(binary.toUserOutput()); + if (!queryTool.isExecutableFile()) { + *error = QCoreApplication::translate("QtVersion", "\"%1\" is not an executable.") + .arg(queryTool.toUserOutput()); return false; } QByteArray output; - output = runQmakeQuery(binary, env, error); + output = runQmakeQuery(queryTool, env, error); if (!output.contains("QMAKE_VERSION:")) { // Some setups pass error messages via stdout, fooling the logic below. @@ -1847,14 +1871,14 @@ bool QtVersionPrivate::queryQMakeVariables(const FilePath &binary, const Environ // Try running qmake with all kinds of tool chains set up in the environment. // This is required to make non-static qmakes work on windows where every tool chain // tries to be incompatible with any other. - const Abis abiList = Abi::abisOfBinary(binary); + const Abis abiList = Abi::abisOfBinary(queryTool); const Toolchains tcList = ToolChainManager::toolchains([&abiList](const ToolChain *t) { return abiList.contains(t->targetAbi()); }); for (ToolChain *tc : tcList) { Environment realEnv = env; tc->addToEnvironment(realEnv); - output = runQmakeQuery(binary, realEnv, error); + output = runQmakeQuery(queryTool, realEnv, error); if (error->isEmpty()) break; } @@ -1876,18 +1900,18 @@ QString QtVersionPrivate::qmakeProperty(const QByteArray &name, } FilePath QtVersionPrivate::mkspecDirectoryFromVersionInfo(const QHash &versionInfo, - const FilePath &qmakeCommand) + const FilePath &queryTool) { QString dataDir = qmakeProperty(versionInfo, "QT_HOST_DATA", PropertyVariantSrc); if (dataDir.isEmpty()) return FilePath(); - return FilePath::fromUserInput(dataDir + "/mkspecs").onDevice(qmakeCommand); + return FilePath::fromUserInput(dataDir + "/mkspecs").onDevice(queryTool); } FilePath QtVersionPrivate::mkspecFromVersionInfo(const QHash &versionInfo, - const FilePath &qmakeCommand) + const FilePath &queryTool) { - FilePath baseMkspecDir = mkspecDirectoryFromVersionInfo(versionInfo, qmakeCommand); + FilePath baseMkspecDir = mkspecDirectoryFromVersionInfo(versionInfo, queryTool); if (baseMkspecDir.isEmpty()) return FilePath(); @@ -2319,14 +2343,16 @@ void QtVersion::resetCache() const static QList g_qtVersionFactories; -QtVersion *QtVersionFactory::createQtVersionFromQMakePath - (const FilePath &qmakePath, bool isAutoDetected, const QString &detectionSource, QString *error) +QtVersion *QtVersionFactory::createQtVersionFromQueryToolPath(const FilePath &queryTool, + bool isAutoDetected, + const QString &detectionSource, + QString *error) { QHash versionInfo; - const Environment env = qmakePath.deviceEnvironment(); - if (!QtVersionPrivate::queryQMakeVariables(qmakePath, env, &versionInfo, error)) + const Environment env = queryTool.deviceEnvironment(); + if (!QtVersionPrivate::queryQtPaths(queryTool, env, &versionInfo, error)) return nullptr; - FilePath mkspec = QtVersionPrivate::mkspecFromVersionInfo(versionInfo, qmakePath); + FilePath mkspec = QtVersionPrivate::mkspecFromVersionInfo(versionInfo, queryTool); QMakeVfs vfs; QMakeGlobals globals; @@ -2342,7 +2368,7 @@ QtVersion *QtVersionFactory::createQtVersionFromQMakePath return l->m_priority > r->m_priority; }); - if (!qmakePath.isExecutableFile()) + if (!queryTool.isExecutableFile()) return nullptr; QtVersionFactory::SetupData setup; @@ -2355,8 +2381,8 @@ QtVersion *QtVersionFactory::createQtVersionFromQMakePath QtVersion *ver = factory->create(); QTC_ASSERT(ver, continue); ver->d->m_id = QtVersionManager::getUniqueId(); - QTC_CHECK(ver->d->m_qmakeCommand.isEmpty()); // Should only be used once. - ver->d->m_qmakeCommand = qmakePath; + QTC_CHECK(ver->d->m_queryTool.isEmpty()); // Should only be used once. + ver->d->m_queryTool = queryTool; ver->d->m_detectionSource = detectionSource; ver->d->m_isAutodetected = isAutoDetected; ver->updateDefaultDisplayName(); @@ -2367,7 +2393,7 @@ QtVersion *QtVersionFactory::createQtVersionFromQMakePath ProFileCacheManager::instance()->decRefCount(); if (error) { *error = QCoreApplication::translate("QtSupport::QtVersionFactory", - "No factory found for qmake: \"%1\"").arg(qmakePath.toUserOutput()); + "No factory found for query tool \"%1\"").arg(queryTool.toUserOutput()); } return nullptr; } diff --git a/src/plugins/qtsupport/baseqtversion.h b/src/plugins/qtsupport/baseqtversion.h index 269a6507168..f0400bc99b5 100644 --- a/src/plugins/qtsupport/baseqtversion.h +++ b/src/plugins/qtsupport/baseqtversion.h @@ -150,6 +150,8 @@ public: bool hasDocs() const; bool hasDemos() const; + Utils::FilePath queryToolFilePath() const; + // former local functions Utils::FilePath qmakeFilePath() const; diff --git a/src/plugins/qtsupport/qtkitinformation.cpp b/src/plugins/qtsupport/qtkitinformation.cpp index 43bbc07c1c3..672ad493df4 100644 --- a/src/plugins/qtsupport/qtkitinformation.cpp +++ b/src/plugins/qtsupport/qtkitinformation.cpp @@ -324,6 +324,11 @@ void QtKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) const QtVersion *version = qtVersion(kit); return version ? version->qmakeFilePath().path() : QString(); }); + expander->registerVariable("Qt:queryToolExecutable", tr("Path to the query tool executable"), + [kit]() -> QString { + QtVersion *version = qtVersion(kit); + return version ? version->queryToolFilePath().path() : QString(); + }); } Id QtKitAspect::id() diff --git a/src/plugins/qtsupport/qtoptionspage.cpp b/src/plugins/qtsupport/qtoptionspage.cpp index c0049058abd..06a8a6bbd52 100644 --- a/src/plugins/qtsupport/qtoptionspage.cpp +++ b/src/plugins/qtsupport/qtoptionspage.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -111,7 +112,7 @@ public: if (column == 0) return m_version->displayName(); if (column == 1) - return m_version->qmakeFilePath().toUserOutput(); + return m_version->queryToolFilePath().toUserOutput(); } if (role == Qt::FontRole && m_changed) { @@ -128,7 +129,8 @@ public: "
%2
"; return QString("
" + row.arg(tr("Qt Version"), m_version->qtVersionString()) - + row.arg(tr("Location of qmake"), m_version->qmakeFilePath().toUserOutput()) + + row.arg(tr("Location of the query tool"), + m_version->queryToolFilePath().toUserOutput()) + "
"); } @@ -600,27 +602,42 @@ QtOptionsPageWidget::~QtOptionsPageWidget() delete m_configurationWidget; } +static bool isIncompatibleQtPathsTool(const FilePath &tool) +{ + if (!tool.baseName().startsWith("qtpaths")) + return false; + QtcProcess process; + process.setTimeoutS(1); + process.setCommand({tool, {"-query"}}); + process.runBlocking(); + return process.result() != ProcessResult::FinishedWithSuccess; +} + void QtOptionsPageWidget::addQtDir() { - FilePath qtVersion = FileUtils::getOpenFilePath(this, - tr("Select a qmake Executable"), - {}, - BuildableHelperLibrary::filterForQmakeFileDialog(), - 0, - QFileDialog::DontResolveSymlinks); - if (qtVersion.isEmpty()) + FilePath qtQueryTool = + FileUtils::getOpenFilePath(this, + tr("Select a qmake or qtpaths Executable"), + {}, + BuildableHelperLibrary::filterForQtQueryToolsFileDialog(), + 0, + QFileDialog::DontResolveSymlinks); + if (qtQueryTool.isEmpty()) return; - // should add all qt versions here ? - if (BuildableHelperLibrary::isQtChooser(qtVersion)) - qtVersion = BuildableHelperLibrary::qtChooserToQmakePath(qtVersion.symLinkTarget()); + if (isIncompatibleQtPathsTool(qtQueryTool)) + qtQueryTool = qtQueryTool.parentDir() / HostOsInfo::withExecutableSuffix("qmake"); - auto checkAlreadyExists = [qtVersion](TreeItem *parent) { + // should add all qt versions here ? + if (BuildableHelperLibrary::isQtChooser(qtQueryTool)) + qtQueryTool = BuildableHelperLibrary::qtChooserToQueryToolPath(qtQueryTool.symLinkTarget()); + + auto checkAlreadyExists = [qtQueryTool](TreeItem *parent) { for (int i = 0; i < parent->childCount(); ++i) { auto item = static_cast(parent->childAt(i)); - if (item->version()->qmakeFilePath() == qtVersion) { + // Compare parent dirs, since it could be either qmake or qtpaths + if (item->version()->queryToolFilePath().parentDir() == qtQueryTool.parentDir()) return std::make_pair(true, item->version()->displayName()); - } } return std::make_pair(false, QString()); }; @@ -640,7 +657,8 @@ void QtOptionsPageWidget::addQtDir() } QString error; - QtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion, false, QString(), &error); + QtVersion *version = QtVersionFactory::createQtVersionFromQueryToolPath(qtQueryTool, false, + QString(), &error); if (version) { auto item = new QtVersionItem(version); item->setIcon(version->isValid()? m_validVersionIcon : m_invalidVersionIcon); @@ -650,8 +668,9 @@ void QtOptionsPageWidget::addQtDir() m_versionUi.nameEdit->setFocus(); m_versionUi.nameEdit->selectAll(); } else { - QMessageBox::warning(this, tr("Qmake Not Executable"), - tr("The qmake executable %1 could not be added: %2").arg(qtVersion.toUserOutput()).arg(error)); + QMessageBox::warning(this, tr("Not Executable"), + tr("The executable %1 could not be added: %2").arg( + qtQueryTool.toUserOutput()).arg(error)); return; } updateCleanUpButton(); @@ -671,16 +690,16 @@ void QtOptionsPageWidget::removeQtDir() void QtOptionsPageWidget::editPath() { QtVersion *current = currentVersion(); - FilePath qtVersion = + const FilePath queryTool = FileUtils::getOpenFilePath(this, - tr("Select a qmake Executable"), - current->qmakeFilePath().absolutePath(), - BuildableHelperLibrary::filterForQmakeFileDialog(), + tr("Select a qmake or qtpaths Executable"), + current->queryToolFilePath().absolutePath(), + BuildableHelperLibrary::filterForQtQueryToolsFileDialog(), nullptr, QFileDialog::DontResolveSymlinks); - if (qtVersion.isEmpty()) + if (queryTool.isEmpty()) return; - QtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qtVersion); + QtVersion *version = QtVersionFactory::createQtVersionFromQueryToolPath(queryTool); if (!version) return; // Same type? then replace! @@ -768,7 +787,7 @@ void QtOptionsPageWidget::updateWidgets() QtVersion *version = currentVersion(); if (version) { m_versionUi.nameEdit->setText(version->unexpandedDisplayName()); - m_versionUi.qmakePath->setText(version->qmakeFilePath().toUserOutput()); + m_versionUi.queryToolPath->setText(version->queryToolFilePath().toUserOutput()); m_configurationWidget = version->createConfigurationWidget(); if (m_configurationWidget) { m_versionUi.formLayout->addRow(m_configurationWidget); @@ -778,7 +797,7 @@ void QtOptionsPageWidget::updateWidgets() } } else { m_versionUi.nameEdit->clear(); - m_versionUi.qmakePath->clear(); + m_versionUi.queryToolPath->clear(); } const bool enabled = version != nullptr; diff --git a/src/plugins/qtsupport/qtprojectimporter.cpp b/src/plugins/qtsupport/qtprojectimporter.cpp index 9014aa71f08..fc44f3b7d37 100644 --- a/src/plugins/qtsupport/qtprojectimporter.cpp +++ b/src/plugins/qtsupport/qtprojectimporter.cpp @@ -57,7 +57,7 @@ QtProjectImporter::QtVersionData QtProjectImporter::findOrCreateQtVersion(const Utils::FilePath &qmakePath) const { QtVersionData result; - result.qt = QtVersionManager::version(Utils::equal(&QtVersion::qmakeFilePath, qmakePath)); + result.qt = QtVersionManager::version(Utils::equal(&QtVersion::queryToolFilePath, qmakePath)); if (result.qt) { // Check if version is a temporary qt const int qtId = result.qt->uniqueId(); @@ -67,7 +67,7 @@ QtProjectImporter::findOrCreateQtVersion(const Utils::FilePath &qmakePath) const // Create a new version if not found: // Do not use the canonical path here... - result.qt = QtVersionFactory::createQtVersionFromQMakePath(qmakePath); + result.qt = QtVersionFactory::createQtVersionFromQueryToolPath(qmakePath); result.isTemporary = true; if (result.qt) { UpdateGuard guard(*this); @@ -281,7 +281,7 @@ static QStringList additionalFilesToCopy(const QtVersion *qt) } else if (HostOsInfo::isWindowsHost()) { const QString release = QString("bin/Qt%1Core.dll").arg(major); const QString debug = QString("bin/Qt%1Cored.dll").arg(major); - const FilePath base = qt->qmakeFilePath().parentDir().parentDir(); + const FilePath base = qt->queryToolFilePath().parentDir().parentDir(); if (base.pathAppended(release).exists()) return {release}; if (base.pathAppended(debug).exists()) @@ -289,7 +289,7 @@ static QStringList additionalFilesToCopy(const QtVersion *qt) return {release}; } else if (HostOsInfo::isLinuxHost()) { const QString core = QString("lib/libQt%1Core.so.%1").arg(major); - const QDir base(qt->qmakeFilePath().parentDir().parentDir().pathAppended("lib").toString()); + const QDir base(qt->queryToolFilePath().parentDir().parentDir().pathAppended("lib").toString()); const QStringList icuLibs = Utils::transform(base.entryList({"libicu*.so.*"}), [](const QString &lib) { return QString("lib/" + lib); }); return QStringList(core) + icuLibs; } @@ -300,7 +300,7 @@ static QStringList additionalFilesToCopy(const QtVersion *qt) static Utils::FilePath setupQmake(const QtVersion *qt, const QString &path) { // This is a hack and only works with local, "standard" installations of Qt - const FilePath qmake = qt->qmakeFilePath().canonicalPath(); + const FilePath qmake = qt->queryToolFilePath().canonicalPath(); const QString qmakeFile = "bin/" + qmake.fileName(); const FilePath source = qmake.parentDir().parentDir(); const FilePath target = FilePath::fromString(path); diff --git a/src/plugins/qtsupport/qtversionfactory.h b/src/plugins/qtsupport/qtversionfactory.h index bc01ad7601a..96bf8e2088a 100644 --- a/src/plugins/qtsupport/qtversionfactory.h +++ b/src/plugins/qtsupport/qtversionfactory.h @@ -51,7 +51,7 @@ public: /// the desktop factory claims to handle all paths int priority() const { return m_priority; } - static QtVersion *createQtVersionFromQMakePath(const Utils::FilePath &qmakePath, + static QtVersion *createQtVersionFromQueryToolPath(const Utils::FilePath &qmakePath, bool isAutoDetected = false, const QString &detectionSource = {}, QString *error = nullptr); diff --git a/src/plugins/qtsupport/qtversioninfo.ui b/src/plugins/qtsupport/qtversioninfo.ui index 467e898a20e..815be818177 100644 --- a/src/plugins/qtsupport/qtversioninfo.ui +++ b/src/plugins/qtsupport/qtversioninfo.ui @@ -39,14 +39,14 @@ - qmake path: + Query tool path: - + 0 diff --git a/src/plugins/qtsupport/qtversionmanager.cpp b/src/plugins/qtsupport/qtversionmanager.cpp index 2ca6c3a1237..c5f00e96189 100644 --- a/src/plugins/qtsupport/qtversionmanager.cpp +++ b/src/plugins/qtsupport/qtversionmanager.cpp @@ -265,7 +265,8 @@ void QtVersionManager::updateFromInstaller(bool emitSignal) if (log().isDebugEnabled()) { qCDebug(log) << "======= Existing Qt versions ======="; for (QtVersion *version : qAsConst(m_versions)) { - qCDebug(log) << version->qmakeFilePath().toUserOutput() << "id:"<uniqueId(); + qCDebug(log) << version->queryToolFilePath().toUserOutput() + << "id:" <uniqueId(); qCDebug(log) << " autodetection source:" << version->detectionSource(); qCDebug(log) << ""; } @@ -341,7 +342,8 @@ void QtVersionManager::updateFromInstaller(bool emitSignal) if (log().isDebugEnabled()) { qCDebug(log) << "======= Before removing outdated sdk versions ======="; for (QtVersion *version : qAsConst(m_versions)) { - qCDebug(log) << version->qmakeFilePath().toUserOutput() << "id:" << version->uniqueId(); + qCDebug(log) << version->queryToolFilePath().toUserOutput() + << "id:" << version->uniqueId(); qCDebug(log) << " autodetection source:" << version->detectionSource(); qCDebug(log) << ""; } @@ -360,7 +362,8 @@ void QtVersionManager::updateFromInstaller(bool emitSignal) if (log().isDebugEnabled()) { qCDebug(log)<< "======= End result ======="; for (QtVersion *version : qAsConst(m_versions)) { - qCDebug(log) << version->qmakeFilePath().toUserOutput() << "id:" << version->uniqueId(); + qCDebug(log) << version->queryToolFilePath().toUserOutput() + << "id:" << version->uniqueId(); qCDebug(log) << " autodetection source:" << version->detectionSource(); qCDebug(log) << ""; } @@ -401,14 +404,20 @@ static QList runQtChooser(const QString &qtchooser, const QStringLis } // Asks qtchooser for the qmake path of a given version +// TODO: Extend to qtpaths if qtchooser is also used for Qt 6 static QString qmakePath(const QString &qtchooser, const QString &version) { const QList outputs = runQtChooser(qtchooser, {QStringLiteral("-qt=%1").arg(version), QStringLiteral("-print-env")}); + // Exemplary output of "qtchooser -qt=qt5-x86_64-linux-gnu -print-env": + // QT_SELECT="qt5-x86_64-linux-gnu" + // QTTOOLDIR="/usr/lib/qt5/bin" + // QTLIBDIR="/usr/lib/x86_64-linux-gnu" + const QByteArray qtToolDirPrefix("QTTOOLDIR=\""); for (const QByteArray &output : outputs) { - if (output.startsWith("QTTOOLDIR=\"")) { - QByteArray withoutVarName = output.mid(11); // remove QTTOOLDIR=" + if (output.startsWith(qtToolDirPrefix)) { + QByteArray withoutVarName = output.mid(qtToolDirPrefix.size()); // remove QTTOOLDIR=" withoutVarName.chop(1); // remove trailing quote return QStandardPaths::findExecutable(QStringLiteral("qmake"), QStringList() << QString::fromLocal8Bit(withoutVarName)); @@ -424,6 +433,15 @@ static FilePaths gatherQmakePathsFromQtChooser() return FilePaths(); const QList versions = runQtChooser(qtchooser, QStringList("-l")); + // Exemplary output of "qtchooser -l": + // 4 + // 5 + // default + // qt4-x86_64-linux-gnu + // qt4 + // qt5-x86_64-linux-gnu + // qt5 + // "" QSet foundQMakes; for (const QByteArray &version : versions) { FilePath possibleQMake = FilePath::fromString( @@ -436,19 +454,20 @@ static FilePaths gatherQmakePathsFromQtChooser() static void findSystemQt() { - FilePaths systemQMakes + FilePaths systemQueryTools = BuildableHelperLibrary::findQtsInEnvironment(Environment::systemEnvironment()); - systemQMakes.append(gatherQmakePathsFromQtChooser()); - for (const FilePath &qmakePath : qAsConst(systemQMakes)) { - if (BuildableHelperLibrary::isQtChooser(qmakePath)) + systemQueryTools.append(gatherQmakePathsFromQtChooser()); + for (const FilePath &queryToolPath : qAsConst(systemQueryTools)) { + if (BuildableHelperLibrary::isQtChooser(queryToolPath)) continue; - const auto isSameQmake = [qmakePath](const QtVersion *version) { + const auto isSameQueryTool = [queryToolPath](const QtVersion *version) { return Environment::systemEnvironment(). - isSameExecutable(qmakePath.toString(), version->qmakeFilePath().toString()); + isSameExecutable(queryToolPath.toString(), + version->queryToolFilePath().toString()); }; - if (contains(m_versions, isSameQmake)) + if (contains(m_versions, isSameQueryTool)) continue; - QtVersion *version = QtVersionFactory::createQtVersionFromQMakePath(qmakePath, + QtVersion *version = QtVersionFactory::createQtVersionFromQueryToolPath(queryToolPath, false, "PATH"); if (version)