diff --git a/src/qml/Qt6QmlMacros.cmake b/src/qml/Qt6QmlMacros.cmake index 9af3d52a47..4b1fd058e9 100644 --- a/src/qml/Qt6QmlMacros.cmake +++ b/src/qml/Qt6QmlMacros.cmake @@ -763,7 +763,7 @@ function(_qt_internal_write_deferred_qmlls_ini_file) # cmake list separator and windows path separator are both ';', so no replacement needed set(concatenated_build_dirs "${_qmlls_ini_build_folders}") endif() - set(file_content "[General]\nbuildDir=${concatenated_build_dirs}\n") + set(file_content "[General]\nbuildDir=${concatenated_build_dirs}\nno-cmake-calls=false\n") file(CONFIGURE OUTPUT "${qmlls_ini_file}" CONTENT "${file_content}") endfunction() diff --git a/src/qmlls/qqmlcodemodel.cpp b/src/qmlls/qqmlcodemodel.cpp index 88b2c4cc5d..706382c7a5 100644 --- a/src/qmlls/qqmlcodemodel.cpp +++ b/src/qmlls/qqmlcodemodel.cpp @@ -90,8 +90,17 @@ QQmlCodeModel::QQmlCodeModel(QObject *parent, QQmlToolingSettings *settings) DomEnvironment::Option::SingleThreaded)), m_settings(settings) { - QObject::connect(&m_cppFileWatcher, &QFileSystemWatcher::fileChanged, this, - &QQmlCodeModel::onCppFileChanged); +} + +/*! +\internal +Disable the functionality that uses CMake, and remove the already watched paths if there are some. +*/ +void QQmlCodeModel::disableCMakeCalls() +{ + m_cmakeStatus = DoesNotHaveCMake; + m_cppFileWatcher.removePaths(m_cppFileWatcher.files()); + QObject::disconnect(&m_cppFileWatcher, &QFileSystemWatcher::fileChanged, nullptr, nullptr); } QQmlCodeModel::~QQmlCodeModel() @@ -419,17 +428,34 @@ void QQmlCodeModel::openUpdateEnd() /*! \internal -Test for CMake on the current system, and save result in m_cmakeStatus. +Performs initialization for m_cmakeStatus, including testing for CMake on the current system. */ -QQmlCodeModel::CMakeStatus QQmlCodeModel::testCMakeStatus() +void QQmlCodeModel::initializeCMakeStatus(const QString &pathForSettings) { + if (m_settings) { + const QString cmakeCalls = u"no-cmake-calls"_s; + m_settings->search(pathForSettings); + if (m_settings->isSet(cmakeCalls) && m_settings->value(cmakeCalls).toBool()) { + qWarning() << "Disabling CMake calls via .qmlls.ini setting."; + m_cmakeStatus = DoesNotHaveCMake; + return; + } + } + QProcess process; process.setProgram(u"cmake"_s); process.setArguments({ u"--version"_s }); process.start(); process.waitForFinished(); m_cmakeStatus = process.exitCode() == 0 ? HasCMake : DoesNotHaveCMake; - return m_cmakeStatus; + + if (m_cmakeStatus == DoesNotHaveCMake) { + qWarning() << "Disabling CMake calls because CMake was not found."; + return; + } + + QObject::connect(&m_cppFileWatcher, &QFileSystemWatcher::fileChanged, this, + &QQmlCodeModel::onCppFileChanged); } /*! @@ -547,13 +573,14 @@ void QQmlCodeModel::newDocForOpenFile(const QByteArray &url, int version, const { qCDebug(codeModelLog) << "updating doc" << url << "to version" << version << "(" << docText.size() << "chars)"; + + const QString fPath = url2Path(url, UrlLookup::ForceLookup); + if (m_cmakeStatus == RequiresInitialization) + initializeCMakeStatus(fPath); + DomItem newCurrent = m_currentEnv.makeCopy(DomItem::CopyOption::EnvConnected).item(); QStringList loadPaths = buildPathsForFileUrl(url); - if (m_cmakeStatus == ToTest) { - m_cmakeStatus = testCMakeStatus(); - } - if (m_cmakeStatus == HasCMake && !loadPaths.isEmpty() && m_rebuildRequired) { callCMakeBuild(loadPaths); m_rebuildRequired = false; @@ -563,7 +590,6 @@ void QQmlCodeModel::newDocForOpenFile(const QByteArray &url, int version, const if (std::shared_ptr newCurrentPtr = newCurrent.ownerAs()) { newCurrentPtr->setLoadPaths(loadPaths); } - QString fPath = url2Path(url, UrlLookup::ForceLookup); Path p; DomCreationOptions options; options.setFlag(DomCreationOption::WithScriptExpressions); diff --git a/src/qmlls/qqmlcodemodel_p.h b/src/qmlls/qqmlcodemodel_p.h index 159d7296a5..a31cb16ae7 100644 --- a/src/qmlls/qqmlcodemodel_p.h +++ b/src/qmlls/qqmlcodemodel_p.h @@ -104,6 +104,7 @@ public: QQmlToolingSettings *settings(); QStringList findFilePathsFromFileNames(const QStringList &fileNames) const; static QStringList fileNamesToWatch(const QQmlJS::Dom::DomItem &qmlFile); + void disableCMakeCalls(); Q_SIGNALS: void updatedSnapshot(const QByteArray &url); private: @@ -122,8 +123,8 @@ private: static bool callCMakeBuild(const QStringList &buildPaths); void addFileWatches(const QQmlJS::Dom::DomItem &qmlFile); - enum CMakeStatus { ToTest, HasCMake, DoesNotHaveCMake }; - CMakeStatus testCMakeStatus(); + enum CMakeStatus { RequiresInitialization, HasCMake, DoesNotHaveCMake }; + void initializeCMakeStatus(const QString &); mutable QMutex m_mutex; State m_state = State::Running; @@ -145,7 +146,7 @@ private: QQmlToolingSettings *m_settings; QFileSystemWatcher m_cppFileWatcher; bool m_rebuildRequired = true; // always trigger a rebuild on start - CMakeStatus m_cmakeStatus = ToTest; + CMakeStatus m_cmakeStatus = RequiresInitialization; private slots: void onCppFileChanged(const QString &); }; diff --git a/tests/auto/cmake/test_generate_qmlls_ini/main.cpp b/tests/auto/cmake/test_generate_qmlls_ini/main.cpp index 0aedf61c1f..4b8ddbc5da 100644 --- a/tests/auto/cmake/test_generate_qmlls_ini/main.cpp +++ b/tests/auto/cmake/test_generate_qmlls_ini/main.cpp @@ -41,7 +41,7 @@ void tst_generate_qmlls_ini::qmllsIniAreCorrect() auto secondFolder = QDir(build.absolutePath().append(u"/qml/hello/subfolders"_s)); QVERIFY(secondFolder.exists()); QCOMPARE(fileContent, - u"[General]\nbuildDir=%1%2%3\n"_s.arg(build.absolutePath(), QDir::listSeparator(), + u"[General]\nbuildDir=%1%2%3\nno-cmake-calls=false\n"_s.arg(build.absolutePath(), QDir::listSeparator(), secondFolder.absolutePath())); } @@ -54,7 +54,7 @@ void tst_generate_qmlls_ini::qmllsIniAreCorrect() QVERIFY(file.open(QFile::ReadOnly | QFile::Text)); const auto fileContent = QString::fromUtf8(file.readAll()); QCOMPARE(fileContent, - u"[General]\nbuildDir=%1\n"_s.arg(buildSubfolder.absolutePath())); + u"[General]\nbuildDir=%1\nno-cmake-calls=false\n"_s.arg(buildSubfolder.absolutePath())); } } diff --git a/tools/qmlls/qmllanguageservertool.cpp b/tools/qmlls/qmllanguageservertool.cpp index f30cf1655c..6427c4ac09 100644 --- a/tools/qmlls/qmllanguageservertool.cpp +++ b/tools/qmlls/qmllanguageservertool.cpp @@ -175,6 +175,12 @@ int main(int argv, char *argc[]) "command line options into consideration")); parser.addOption(ignoreSettings); + QCommandLineOption noCMakeCallsOption( + QStringList() << "no-cmake-calls", + QLatin1String("Disables automatic CMake rebuilds and C++ file watching.")); + parser.addOption(noCMakeCallsOption); + settings.addOption("no-cmake-calls", "false"); + parser.process(app); if (parser.isSet(writeDefaultsOption)) { @@ -213,6 +219,19 @@ int main(int argv, char *argc[]) }, (parser.isSet(ignoreSettings) ? nullptr : &settings)); + const bool disableCMakeCallsViaEnvironment = + qmlGetConfigOption("QMLLS_NO_CMAKE_CALLS"); + + if (disableCMakeCallsViaEnvironment || parser.isSet(noCMakeCallsOption)) { + if (disableCMakeCallsViaEnvironment) { + qWarning() << "Disabling CMake calls via QMLLS_NO_CMAKE_CALLS environment variable."; + } else { + qWarning() << "Disabling CMake calls via command line switch."; + } + + qmlServer.codeModel()->disableCMakeCalls(); + } + const QStringList envPaths = qEnvironmentVariable("QMLLS_BUILD_DIRS").split(u',', Qt::SkipEmptyParts); for (const QString &envPath : envPaths) {