forked from qt-creator/qt-creator
CMake: Delay initialization of CMakeTools
Do not run cmake 5 times during startup. Delay that as far as possible. Also add a supportedGenerators() method while visiting the code anyway. Fix up and simplify the other cmake help output parsers. Change-Id: I6622d552ffe559bf099b4b278618676a045e350e Reviewed-by: Tobias Hunger <tobias.hunger@theqtcompany.com>
This commit is contained in:
@@ -26,12 +26,16 @@
|
||||
#include "cmaketool.h"
|
||||
#include "cmaketoolmanager.h"
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/environment.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QProcess>
|
||||
#include <QFileInfo>
|
||||
#include <QProcess>
|
||||
#include <QSet>
|
||||
#include <QTextDocument>
|
||||
#include <QUuid>
|
||||
#include <QVariantMap>
|
||||
|
||||
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<void (QProcess::*)(int)>(&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<void (QProcess::*)(int)>(&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<QString, QStringList>());
|
||||
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<QByteArray> 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("<table><tr><td><b>%1</b></td><td>%2</td></tr>")
|
||||
.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<QByteArray> 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<QChar> braceStack;
|
||||
|
||||
if (!cmakeFunctionsList.isEmpty() && cmakeFunctionsList.first().toLatin1() == lineTrimmed) {
|
||||
//start of next function in output
|
||||
if (!currentCommandSyntax.isEmpty())
|
||||
commandSyntaxes << currentCommandSyntax.append(QLatin1String("</table>"));
|
||||
--i;
|
||||
break;
|
||||
}
|
||||
if (lineTrimmed.startsWith(currentCommand.toLatin1() + "(")) {
|
||||
if (!currentCommandSyntax.isEmpty())
|
||||
commandSyntaxes << currentCommandSyntax.append(QLatin1String("</table>"));
|
||||
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("</table>"));
|
||||
currentCommandSyntax.clear();
|
||||
} else {
|
||||
extractKeywords(lineTrimmed, &m_variables);
|
||||
currentCommandSyntax += QString::fromLatin1("<tr><td> </td><td>%1</td></tr>")
|
||||
.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<QString> 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<QByteArray> variableList = output.split('\n');
|
||||
if (!variableList.isEmpty()) {
|
||||
variableList.removeFirst(); //remove version string
|
||||
foreach (const QByteArray &variable, variableList) {
|
||||
if (variable.contains("_<CONFIG>")) {
|
||||
m_variables << QString::fromLocal8Bit(variable).replace(QLatin1String("_<CONFIG>"), QLatin1String("_DEBUG"));
|
||||
m_variables << QString::fromLocal8Bit(variable).replace(QLatin1String("_<CONFIG>"), QLatin1String("_RELEASE"));
|
||||
m_variables << QString::fromLocal8Bit(variable).replace(QLatin1String("_<CONFIG>"), QLatin1String("_MINSIZEREL"));
|
||||
m_variables << QString::fromLocal8Bit(variable).replace(QLatin1String("_<CONFIG>"), QLatin1String("_RELWITHDEBINFO"));
|
||||
} else if (variable.contains("_<LANG>")) {
|
||||
m_variables << QString::fromLocal8Bit(variable).replace(QLatin1String("_<LANG>"), QLatin1String("_C"));
|
||||
m_variables << QString::fromLocal8Bit(variable).replace(QLatin1String("_<LANG>"), 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("<CONFIG>"))) {
|
||||
const QString tmp = QString(v).replace(QLatin1String("<CONFIG>"), QLatin1String("%1"));
|
||||
result << tmp.arg(QLatin1String("DEBUG")) << tmp.arg(QLatin1String("RELEASE"))
|
||||
<< tmp.arg(QLatin1String("MINSIZEREL")) << tmp.arg(QLatin1String("RELWITHDEBINFO"));
|
||||
} else if (v.contains(QLatin1String("<LANG>"))) {
|
||||
const QString tmp = QString(v).replace(QLatin1String("<LANG>"), QLatin1String("%1"));
|
||||
result << tmp.arg(QLatin1String("C")) << tmp.arg(QLatin1String("CXX"));
|
||||
} else if (!v.contains(QLatin1Char('<')) && !v.contains(QLatin1Char('['))) {
|
||||
result << v;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user