diff --git a/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp b/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp index 565bbd98595..e8d1c695bdd 100644 --- a/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp +++ b/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.cpp @@ -46,8 +46,6 @@ using namespace ProjectExplorer; // ------------------------------- // CMakeFileCompletionAssistProvider // ------------------------------- -CMakeFileCompletionAssistProvider::CMakeFileCompletionAssistProvider() -{} bool CMakeFileCompletionAssistProvider::supportsEditor(Core::Id editorId) const { @@ -56,31 +54,26 @@ bool CMakeFileCompletionAssistProvider::supportsEditor(Core::Id editorId) const IAssistProcessor *CMakeFileCompletionAssistProvider::createProcessor() const { - return new CMakeFileCompletionAssist(); + return new CMakeFileCompletionAssist; } - CMakeFileCompletionAssist::CMakeFileCompletionAssist() : KeywordsCompletionAssistProcessor(Keywords()) {} IAssistProposal *CMakeFileCompletionAssist::perform(const AssistInterface *interface) { - TextEditor::Keywords keywords; - + Keywords kw; QString fileName = interface->fileName(); if (!fileName.isEmpty() && QFileInfo(fileName).isFile()) { - Utils::FileName file = Utils::FileName::fromString(fileName); - if (Project *p = SessionManager::projectForFile(file)) { - if (Target *t = p->activeTarget()) { - if (CMakeTool *cmake = CMakeKitInformation::cmakeTool(t->kit())) { - if (cmake->isValid()) - keywords = cmake->keywords(); - } - } + Project *p = SessionManager::projectForFile(Utils::FileName::fromString(fileName)); + if (p && p->activeTarget()) { + CMakeTool *cmake = CMakeKitInformation::cmakeTool(p->activeTarget()->kit()); + if (cmake && cmake->isValid()) + kw = cmake->keywords(); } } - setKeywords(keywords); + setKeywords(kw); return KeywordsCompletionAssistProcessor::perform(interface); } diff --git a/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.h b/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.h index 17fe6888277..521d0c2d760 100644 --- a/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.h +++ b/src/plugins/cmakeprojectmanager/cmakefilecompletionassist.h @@ -47,8 +47,6 @@ class CMakeFileCompletionAssistProvider : public TextEditor::CompletionAssistPro Q_OBJECT public: - CMakeFileCompletionAssistProvider(); - bool supportsEditor(Core::Id editorId) const override; TextEditor::IAssistProcessor *createProcessor() const override; }; diff --git a/src/plugins/cmakeprojectmanager/cmaketool.cpp b/src/plugins/cmakeprojectmanager/cmaketool.cpp index feadffb01b9..d8c2b742c85 100644 --- a/src/plugins/cmakeprojectmanager/cmaketool.cpp +++ b/src/plugins/cmakeprojectmanager/cmaketool.cpp @@ -26,12 +26,16 @@ #include "cmaketool.h" #include "cmaketoolmanager.h" +#include +#include #include -#include #include +#include +#include #include #include +#include using namespace CMakeProjectManager; @@ -44,16 +48,12 @@ const char CMAKE_INFORMATION_AUTODETECTED[] = "AutoDetected"; // CMakeTool /////////////////////////// CMakeTool::CMakeTool(Detection d, const Core::Id &id) : - m_isAutoDetected(d == AutoDetection), - m_id(id) + m_id(id), m_isAutoDetected(d == AutoDetection) { - //make sure every CMakeTool has a valid ID - if (!m_id.isValid()) - createId(); + QTC_ASSERT(m_id.isValid(), m_id = Core::Id::fromString(QUuid::createUuid().toString())); } -CMakeTool::CMakeTool(const QVariantMap &map, bool fromSdk) : - m_isAutoDetected(fromSdk) +CMakeTool::CMakeTool(const QVariantMap &map, bool fromSdk) : m_isAutoDetected(fromSdk) { m_id = Core::Id::fromSetting(map.value(QLatin1String(CMAKE_INFORMATION_ID))); m_displayName = map.value(QLatin1String(CMAKE_INFORMATION_DISPLAYNAME)).toString(); @@ -62,106 +62,57 @@ CMakeTool::CMakeTool(const QVariantMap &map, bool fromSdk) : if (!fromSdk) m_isAutoDetected = map.value(QLatin1String(CMAKE_INFORMATION_AUTODETECTED), false).toBool(); - setCMakeExecutable(Utils::FileName::fromUserInput(map.value(QLatin1String(CMAKE_INFORMATION_COMMAND)).toString())); + setCMakeExecutable(Utils::FileName::fromString(map.value(QLatin1String(CMAKE_INFORMATION_COMMAND)).toString())); } -CMakeTool::~CMakeTool() +Core::Id CMakeTool::createId() { - cancel(); -} - -void CMakeTool::cancel() -{ - if (m_process) { - disconnect(m_process, static_cast(&QProcess::finished), - this, &CMakeTool::finished); - - if (m_process->state() != QProcess::NotRunning) - m_process->kill(); - - m_process->waitForFinished(); - delete m_process; - m_process = 0; - } + return Core::Id::fromString(QUuid::createUuid().toString()); } void CMakeTool::setCMakeExecutable(const Utils::FileName &executable) { - cancel(); - m_process = new QProcess(); - connect(m_process, static_cast(&QProcess::finished), this, &CMakeTool::finished); + if (m_executable == executable) + return; + + m_didRun = false; + m_didAttemptToRun = false; m_executable = executable; - QFileInfo fi = m_executable.toFileInfo(); - if (fi.exists() && fi.isExecutable()) { - // Run it to find out more - m_state = CMakeTool::RunningBasic; - if (!startProcess(QStringList(QLatin1String("--help")))) - m_state = CMakeTool::Invalid; - } else { - m_state = CMakeTool::Invalid; - } - CMakeToolManager::notifyAboutUpdate(this); } -void CMakeTool::finished(int exitCode) -{ - if (exitCode) { - m_state = CMakeTool::Invalid; - return; - } - if (m_state == CMakeTool::RunningBasic) { - QByteArray response = m_process->readAll(); - - m_hasCodeBlocksMsvcGenerator = response.contains("CodeBlocks - NMake Makefiles"); - m_hasCodeBlocksNinjaGenerator = response.contains("CodeBlocks - Ninja"); - - if (response.isEmpty()) { - m_state = CMakeTool::Invalid; - } else { - m_state = CMakeTool::RunningFunctionList; - if (!startProcess(QStringList(QLatin1String("--help-command-list")))) - finished(0); // should never happen, just continue - } - } else if (m_state == CMakeTool::RunningFunctionList) { - parseFunctionOutput(m_process->readAll()); - m_state = CMakeTool::RunningFunctionDetails; - if (!startProcess(QStringList(QLatin1String("--help-commands")))) - finished(0); // should never happen, just continue - } else if (m_state == CMakeTool::RunningFunctionDetails) { - parseFunctionDetailsOutput(m_process->readAll()); - m_state = CMakeTool::RunningPropertyList; - if (!startProcess(QStringList(QLatin1String("--help-property-list")))) - finished(0); // should never happen, just continue - } else if (m_state == CMakeTool::RunningPropertyList) { - parseVariableOutput(m_process->readAll()); - m_state = CMakeTool::RunningVariableList; - if (!startProcess(QStringList(QLatin1String("--help-variable-list")))) - finished(0); // should never happen, just continue - } else if (m_state == CMakeTool::RunningVariableList) { - parseVariableOutput(m_process->readAll()); - parseDone(); - m_state = CMakeTool::RunningDone; - } -} - bool CMakeTool::isValid() const { - if (m_state == CMakeTool::Invalid || !m_id.isValid()) + if (!m_id.isValid()) return false; - if (m_state == CMakeTool::RunningBasic) { - if (!m_process->waitForFinished(10000)) { - return false; - } - } - return (m_state != CMakeTool::Invalid); + + if (!m_didAttemptToRun) + supportedGenerators(); + + return m_didRun; } -void CMakeTool::createId() +Utils::SynchronousProcessResponse CMakeTool::run(const QString &arg) const { - QTC_ASSERT(!m_id.isValid(), return); - m_id = Core::Id::fromString(QUuid::createUuid().toString()); + if (m_didAttemptToRun && !m_didRun) { + Utils::SynchronousProcessResponse response; + response.result = Utils::SynchronousProcessResponse::StartFailed; + return response; + } + + Utils::SynchronousProcess cmake; + cmake.setTimeoutS(1); + cmake.setFlags(Utils::SynchronousProcess::UnixTerminalDisabled); + Utils::Environment env = Utils::Environment::systemEnvironment(); + env.set(QLatin1String("LC_ALL"), QLatin1String("C")); + cmake.setProcessEnvironment(env.toProcessEnvironment()); + cmake.setTimeOutMessageBoxEnabled(false); + + Utils::SynchronousProcessResponse response = cmake.run(m_executable.toString(), QStringList() << arg); + m_didAttemptToRun = true; + m_didRun = (response.result == Utils::SynchronousProcessResponse::Finished); + return response; } QVariantMap CMakeTool::toMap() const @@ -174,12 +125,6 @@ QVariantMap CMakeTool::toMap() const return data; } -bool CMakeTool::startProcess(const QStringList &args) -{ - m_process->start(m_executable.toString(), args); - return m_process->waitForStarted(2000); -} - Utils::FileName CMakeTool::cmakeExecutable() const { return m_executable; @@ -189,24 +134,70 @@ bool CMakeTool::hasCodeBlocksMsvcGenerator() const { if (!isValid()) return false; - return m_hasCodeBlocksMsvcGenerator; + return supportedGenerators().contains(QLatin1String("CodeBlocks - NMake Makefiles")); } bool CMakeTool::hasCodeBlocksNinjaGenerator() const { - if (!isValid()) - return false; - return m_hasCodeBlocksNinjaGenerator; + return supportedGenerators().contains(QLatin1String("CodeBlocks - Ninja")); +} + +QStringList CMakeTool::supportedGenerators() const +{ + if (m_generators.isEmpty()) { + Utils::SynchronousProcessResponse response = run(QLatin1String("--help")); + if (response.result == Utils::SynchronousProcessResponse::Finished) { + bool inGeneratorSection = false; + const QStringList lines = response.stdOut.split(QLatin1Char('\n')); + foreach (const QString &line, lines) { + if (line.isEmpty()) + continue; + if (line == QLatin1String("Generators")) + inGeneratorSection = true; + if (!inGeneratorSection) + continue; + + if (line.startsWith(QLatin1String(" "))) { + int pos = line.indexOf(QLatin1Char('=')); + if (pos < 0) + pos = line.length(); + if (pos >= 0) { + --pos; + while (pos > 2 && line.at(pos).isSpace()) + --pos; + } + if (pos > 2) + m_generators.append(line.mid(2, pos - 1)); + } + } + } + } + return m_generators; } TextEditor::Keywords CMakeTool::keywords() { - while (m_state != RunningDone && m_state != CMakeTool::Invalid) { - m_process->waitForFinished(); - } + if (m_functions.isEmpty()) { + Utils::SynchronousProcessResponse response; + response = run(QLatin1String("--help-command-list")); + if (response.result == Utils::SynchronousProcessResponse::Finished) + m_functions = response.stdOut.split(QLatin1Char('\n')); - if (m_state == CMakeTool::Invalid) - return TextEditor::Keywords(QStringList(), QStringList(), QMap()); + response = run(QLatin1String("--help-commands")); + if (response.result == Utils::SynchronousProcessResponse::Finished) + parseFunctionDetailsOutput(response.stdOut); + + response = run(QLatin1String("--help-property-list")); + if (response.result == Utils::SynchronousProcessResponse::Finished) + m_variables = parseVariableOutput(response.stdOut); + + response = run(QLatin1String("--help-variable-list")); + if (response.result == Utils::SynchronousProcessResponse::Finished) { + m_variables.append(parseVariableOutput(response.stdOut)); + m_variables = Utils::filteredUnique(m_variables); + Utils::sort(m_variables); + } + } return TextEditor::Keywords(m_variables, m_functions, m_functionArgs); } @@ -216,53 +207,6 @@ bool CMakeTool::isAutoDetected() const return m_isAutoDetected; } -static void extractKeywords(const QByteArray &input, QStringList *destination) -{ - if (!destination) - return; - - QString keyword; - int ignoreZone = 0; - for (int i = 0; i < input.count(); ++i) { - const QChar chr = QLatin1Char(input.at(i)); - if (chr == QLatin1Char('{')) - ++ignoreZone; - if (chr == QLatin1Char('}')) - --ignoreZone; - if (ignoreZone == 0) { - if ((chr.isLetterOrNumber() && chr.isUpper()) - || chr == QLatin1Char('_')) { - keyword += chr; - } else { - if (!keyword.isEmpty()) { - if (keyword.size() > 1) - *destination << keyword; - keyword.clear(); - } - } - } - } - if (keyword.size() > 1) - *destination << keyword; -} - -void CMakeTool::parseFunctionOutput(const QByteArray &output) -{ - QList cmakeFunctionsList = output.split('\n'); - m_functions.clear(); - if (!cmakeFunctionsList.isEmpty()) { - cmakeFunctionsList.removeFirst(); //remove version string - foreach (const QByteArray &function, cmakeFunctionsList) - m_functions << QString::fromLocal8Bit(function.trimmed()); - } -} - -QString CMakeTool::formatFunctionDetails(const QString &command, const QString &args) -{ - return QString::fromLatin1("") - .arg(command.toHtmlEscaped(), args.toHtmlEscaped()); -} - QString CMakeTool::displayName() const { return m_displayName; @@ -286,78 +230,91 @@ QString CMakeTool::mapAllPaths(ProjectExplorer::Kit *kit, const QString &in) con return in; } -void CMakeTool::parseFunctionDetailsOutput(const QByteArray &output) +static QStringList parseDefinition(const QString &definition) { - QStringList cmakeFunctionsList = m_functions; - QList cmakeCommandsHelp = output.split('\n'); - for (int i = 0; i < cmakeCommandsHelp.count(); ++i) { - QByteArray lineTrimmed = cmakeCommandsHelp.at(i).trimmed(); - if (cmakeFunctionsList.isEmpty()) - break; - if (cmakeFunctionsList.first().toLatin1() == lineTrimmed) { - QStringList commandSyntaxes; - QString currentCommandSyntax; - QString currentCommand = cmakeFunctionsList.takeFirst(); - ++i; - for (; i < cmakeCommandsHelp.count(); ++i) { - lineTrimmed = cmakeCommandsHelp.at(i).trimmed(); + QStringList result; + QString word; + bool ignoreWord = false; + QVector braceStack; - if (!cmakeFunctionsList.isEmpty() && cmakeFunctionsList.first().toLatin1() == lineTrimmed) { - //start of next function in output - if (!currentCommandSyntax.isEmpty()) - commandSyntaxes << currentCommandSyntax.append(QLatin1String("
%1%2
")); - --i; - break; - } - if (lineTrimmed.startsWith(currentCommand.toLatin1() + "(")) { - if (!currentCommandSyntax.isEmpty()) - commandSyntaxes << currentCommandSyntax.append(QLatin1String("")); + foreach (const QChar &c, definition) { + if (c == QLatin1Char('[') || c == QLatin1Char('<') || c == QLatin1Char('(')) { + braceStack.append(c); + ignoreWord = false; + } else if (c == QLatin1Char(']') || c == QLatin1Char('>') || c == QLatin1Char(')')) { + if (braceStack.isEmpty() || braceStack.takeLast() == QLatin1Char('<')) + ignoreWord = true; + } - QByteArray argLine = lineTrimmed.mid(currentCommand.length()); - extractKeywords(argLine, &m_variables); - currentCommandSyntax = formatFunctionDetails(currentCommand, QString::fromUtf8(argLine)); - } else { - if (!currentCommandSyntax.isEmpty()) { - if (lineTrimmed.isEmpty()) { - commandSyntaxes << currentCommandSyntax.append(QLatin1String("")); - currentCommandSyntax.clear(); - } else { - extractKeywords(lineTrimmed, &m_variables); - currentCommandSyntax += QString::fromLatin1(" %1") - .arg(QString::fromLocal8Bit(lineTrimmed).toHtmlEscaped()); - } + if (c == QLatin1Char(' ') || c == QLatin1Char('[') || c == QLatin1Char('<') || c == QLatin1Char('(') + || c == QLatin1Char(']') || c == QLatin1Char('>') || c == QLatin1Char(')')) { + if (!ignoreWord && !word.isEmpty()) { + if (result.isEmpty() || Utils::allOf(word, [](const QChar &c) { return c.isUpper() || c == QLatin1Char('_'); })) + result.append(word); + } + word.clear(); + ignoreWord = false; + } else { + word.append(c); + } + } + return result; +} + +void CMakeTool::parseFunctionDetailsOutput(const QString &output) +{ + QSet functionSet; + functionSet.fromList(m_functions); + + bool expectDefinition = false; + QString currentDefinition; + + const QStringList lines = output.split(QLatin1Char('\n')); + for (int i = 0; i < lines.count(); ++i) { + const QString line = lines.at(i); + + if (line == QLatin1String("::")) { + expectDefinition = true; + continue; + } + + if (expectDefinition) { + if (!line.startsWith(QLatin1Char(' ')) && !line.isEmpty()) { + expectDefinition = false; + QStringList words = parseDefinition(currentDefinition); + if (!words.isEmpty()) { + const QString command = words.takeFirst(); + if (functionSet.contains(command)) { + QStringList tmp = words + m_functionArgs[command]; + Utils::sort(tmp); + m_functionArgs[command] = Utils::filteredUnique(tmp); } } - } - m_functionArgs[currentCommand] = commandSyntaxes; - } - } - m_functions = m_functionArgs.keys(); -} - -void CMakeTool::parseVariableOutput(const QByteArray &output) -{ - QList variableList = output.split('\n'); - if (!variableList.isEmpty()) { - variableList.removeFirst(); //remove version string - foreach (const QByteArray &variable, variableList) { - if (variable.contains("_")) { - m_variables << QString::fromLocal8Bit(variable).replace(QLatin1String("_"), QLatin1String("_DEBUG")); - m_variables << QString::fromLocal8Bit(variable).replace(QLatin1String("_"), QLatin1String("_RELEASE")); - m_variables << QString::fromLocal8Bit(variable).replace(QLatin1String("_"), QLatin1String("_MINSIZEREL")); - m_variables << QString::fromLocal8Bit(variable).replace(QLatin1String("_"), QLatin1String("_RELWITHDEBINFO")); - } else if (variable.contains("_")) { - m_variables << QString::fromLocal8Bit(variable).replace(QLatin1String("_"), QLatin1String("_C")); - m_variables << QString::fromLocal8Bit(variable).replace(QLatin1String("_"), QLatin1String("_CXX")); - } else if (!variable.contains("_<") && !variable.contains('[')) { - m_variables << QString::fromLocal8Bit(variable); + if (!words.isEmpty() && functionSet.contains(words.at(0))) + m_functionArgs[words.at(0)]; + currentDefinition.clear(); + } else { + currentDefinition.append(line.trimmed() + QLatin1Char(' ')); } } } } -void CMakeTool::parseDone() +QStringList CMakeTool::parseVariableOutput(const QString &output) { - m_variables.sort(); - m_variables.removeDuplicates(); + const QStringList variableList = output.split(QLatin1Char('\n')); + QStringList result; + foreach (const QString &v, variableList) { + if (v.contains(QLatin1String(""))) { + const QString tmp = QString(v).replace(QLatin1String(""), QLatin1String("%1")); + result << tmp.arg(QLatin1String("DEBUG")) << tmp.arg(QLatin1String("RELEASE")) + << tmp.arg(QLatin1String("MINSIZEREL")) << tmp.arg(QLatin1String("RELWITHDEBINFO")); + } else if (v.contains(QLatin1String(""))) { + const QString tmp = QString(v).replace(QLatin1String(""), QLatin1String("%1")); + result << tmp.arg(QLatin1String("C")) << tmp.arg(QLatin1String("CXX")); + } else if (!v.contains(QLatin1Char('<')) && !v.contains(QLatin1Char('['))) { + result << v; + } + } + return result; } diff --git a/src/plugins/cmakeprojectmanager/cmaketool.h b/src/plugins/cmakeprojectmanager/cmaketool.h index 74bf23086bd..a24d304be9d 100644 --- a/src/plugins/cmakeprojectmanager/cmaketool.h +++ b/src/plugins/cmakeprojectmanager/cmaketool.h @@ -27,12 +27,14 @@ #include "cmake_global.h" -#include -#include #include +#include + +#include +#include #include -#include +#include #include QT_FORWARD_DECLARE_CLASS(QProcess) @@ -52,23 +54,25 @@ public: typedef std::function PathMapper; - explicit CMakeTool(Detection d, const Core::Id &id = Core::Id()); + explicit CMakeTool(Detection d, const Core::Id &id); explicit CMakeTool(const QVariantMap &map, bool fromSdk); - ~CMakeTool() override; + ~CMakeTool() override = default; + + static Core::Id createId(); - enum State { Invalid, RunningBasic, RunningFunctionList, RunningFunctionDetails, - RunningPropertyList, RunningVariableList, RunningDone }; - void cancel(); bool isValid() const; Core::Id id() const { return m_id; } QVariantMap toMap () const; void setCMakeExecutable(const Utils::FileName &executable); + Utils::FileName cmakeExecutable() const; bool hasCodeBlocksMsvcGenerator() const; bool hasCodeBlocksNinjaGenerator() const; + QStringList supportedGenerators() const; TextEditor::Keywords keywords(); + bool isAutoDetected() const; QString displayName() const; void setDisplayName(const QString &displayName); @@ -77,32 +81,24 @@ public: QString mapAllPaths(ProjectExplorer::Kit *kit, const QString &in) const; private: - void finished(int exitCode); - - void createId(); - void finishStep(); - void startNextStep(); - bool startProcess(const QStringList &args); - void parseFunctionOutput(const QByteArray &output); - void parseFunctionDetailsOutput(const QByteArray &output); - void parseVariableOutput(const QByteArray &output); - void parseDone(); - QString formatFunctionDetails(const QString &command, const QString &args); - - State m_state = Invalid; - QProcess *m_process = 0; - Utils::FileName m_executable; - - bool m_isAutoDetected; - bool m_hasCodeBlocksMsvcGenerator = false; - bool m_hasCodeBlocksNinjaGenerator = false; - - QMap m_functionArgs; - QStringList m_variables; - QStringList m_functions; + Utils::SynchronousProcessResponse run(const QString &arg) const; + void parseFunctionDetailsOutput(const QString &output); + QStringList parseVariableOutput(const QString &output); Core::Id m_id; QString m_displayName; + Utils::FileName m_executable; + + bool m_isAutoDetected; + + mutable bool m_didAttemptToRun; + mutable bool m_didRun; + + mutable QStringList m_generators; + mutable QMap m_functionArgs; + mutable QStringList m_variables; + mutable QStringList m_functions; + PathMapper m_pathMapper; }; diff --git a/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp b/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp index 2fe39e28f82..e516605f5e8 100644 --- a/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp +++ b/src/plugins/cmakeprojectmanager/cmaketoolmanager.cpp @@ -131,7 +131,7 @@ static void readAndDeleteLegacyCMakeSettings () if (exec.toFileInfo().isExecutable()) { CMakeTool *item = CMakeToolManager::findByCommand(exec); if (!item) { - item = new CMakeTool(CMakeTool::ManualDetection); + item = new CMakeTool(CMakeTool::ManualDetection, CMakeTool::createId()); item->setCMakeExecutable(exec); item->setDisplayName(CMakeToolManager::tr("CMake at %1").arg(item->cmakeExecutable().toUserOutput())); @@ -182,7 +182,7 @@ static QList autoDetectCMakeTools() QList found; foreach (const FileName &command, suspects) { - auto item = new CMakeTool(CMakeTool::AutoDetection); + auto item = new CMakeTool(CMakeTool::AutoDetection, CMakeTool::createId()); item->setCMakeExecutable(command); item->setDisplayName(CMakeToolManager::tr("System CMake at %1").arg(command.toUserOutput())); @@ -247,7 +247,7 @@ Id CMakeToolManager::registerOrFindCMakeTool(const FileName &command) if (cmake) return cmake->id(); - cmake = new CMakeTool(CMakeTool::ManualDetection); + cmake = new CMakeTool(CMakeTool::ManualDetection, CMakeTool::createId()); cmake->setCMakeExecutable(command); cmake->setDisplayName(tr("CMake at %1").arg(command.toUserOutput()));