diff --git a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp index 62b422b00cc..5805d88ab23 100644 --- a/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp +++ b/src/plugins/cmakeprojectmanager/cmakeprojectmanager.cpp @@ -31,6 +31,8 @@ #include "cmakeprojectconstants.h" #include "cmakeproject.h" +#include + #include #include #include @@ -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); diff --git a/src/plugins/projectexplorer/debugginghelper.cpp b/src/plugins/projectexplorer/debugginghelper.cpp index 848c2a404f9..ed6429cbefc 100644 --- a/src/plugins/projectexplorer/debugginghelper.cpp +++ b/src/plugins/projectexplorer/debugginghelper.cpp @@ -37,6 +37,8 @@ #include #include +#include + #include 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(); } diff --git a/src/plugins/projectexplorer/toolchain.cpp b/src/plugins/projectexplorer/toolchain.cpp index 3f3adc86a3b..fac8f256c59 100644 --- a/src/plugins/projectexplorer/toolchain.cpp +++ b/src/plugins/projectexplorer/toolchain.cpp @@ -35,6 +35,9 @@ #include "msvcparser.h" #include "linuxiccparser.h" + +#include + #include #include #include @@ -175,94 +178,123 @@ ToolChain::ToolChainType GccToolChain::type() const return ToolChain::GCC; } +static QByteArray gccPredefinedMacros(const QString &gcc, const QStringList &env) +{ + QStringList arguments; + arguments << QLatin1String("-xc++") + << QLatin1String("-E") + << QLatin1String("-dM") + << QLatin1String("-"); + + 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__"); + 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 + predefinedMacros.append("#define __strong\n"); + predefinedMacros.append("#define __weak\n"); +#endif // Q_OS_MAC + return predefinedMacros; +} + QByteArray GccToolChain::predefinedMacros() { if (m_predefinedMacros.isEmpty()) { - 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(); - -#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); - } - - // 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"); -#endif // Q_OS_MAC + m_predefinedMacros = gccPredefinedMacros(m_gcc, env.toStringList()); } return m_predefinedMacros; } +static QList gccSystemHeaderPaths(const QString &gcc, ProjectExplorer::Environment env) +{ + QList systemHeaderPaths; + QStringList arguments; + arguments << QLatin1String("-xc++") + << QLatin1String("-E") + << QLatin1String("-v") + << QLatin1String("-"); + + 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; + } + + QByteArray line; + while (cpp.canReadLine()) { + line = cpp.readLine(); + if (line.startsWith("#include")) + break; + } + + 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 GccToolChain::systemHeaderPaths() { if (m_systemHeaderPaths.isEmpty()) { - 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; - } - - 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; - } - - m_systemHeaderPaths.append(HeaderPath(QFile::decodeName(line), thisHeaderKind)); - } else if (line.startsWith("End of search list.")) { - break; - } else { - qWarning() << "ignore line:" << line; - } - } - } + 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 output = cpp.readAllStandardOutput().split('\n'); + foreach (const QByteArray& line, output) { + if (line.startsWith('V')) { + QList 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 output = cpp.readAllStandardOutput().split('\n'); - foreach (const QByteArray& line, output) { - if (line.startsWith('V')) { - QList 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")< #include #include +#include #include #include #include @@ -1060,21 +1061,13 @@ QtVersion::QmakeBuildConfigs QtVersionManager::qmakeBuildConfigFromCmdArgs(QList return result; } -void QtVersion::updateVersionInfo() const +static bool queryQMakeVariables(const QString &binary, QHash *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; +} - if (m_versionInfo.contains("QT_INSTALL_DATA")) { - QString qtInstallData = m_versionInfo.value("QT_INSTALL_DATA"); - m_versionInfo.insert("QMAKE_MKSPECS", QDir::cleanPath(qtInstallData+"/mkspecs")); +void QtVersion::updateVersionInfo() const +{ + if (m_versionInfoUpToDate) + return; - if (!qtInstallData.isEmpty()) - m_hasDebuggingHelper = !DebuggingHelperLibrary::debuggingHelperLibraryByInstallData(qtInstallData).isEmpty(); - } + // extract data from qmake executable + m_versionInfo.clear(); + m_notInstalled = false; + m_hasExamples = false; + m_hasDocumentation = false; + m_hasDebuggingHelper = false; - // 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 (!queryQMakeVariables(qmakeCommand(), &m_versionInfo)) + 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")); + + 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;