Commit cb23999f authored by Friedemann Kleint's avatar Friedemann Kleint
Browse files

Add error handling to QProcesses run at startup.

Give warnings about failures to be able to fix things/timeouts on
slow machines. Pass on arguments correctly on Windows.
Make sure processes are killed on timeouts.
parent ba032397
......@@ -31,6 +31,8 @@
#include "cmakeprojectconstants.h"
#include "cmakeproject.h"
#include <utils/synchronousprocess.h>
#include <coreplugin/icore.h>
#include <coreplugin/uniqueidmanager.h>
#include <projectexplorer/projectexplorerconstants.h>
......@@ -139,8 +141,15 @@ QString CMakeManager::qtVersionForQMake(const QString &qmakePath)
{
QProcess qmake;
qmake.start(qmakePath, QStringList(QLatin1String("--version")));
if (!qmake.waitForFinished())
if (!qmake.waitForStarted()) {
qWarning("Cannot start '%s': %s", qPrintable(qmakePath), qPrintable(qmake.errorString()));
return QString();
}
if (!qmake.waitForFinished()) {
Utils::SynchronousProcess::stopProcess(qmake);
qWarning("Timeout running '%s'.", qPrintable(qmakePath));
return QString();
}
QString output = qmake.readAllStandardOutput();
QRegExp regexp(QLatin1String("(QMake version|Qmake version:)[\\s]*([\\d.]*)"));
regexp.indexIn(output);
......
......@@ -37,6 +37,8 @@
#include <QtCore/QDir>
#include <QtCore/QDateTime>
#include <utils/synchronousprocess.h>
#include <QtGui/QDesktopServices>
using namespace ProjectExplorer;
......@@ -242,17 +244,28 @@ QString DebuggingHelperLibrary::buildDebuggingHelperLibrary(const QString &direc
QString DebuggingHelperLibrary::qtVersionForQMake(const QString &qmakePath)
{
if (qmakePath.isEmpty())
return QString();
QProcess qmake;
qmake.start(qmakePath, QStringList(QLatin1String("--version")));
if (!qmake.waitForFinished())
if (!qmake.waitForStarted()) {
qWarning("Cannot start '%s': %s", qPrintable(qmakePath), qPrintable(qmake.errorString()));
return QString();
QString output = qmake.readAllStandardOutput();
}
if (!qmake.waitForFinished()) {
Utils::SynchronousProcess::stopProcess(qmake);
qWarning("Timeout running '%s'.", qPrintable(qmakePath));
return QString();
}
const QString output = QString::fromLocal8Bit(qmake.readAllStandardOutput());
QRegExp regexp(QLatin1String("(QMake version|QMake version:)[\\s]*([\\d.]*)"), Qt::CaseInsensitive);
regexp.indexIn(output);
if (regexp.cap(2).startsWith(QLatin1String("2."))) {
QRegExp regexp2(QLatin1String("Using Qt version[\\s]*([\\d\\.]*)"), Qt::CaseInsensitive);
regexp2.indexIn(output);
return regexp2.cap(1);
const QString version = regexp2.cap(1);
return version;
}
return QString();
}
......
......@@ -35,6 +35,9 @@
#include "msvcparser.h"
#include "linuxiccparser.h"
#include <utils/synchronousprocess.h>
#include <QtCore/QDebug>
#include <QtCore/QFileInfo>
#include <QtCore/QProcess>
......@@ -175,95 +178,124 @@ ToolChain::ToolChainType GccToolChain::type() const
return ToolChain::GCC;
}
QByteArray GccToolChain::predefinedMacros()
static QByteArray gccPredefinedMacros(const QString &gcc, const QStringList &env)
{
if (m_predefinedMacros.isEmpty()) {
QStringList arguments;
arguments << QLatin1String("-xc++")
<< QLatin1String("-E")
<< QLatin1String("-dM")
<< QLatin1String("-");
QStringList arguments;
arguments << QLatin1String("-xc++")
<< QLatin1String("-E")
<< QLatin1String("-dM")
<< QLatin1String("-");
QProcess cpp;
ProjectExplorer::Environment env = ProjectExplorer::Environment::systemEnvironment();
addToEnvironment(env);
cpp.setEnvironment(env.toStringList());
cpp.start(m_gcc, arguments);
cpp.closeWriteChannel();
cpp.waitForFinished();
m_predefinedMacros = cpp.readAllStandardOutput();
QProcess cpp;
cpp.setEnvironment(env);
cpp.start(gcc, arguments);
if (!cpp.waitForStarted()) {
qWarning("Cannot start '%s': %s", qPrintable(gcc), qPrintable(cpp.errorString()));
return QByteArray();
}
cpp.closeWriteChannel();
if (!cpp.waitForFinished()) {
Utils::SynchronousProcess::stopProcess(cpp);
qWarning("Timeout running '%s'.", qPrintable(gcc));
return QByteArray();
}
QByteArray predefinedMacros = cpp.readAllStandardOutput();
#ifdef Q_OS_MAC
// Turn off flag indicating Apple's blocks support
const QByteArray blocksDefine("#define __BLOCKS__ 1");
const QByteArray blocksUndefine("#undef __BLOCKS__");
int idx = m_predefinedMacros.indexOf(blocksDefine);
if (idx != -1) {
m_predefinedMacros.replace(idx, blocksDefine.length(), blocksUndefine);
}
// Turn off flag indicating Apple's blocks support
const QByteArray blocksDefine("#define __BLOCKS__ 1");
const QByteArray blocksUndefine("#undef __BLOCKS__");
const int idx = predefinedMacros.indexOf(blocksDefine);
if (idx != -1) {
predefinedMacros.replace(idx, blocksDefine.length(), blocksUndefine);
}
// Define __strong and __weak (used for Apple's GC extension of C) to be empty
m_predefinedMacros.append("#define __strong\n");
m_predefinedMacros.append("#define __weak\n");
// Define __strong and __weak (used for Apple's GC extension of C) to be empty
predefinedMacros.append("#define __strong\n");
predefinedMacros.append("#define __weak\n");
#endif // Q_OS_MAC
return predefinedMacros;
}
QByteArray GccToolChain::predefinedMacros()
{
if (m_predefinedMacros.isEmpty()) {
ProjectExplorer::Environment env = ProjectExplorer::Environment::systemEnvironment();
addToEnvironment(env);
m_predefinedMacros = gccPredefinedMacros(m_gcc, env.toStringList());
}
return m_predefinedMacros;
}
QList<HeaderPath> GccToolChain::systemHeaderPaths()
static QList<HeaderPath> gccSystemHeaderPaths(const QString &gcc, ProjectExplorer::Environment env)
{
if (m_systemHeaderPaths.isEmpty()) {
QStringList arguments;
arguments << QLatin1String("-xc++")
<< QLatin1String("-E")
<< QLatin1String("-v")
<< QLatin1String("-");
QList<HeaderPath> systemHeaderPaths;
QStringList arguments;
arguments << QLatin1String("-xc++")
<< QLatin1String("-E")
<< QLatin1String("-v")
<< QLatin1String("-");
QProcess cpp;
ProjectExplorer::Environment env = ProjectExplorer::Environment::systemEnvironment();
addToEnvironment(env);
env.set(QLatin1String("LC_ALL"), QLatin1String("C")); //override current locale settings
cpp.setEnvironment(env.toStringList());
cpp.setReadChannelMode(QProcess::MergedChannels);
cpp.start(m_gcc, arguments);
cpp.closeWriteChannel();
cpp.waitForFinished();
QByteArray line;
while (cpp.canReadLine()) {
line = cpp.readLine();
if (line.startsWith("#include"))
break;
}
QProcess cpp;
env.set(QLatin1String("LC_ALL"), QLatin1String("C")); //override current locale settings
cpp.setEnvironment(env.toStringList());
cpp.setReadChannelMode(QProcess::MergedChannels);
cpp.start(gcc, arguments);
if (!cpp.waitForStarted()) {
qWarning("Cannot start '%s': %s", qPrintable(gcc), qPrintable(cpp.errorString()));
return systemHeaderPaths;
}
cpp.closeWriteChannel();
if (!cpp.waitForFinished()) {
Utils::SynchronousProcess::stopProcess(cpp);
qWarning("Timeout running '%s'.", qPrintable(gcc));
return systemHeaderPaths;
}
if (! line.isEmpty() && line.startsWith("#include")) {
HeaderPath::Kind kind = HeaderPath::UserHeaderPath;
while (cpp.canReadLine()) {
line = cpp.readLine();
if (line.startsWith("#include")) {
kind = HeaderPath::GlobalHeaderPath;
} else if (! line.isEmpty() && QChar(line.at(0)).isSpace()) {
HeaderPath::Kind thisHeaderKind = kind;
line = line.trimmed();
if (line.endsWith('\n'))
line.chop(1);
int index = line.indexOf(" (framework directory)");
if (index != -1) {
line = line.left(index);
thisHeaderKind = HeaderPath::FrameworkHeaderPath;
}
QByteArray line;
while (cpp.canReadLine()) {
line = cpp.readLine();
if (line.startsWith("#include"))
break;
}
m_systemHeaderPaths.append(HeaderPath(QFile::decodeName(line), thisHeaderKind));
} else if (line.startsWith("End of search list.")) {
break;
} else {
qWarning() << "ignore line:" << line;
if (! line.isEmpty() && line.startsWith("#include")) {
HeaderPath::Kind kind = HeaderPath::UserHeaderPath;
while (cpp.canReadLine()) {
line = cpp.readLine();
if (line.startsWith("#include")) {
kind = HeaderPath::GlobalHeaderPath;
} else if (! line.isEmpty() && QChar(line.at(0)).isSpace()) {
HeaderPath::Kind thisHeaderKind = kind;
line = line.trimmed();
if (line.endsWith('\n'))
line.chop(1);
const int index = line.indexOf(" (framework directory)");
if (index != -1) {
line.truncate(index);
thisHeaderKind = HeaderPath::FrameworkHeaderPath;
}
systemHeaderPaths.append(HeaderPath(QFile::decodeName(line), thisHeaderKind));
} else if (line.startsWith("End of search list.")) {
break;
} else {
qWarning() << "ignore line:" << line;
}
}
}
return systemHeaderPaths;
}
QList<HeaderPath> GccToolChain::systemHeaderPaths()
{
if (m_systemHeaderPaths.isEmpty()) {
ProjectExplorer::Environment env = ProjectExplorer::Environment::systemEnvironment();
addToEnvironment(env);
m_systemHeaderPaths = gccSystemHeaderPaths(m_gcc, env);
}
return m_systemHeaderPaths;
}
......@@ -587,53 +619,72 @@ QByteArray msvcCompilationFile() {
return file;
}
// Run MSVC 'cl' compiler to obtain #defines.
static QByteArray msvcPredefinedMacros(const QStringList &env)
{
QByteArray predefinedMacros = "#define __MSVCRT__\n"
"#define __w64\n"
"#define __int64 long long\n"
"#define __int32 long\n"
"#define __int16 short\n"
"#define __int8 char\n"
"#define __ptr32\n"
"#define __ptr64\n";
QString tmpFilePath;
{
// QTemporaryFile is buggy and will not unlock the file for cl.exe
QTemporaryFile tmpFile(QDir::tempPath()+"/envtestXXXXXX.cpp");
tmpFile.setAutoRemove(false);
if (!tmpFile.open())
return predefinedMacros;
tmpFilePath = QFileInfo(tmpFile).canonicalFilePath();
tmpFile.write(msvcCompilationFile());
tmpFile.close();
}
QProcess cpp;
cpp.setEnvironment(env);
cpp.setWorkingDirectory(QDir::tempPath());
QStringList arguments;
const QString binary = QLatin1String("cl.exe");
arguments << QLatin1String("/EP") << QDir::toNativeSeparators(tmpFilePath);
cpp.start(QLatin1String("cl.exe"), arguments);
if (!cpp.waitForStarted()) {
qWarning("Cannot start '%s': %s", qPrintable(binary), qPrintable(cpp.errorString()));
return predefinedMacros;
}
cpp.closeWriteChannel();
if (!cpp.waitForFinished()) {
Utils::SynchronousProcess::stopProcess(cpp);
qWarning("Timeout running '%s'.", qPrintable(binary));
return predefinedMacros;
}
const QList<QByteArray> output = cpp.readAllStandardOutput().split('\n');
foreach (const QByteArray& line, output) {
if (line.startsWith('V')) {
QList<QByteArray> split = line.split('=');
const QByteArray key = split.at(0).mid(1);
QByteArray value = split.at(1);
if (!value.isEmpty()) {
value.chop(1); //remove '\n'
}
predefinedMacros += "#define ";
predefinedMacros += key;
predefinedMacros += ' ';
predefinedMacros += value;
predefinedMacros += '\n';
}
}
QFile::remove(tmpFilePath);
return predefinedMacros;
}
QByteArray MSVCToolChain::predefinedMacros()
{
if (m_predefinedMacros.isEmpty()) {
m_predefinedMacros += "#define __MSVCRT__\n"
"#define __w64\n"
"#define __int64 long long\n"
"#define __int32 long\n"
"#define __int16 short\n"
"#define __int8 char\n"
"#define __ptr32\n"
"#define __ptr64\n";
QString tmpFilePath;
{
// QTemporaryFile is buggy and will not unlock the file for cl.exe
QTemporaryFile tmpFile(QDir::tempPath()+"/envtestXXXXXX.cpp");
tmpFile.setAutoRemove(false);
if (!tmpFile.open())
return m_predefinedMacros;
tmpFilePath = QFileInfo(tmpFile).canonicalFilePath();
tmpFile.write(msvcCompilationFile());
tmpFile.close();
}
ProjectExplorer::Environment env = ProjectExplorer::Environment::systemEnvironment();
addToEnvironment(env);
QProcess cpp;
cpp.setEnvironment(env.toStringList());
cpp.setWorkingDirectory(QDir::tempPath());
QStringList arguments;
arguments << "/EP" << QDir::toNativeSeparators(tmpFilePath);
cpp.start(QLatin1String("cl.exe"), arguments);
cpp.closeWriteChannel();
cpp.waitForFinished();
QList<QByteArray> output = cpp.readAllStandardOutput().split('\n');
foreach (const QByteArray& line, output) {
if (line.startsWith('V')) {
QList<QByteArray> split = line.split('=');
QByteArray key = split.at(0).mid(1);
QByteArray value = split.at(1);
if (!value.isEmpty()) {
value.chop(1); //remove '\n'
}
QByteArray newDefine = "#define " + key + ' ' + value + '\n';
m_predefinedMacros.append(newDefine);
}
}
QFile::remove(tmpFilePath);
m_predefinedMacros = msvcPredefinedMacros(env.toStringList());
}
return m_predefinedMacros;
}
......@@ -712,17 +763,24 @@ MSVCToolChain::StringStringPairList MSVCToolChain::readEnvironmentSettingI(const
}
call += "\r\n";
tf.write(call);
QString redirect = "set > \"" + tempOutputFileName + "\"\r\n";
tf.write(redirect.toLocal8Bit());
const QByteArray redirect = "set > \"" + QDir::toNativeSeparators(tempOutputFileName).toLocal8Bit() + "\"\r\n";
tf.write(redirect);
tf.flush();
tf.waitForBytesWritten(30000);
QProcess run;
run.setEnvironment(env.toStringList());
const QString cmdPath = QString::fromLocal8Bit(qgetenv("COMSPEC"));
run.start(cmdPath, QStringList()<< QLatin1String("/c")<<filename);
if (!run.waitForStarted() || !run.waitForFinished())
run.start(cmdPath, QStringList()<< QLatin1String("/c")<<QDir::toNativeSeparators(filename));
if (!run.waitForStarted()) {
qWarning("Unable to run '%s': %s", qPrintable(varsBat), qPrintable(run.errorString()));
return StringStringPairList();
}
if (!run.waitForFinished()) {
qWarning("Timeout running '%s'", qPrintable(varsBat));
Utils::SynchronousProcess::stopProcess(run);
return StringStringPairList();
}
tf.close();
QFile varsFile(tempOutputFileName);
......
......@@ -41,6 +41,7 @@
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/cesdkhandler.h>
#include <utils/synchronousprocess.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/icore.h>
#include <coreplugin/helpmanager.h>
......@@ -1060,21 +1061,13 @@ QtVersion::QmakeBuildConfigs QtVersionManager::qmakeBuildConfigFromCmdArgs(QList
return result;
}
void QtVersion::updateVersionInfo() const
static bool queryQMakeVariables(const QString &binary, QHash<QString, QString> *versionInfo)
{
if (m_versionInfoUpToDate)
return;
// extract data from qmake executable
m_versionInfo.clear();
m_notInstalled = false;
m_hasExamples = false;
m_hasDocumentation = false;
m_hasDebuggingHelper = false;
QFileInfo qmake(qmakeCommand());
if (qmake.exists() && qmake.isExecutable()) {
static const char * const variables[] = {
const int timeOutMS = 30000; // Might be slow on some machines.
QFileInfo qmake(binary);
if (!qmake.exists() || !qmake.isExecutable())
return false;
static const char * const variables[] = {
"QT_VERSION",
"QT_INSTALL_DATA",
"QT_INSTALL_LIBS",
......@@ -1089,59 +1082,82 @@ void QtVersion::updateVersionInfo() const
"QT_INSTALL_PREFIX",
"QMAKEFEATURES"
};
QStringList args;
for (uint i = 0; i < sizeof variables / sizeof variables[0]; ++i)
args << "-query" << variables[i];
QProcess process;
process.start(qmake.absoluteFilePath(), args, QIODevice::ReadOnly);
if (process.waitForFinished(10000)) {
QByteArray output = process.readAllStandardOutput();
QTextStream stream(&output);
while (!stream.atEnd()) {
const QString line = stream.readLine();
const int index = line.indexOf(QLatin1Char(':'));
if (index != -1) {
QString value = QDir::fromNativeSeparators(line.mid(index+1));
if (value != "**Unknown**")
m_versionInfo.insert(line.left(index), value);
}
}
QStringList args;
for (uint i = 0; i < sizeof variables / sizeof variables[0]; ++i)
args << "-query" << variables[i];
QProcess process;
process.start(qmake.absoluteFilePath(), args, QIODevice::ReadOnly);
if (!process.waitForStarted()) {
qWarning("Cannot start '%s': %s", qPrintable(binary), qPrintable(process.errorString()));
return false;
}
if (!process.waitForFinished(timeOutMS)) {
Utils::SynchronousProcess::stopProcess(process);
qWarning("Timeout running '%s' (%dms).", qPrintable(binary), timeOutMS);
return false;
}
QByteArray output = process.readAllStandardOutput();
QTextStream stream(&output);
while (!stream.atEnd()) {
const QString line = stream.readLine();
const int index = line.indexOf(QLatin1Char(':'));
if (index != -1) {
const QString value = QDir::fromNativeSeparators(line.mid(index+1));
if (value != "**Unknown**")
versionInfo->insert(line.left(index), value);
}
}
return true;
}
void QtVersion::updateVersionInfo() const
{
if (m_versionInfoUpToDate)
return;
if (m_versionInfo.contains("QT_INSTALL_DATA")) {
QString qtInstallData = m_versionInfo.value("QT_INSTALL_DATA");
m_versionInfo.insert("QMAKE_MKSPECS", QDir::cleanPath(qtInstallData+"/mkspecs"));
// extract data from qmake executable
m_versionInfo.clear();
m_notInstalled = false;
m_hasExamples = false;
m_hasDocumentation = false;
m_hasDebuggingHelper = false;
if (!qtInstallData.isEmpty())
m_hasDebuggingHelper = !DebuggingHelperLibrary::debuggingHelperLibraryByInstallData(qtInstallData).isEmpty();
}
if (!queryQMakeVariables(qmakeCommand(), &m_versionInfo))
return;
// Now check for a qt that is configured with a prefix but not installed
if (m_versionInfo.contains("QT_INSTALL_BINS")) {
QFileInfo fi(m_versionInfo.value("QT_INSTALL_BINS"));
if (!fi.exists())
m_notInstalled = true;
}
if (m_versionInfo.contains("QT_INSTALL_HEADERS")){
QFileInfo fi(m_versionInfo.value("QT_INSTALL_HEADERS"));
if (!fi.exists())
m_notInstalled = true;
}
if (m_versionInfo.contains("QT_INSTALL_DOCS")){
QFileInfo fi(m_versionInfo.value("QT_INSTALL_DOCS"));
if (fi.exists())
m_hasDocumentation = true;
}
if (m_versionInfo.contains("QT_INSTALL_EXAMPLES")){
QFileInfo fi(m_versionInfo.value("QT_INSTALL_EXAMPLES"));
if (fi.exists())
m_hasExamples = true;
}
if (m_versionInfo.contains("QT_INSTALL_DEMOS")){
QFileInfo fi(m_versionInfo.value("QT_INSTALL_DEMOS"));
if (fi.exists())
m_hasDemos = true;
}
if (m_versionInfo.contains("QT_INSTALL_DATA")) {
QString qtInstallData = m_versionInfo.value("QT_INSTALL_DATA");
m_versionInfo.insert("QMAKE_MKSPECS", QDir::cleanPath(qtInstallData+"/mkspecs"));
if (!qtInstallData.isEmpty())
m_hasDebuggingHelper = !DebuggingHelperLibrary::debuggingHelperLibraryByInstallData(qtInstallData).isEmpty();
}
// Now check for a qt that is configured with a prefix but not installed
if (m_versionInfo.contains("QT_INSTALL_BINS")) {
QFileInfo fi(m_versionInfo.value("QT_INSTALL_BINS"));
if (!fi.exists())
m_notInstalled = true;
}
if (m_versionInfo.contains("QT_INSTALL_HEADERS")){
QFileInfo fi(m_versionInfo.value("QT_INSTALL_HEADERS"));
if (!fi.exists())
m_notInstalled = true;
}
if (m_versionInfo.contains("QT_INSTALL_DOCS")){
QFileInfo fi(m_versionInfo.value("QT_INSTALL_DOCS"));
if (fi.exists())
m_hasDocumentation = true;
}
if (m_versionInfo.contains("QT_INSTALL_EXAMPLES")){
QFileInfo fi(m_versionInfo.value("QT_INSTALL_EXAMPLES"));
if (fi.exists())
m_hasExamples = true;
}
if (m_versionInfo.contains("QT_INSTALL_DEMOS")){
QFileInfo fi(m_versionInfo.value("QT_INSTALL_DEMOS"));
if (fi.exists())
m_hasDemos = true;
}
m_versionInfoUpToDate = true;
......