CMakePM: Better Qt import detection

Qt6_DIR and Qt5_DIR CMake variables are additionally checked for
existence.

Qt6_ROOT and Qt5_ROOT are taken into consideration for both environment
and CMake variables.

CMAKE_PREFIX_PATH is also returned from the qmake probe. This fixes the
case when qt.toolchain.cmake is used exclusively.

Task-number: QTCREATORBUG-29075
Change-Id: I6e0c3adf7f5d9860a1cb776371e66dea1dfc26cc
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Cristian Adam
2023-05-02 20:50:44 +02:00
parent 30882d931d
commit 3dd4b98c0c

View File

@@ -247,44 +247,66 @@ static CMakeConfig configurationFromPresetProbe(
return config;
}
static FilePath qmakeFromCMakeCache(const CMakeConfig &config)
struct QMakeAndCMakePrefixPath
{
FilePath qmakePath;
QString cmakePrefixPath; // can be a semicolon-separated list
};
static QMakeAndCMakePrefixPath qtInfoFromCMakeCache(const CMakeConfig &config,
const Environment &env)
{
// Qt4 way to define things (more convenient for us, so try this first;-)
const FilePath qmake = config.filePathValueOf("QT_QMAKE_EXECUTABLE");
qCDebug(cmInputLog) << "QT_QMAKE_EXECUTABLE=" << qmake.toUserOutput();
if (!qmake.isEmpty())
return qmake;
// Check Qt5 settings: oh, the horror!
const FilePath qtCMakeDir = [config] {
FilePath tmp = config.filePathValueOf("Qt5Core_DIR");
if (tmp.isEmpty())
tmp = config.filePathValueOf("Qt6Core_DIR");
const FilePath qtCMakeDir = [config, env] {
FilePath tmp;
// Check the CMake "<package-name>_DIR" variable
for (const auto &var : {"Qt6", "Qt6Core", "Qt5", "Qt5Core"}) {
tmp = config.filePathValueOf(QByteArray(var) + "_DIR");
if (!tmp.isEmpty())
break;
}
return tmp;
}();
qCDebug(cmInputLog) << "QtXCore_DIR=" << qtCMakeDir.toUserOutput();
const FilePath canQtCMakeDir = FilePath::fromString(qtCMakeDir.toFileInfo().canonicalFilePath());
qCInfo(cmInputLog) << "QtXCore_DIR (canonical)=" << canQtCMakeDir.toUserOutput();
QString prefixPath;
if (!qtCMakeDir.isEmpty()) {
prefixPath = canQtCMakeDir.parentDir().parentDir().parentDir().toString(); // Up 3 levels...
} else {
prefixPath = config.stringValueOf("CMAKE_PREFIX_PATH");
}
const QString prefixPath = [qtCMakeDir, canQtCMakeDir, config, env] {
QString result;
if (!qtCMakeDir.isEmpty()) {
result = canQtCMakeDir.parentDir().parentDir().parentDir().path(); // Up 3 levels...
} else {
// Check the CMAKE_PREFIX_PATH and "<package-name>_ROOT" CMake or environment variables
// This can be a single value or a semicolon-separated list
for (const auto &var : {"CMAKE_PREFIX_PATH", "Qt6_ROOT", "Qt5_ROOT"}) {
result = config.stringValueOf(var);
if (result.isEmpty())
result = env.value(QString::fromUtf8(var));
if (!result.isEmpty())
break;
}
}
return result;
}();
qCDebug(cmInputLog) << "PrefixPath:" << prefixPath;
if (!qmake.isEmpty() && !prefixPath.isEmpty())
return {qmake, prefixPath};
FilePath toolchainFile = config.filePathValueOf(QByteArray("CMAKE_TOOLCHAIN_FILE"));
if (prefixPath.isEmpty() && toolchainFile.isEmpty())
return FilePath();
return {qmake, QString()};
// Run a CMake project that would do qmake probing
TemporaryDirectory qtcQMakeProbeDir("qtc-cmake-qmake-probe-XXXXXXXX");
QFile cmakeListTxt(qtcQMakeProbeDir.filePath("CMakeLists.txt").toString());
if (!cmakeListTxt.open(QIODevice::WriteOnly)) {
return FilePath();
}
cmakeListTxt.write(QByteArray(R"(
FilePath cmakeListTxt(qtcQMakeProbeDir.filePath("CMakeLists.txt"));
cmakeListTxt.writeFileContents(QByteArray(R"(
cmake_minimum_required(VERSION 3.15)
project(qmake-probe LANGUAGES NONE)
@@ -312,15 +334,20 @@ static FilePath qmakeFromCMakeCache(const CMakeConfig &config)
OUTPUT "${CMAKE_SOURCE_DIR}/qmake-location.txt"
CONTENT "$<TARGET_PROPERTY:Qt${QT_VERSION_MAJOR}::qmake,IMPORTED_LOCATION>")
endif()
# Remove a Qt CMake hack that adds lib/cmake at the end of every path in CMAKE_PREFIX_PATH
list(REMOVE_DUPLICATES CMAKE_PREFIX_PATH)
list(TRANSFORM CMAKE_PREFIX_PATH REPLACE "/lib/cmake$" "")
file(WRITE "${CMAKE_SOURCE_DIR}/cmake-prefix-path.txt" "${CMAKE_PREFIX_PATH}")
)"));
cmakeListTxt.close();
QtcProcess cmake;
cmake.setTimeoutS(5);
cmake.setDisableUnixTerminal();
Environment env = Environment::systemEnvironment();
env.setupEnglishOutput();
cmake.setEnvironment(env);
Environment cmakeEnv(env);
cmakeEnv.setupEnglishOutput();
cmake.setEnvironment(cmakeEnv);
cmake.setTimeOutMessageBoxEnabled(false);
QString cmakeGenerator = config.stringValueOf(QByteArray("CMAKE_GENERATOR"));
@@ -364,14 +391,17 @@ static FilePath qmakeFromCMakeCache(const CMakeConfig &config)
cmake.setCommand({cmakeExecutable, args});
cmake.runBlocking();
QFile qmakeLocationTxt(qtcQMakeProbeDir.filePath("qmake-location.txt").path());
if (!qmakeLocationTxt.open(QIODevice::ReadOnly)) {
return FilePath();
}
FilePath qmakeLocation = FilePath::fromUtf8(qmakeLocationTxt.readLine().constData());
const FilePath qmakeLocationTxt = qtcQMakeProbeDir.filePath("qmake-location.txt");
const FilePath qmakeLocation = FilePath::fromUtf8(
qmakeLocationTxt.fileContents().value_or(QByteArray()));
qCDebug(cmInputLog) << "qmake location: " << qmakeLocation.toUserOutput();
return qmakeLocation;
const FilePath prefixPathTxt = qtcQMakeProbeDir.filePath("cmake-prefix-path.txt");
const QString resultedPrefixPath = QString::fromUtf8(
prefixPathTxt.fileContents().value_or(QByteArray()));
qCDebug(cmInputLog) << "PrefixPath [after qmake probe]: " << resultedPrefixPath;
return {qmakeLocation, resultedPrefixPath};
}
static QVector<ToolChainDescription> extractToolChainsFromCache(const CMakeConfig &config)
@@ -686,10 +716,15 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
configurePreset.generator.value().toUtf8());
}
const FilePath qmake = qmakeFromCMakeCache(config);
const auto [qmake, cmakePrefixPath] = qtInfoFromCMakeCache(config, env);
if (!qmake.isEmpty())
data->qt = findOrCreateQtVersion(qmake);
if (!cmakePrefixPath.isEmpty() && config.valueOf("CMAKE_PREFIX_PATH").isEmpty())
config << CMakeConfigItem("CMAKE_PREFIX_PATH",
CMakeConfigItem::PATH,
cmakePrefixPath.toUtf8());
// ToolChains:
data->toolChains = extractToolChainsFromCache(config);
@@ -746,6 +781,8 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
buildConfigurationTypes = buildConfigurationTypesString.split(';');
}
const Environment env = projectDirectory().deviceEnvironment();
for (auto const &buildType: std::as_const(buildConfigurationTypes)) {
auto data = std::make_unique<DirectoryData>();
@@ -777,7 +814,7 @@ QList<void *> CMakeProjectImporter::examineDirectory(const FilePath &importPath,
data->sysroot = config.filePathValueOf("CMAKE_SYSROOT");
// Qt:
const FilePath qmake = qmakeFromCMakeCache(config);
const auto [qmake, cmakePrefixPath] = qtInfoFromCMakeCache(config, env);
if (!qmake.isEmpty())
data->qt = findOrCreateQtVersion(qmake);
@@ -1021,8 +1058,9 @@ void CMakeProjectPlugin::testCMakeProjectImporterQt()
config.append(CMakeConfigItem(key.toUtf8(), value.toUtf8()));
}
FilePath realQmake = qmakeFromCMakeCache(config);
QCOMPARE(realQmake.toString(), expectedQmake);
auto [realQmake, cmakePrefixPath] = qtInfoFromCMakeCache(config,
Environment::systemEnvironment());
QCOMPARE(realQmake.path(), expectedQmake);
}
void CMakeProjectPlugin::testCMakeProjectImporterToolChain_data()
{