Commit cb23999fbf16476d52678d23fc3cf888090560bf

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.
  
3131#include "cmakeprojectconstants.h"
3232#include "cmakeproject.h"
3333
34#include <utils/synchronousprocess.h>
35
3436#include <coreplugin/icore.h>
3537#include <coreplugin/uniqueidmanager.h>
3638#include <projectexplorer/projectexplorerconstants.h>
141141{
142142 QProcess qmake;
143143 qmake.start(qmakePath, QStringList(QLatin1String("--version")));
144 if (!qmake.waitForFinished())
144 if (!qmake.waitForStarted()) {
145 qWarning("Cannot start '%s': %s", qPrintable(qmakePath), qPrintable(qmake.errorString()));
145146 return QString();
147 }
148 if (!qmake.waitForFinished()) {
149 Utils::SynchronousProcess::stopProcess(qmake);
150 qWarning("Timeout running '%s'.", qPrintable(qmakePath));
151 return QString();
152 }
146153 QString output = qmake.readAllStandardOutput();
147154 QRegExp regexp(QLatin1String("(QMake version|Qmake version:)[\\s]*([\\d.]*)"));
148155 regexp.indexIn(output);
  
3737#include <QtCore/QDir>
3838#include <QtCore/QDateTime>
3939
40#include <utils/synchronousprocess.h>
41
4042#include <QtGui/QDesktopServices>
4143
4244using namespace ProjectExplorer;
244244
245245QString DebuggingHelperLibrary::qtVersionForQMake(const QString &qmakePath)
246246{
247 if (qmakePath.isEmpty())
248 return QString();
249
247250 QProcess qmake;
248251 qmake.start(qmakePath, QStringList(QLatin1String("--version")));
249 if (!qmake.waitForFinished())
252 if (!qmake.waitForStarted()) {
253 qWarning("Cannot start '%s': %s", qPrintable(qmakePath), qPrintable(qmake.errorString()));
250254 return QString();
251 QString output = qmake.readAllStandardOutput();
255 }
256 if (!qmake.waitForFinished()) {
257 Utils::SynchronousProcess::stopProcess(qmake);
258 qWarning("Timeout running '%s'.", qPrintable(qmakePath));
259 return QString();
260 }
261 const QString output = QString::fromLocal8Bit(qmake.readAllStandardOutput());
252262 QRegExp regexp(QLatin1String("(QMake version|QMake version:)[\\s]*([\\d.]*)"), Qt::CaseInsensitive);
253263 regexp.indexIn(output);
254264 if (regexp.cap(2).startsWith(QLatin1String("2."))) {
255265 QRegExp regexp2(QLatin1String("Using Qt version[\\s]*([\\d\\.]*)"), Qt::CaseInsensitive);
256266 regexp2.indexIn(output);
257 return regexp2.cap(1);
267 const QString version = regexp2.cap(1);
268 return version;
258269 }
259270 return QString();
260271}
  
3535#include "msvcparser.h"
3636#include "linuxiccparser.h"
3737
38
39#include <utils/synchronousprocess.h>
40
3841#include <QtCore/QDebug>
3942#include <QtCore/QFileInfo>
4043#include <QtCore/QProcess>
178178 return ToolChain::GCC;
179179}
180180
181QByteArray GccToolChain::predefinedMacros()
181static QByteArray gccPredefinedMacros(const QString &gcc, const QStringList &env)
182182{
183 if (m_predefinedMacros.isEmpty()) {
184 QStringList arguments;
185 arguments << QLatin1String("-xc++")
186 << QLatin1String("-E")
187 << QLatin1String("-dM")
188 << QLatin1String("-");
183 QStringList arguments;
184 arguments << QLatin1String("-xc++")
185 << QLatin1String("-E")
186 << QLatin1String("-dM")
187 << QLatin1String("-");
189188
190 QProcess cpp;
191 ProjectExplorer::Environment env = ProjectExplorer::Environment::systemEnvironment();
192 addToEnvironment(env);
193 cpp.setEnvironment(env.toStringList());
194 cpp.start(m_gcc, arguments);
195 cpp.closeWriteChannel();
196 cpp.waitForFinished();
197 m_predefinedMacros = cpp.readAllStandardOutput();
189 QProcess cpp;
190 cpp.setEnvironment(env);
191 cpp.start(gcc, arguments);
192 if (!cpp.waitForStarted()) {
193 qWarning("Cannot start '%s': %s", qPrintable(gcc), qPrintable(cpp.errorString()));
194 return QByteArray();
195 }
196 cpp.closeWriteChannel();
197 if (!cpp.waitForFinished()) {
198 Utils::SynchronousProcess::stopProcess(cpp);
199 qWarning("Timeout running '%s'.", qPrintable(gcc));
200 return QByteArray();
201 }
198202
203 QByteArray predefinedMacros = cpp.readAllStandardOutput();
199204#ifdef Q_OS_MAC
200 // Turn off flag indicating Apple's blocks support
201 const QByteArray blocksDefine("#define __BLOCKS__ 1");
202 const QByteArray blocksUndefine("#undef __BLOCKS__");
203 int idx = m_predefinedMacros.indexOf(blocksDefine);
204 if (idx != -1) {
205 m_predefinedMacros.replace(idx, blocksDefine.length(), blocksUndefine);
206 }
205 // Turn off flag indicating Apple's blocks support
206 const QByteArray blocksDefine("#define __BLOCKS__ 1");
207 const QByteArray blocksUndefine("#undef __BLOCKS__");
208 const int idx = predefinedMacros.indexOf(blocksDefine);
209 if (idx != -1) {
210 predefinedMacros.replace(idx, blocksDefine.length(), blocksUndefine);
211 }
207212
208 // Define __strong and __weak (used for Apple's GC extension of C) to be empty
209 m_predefinedMacros.append("#define __strong\n");
210 m_predefinedMacros.append("#define __weak\n");
213 // Define __strong and __weak (used for Apple's GC extension of C) to be empty
214 predefinedMacros.append("#define __strong\n");
215 predefinedMacros.append("#define __weak\n");
211216#endif // Q_OS_MAC
217 return predefinedMacros;
218}
219
220QByteArray GccToolChain::predefinedMacros()
221{
222 if (m_predefinedMacros.isEmpty()) {
223 ProjectExplorer::Environment env = ProjectExplorer::Environment::systemEnvironment();
224 addToEnvironment(env);
225 m_predefinedMacros = gccPredefinedMacros(m_gcc, env.toStringList());
212226 }
213227 return m_predefinedMacros;
214228}
215229
216QList<HeaderPath> GccToolChain::systemHeaderPaths()
230static QList<HeaderPath> gccSystemHeaderPaths(const QString &gcc, ProjectExplorer::Environment env)
217231{
218 if (m_systemHeaderPaths.isEmpty()) {
219 QStringList arguments;
220 arguments << QLatin1String("-xc++")
221 << QLatin1String("-E")
222 << QLatin1String("-v")
223 << QLatin1String("-");
232 QList<HeaderPath> systemHeaderPaths;
233 QStringList arguments;
234 arguments << QLatin1String("-xc++")
235 << QLatin1String("-E")
236 << QLatin1String("-v")
237 << QLatin1String("-");
224238
225 QProcess cpp;
226 ProjectExplorer::Environment env = ProjectExplorer::Environment::systemEnvironment();
227 addToEnvironment(env);
228 env.set(QLatin1String("LC_ALL"), QLatin1String("C")); //override current locale settings
229 cpp.setEnvironment(env.toStringList());
230 cpp.setReadChannelMode(QProcess::MergedChannels);
231 cpp.start(m_gcc, arguments);
232 cpp.closeWriteChannel();
233 cpp.waitForFinished();
239 QProcess cpp;
240 env.set(QLatin1String("LC_ALL"), QLatin1String("C")); //override current locale settings
241 cpp.setEnvironment(env.toStringList());
242 cpp.setReadChannelMode(QProcess::MergedChannels);
243 cpp.start(gcc, arguments);
244 if (!cpp.waitForStarted()) {
245 qWarning("Cannot start '%s': %s", qPrintable(gcc), qPrintable(cpp.errorString()));
246 return systemHeaderPaths;
247 }
248 cpp.closeWriteChannel();
249 if (!cpp.waitForFinished()) {
250 Utils::SynchronousProcess::stopProcess(cpp);
251 qWarning("Timeout running '%s'.", qPrintable(gcc));
252 return systemHeaderPaths;
253 }
234254
235 QByteArray line;
255 QByteArray line;
256 while (cpp.canReadLine()) {
257 line = cpp.readLine();
258 if (line.startsWith("#include"))
259 break;
260 }
261
262 if (! line.isEmpty() && line.startsWith("#include")) {
263 HeaderPath::Kind kind = HeaderPath::UserHeaderPath;
236264 while (cpp.canReadLine()) {
237265 line = cpp.readLine();
238 if (line.startsWith("#include"))
239 break;
240 }
266 if (line.startsWith("#include")) {
267 kind = HeaderPath::GlobalHeaderPath;
268 } else if (! line.isEmpty() && QChar(line.at(0)).isSpace()) {
269 HeaderPath::Kind thisHeaderKind = kind;
241270
242 if (! line.isEmpty() && line.startsWith("#include")) {
243 HeaderPath::Kind kind = HeaderPath::UserHeaderPath;
244 while (cpp.canReadLine()) {
245 line = cpp.readLine();
246 if (line.startsWith("#include")) {
247 kind = HeaderPath::GlobalHeaderPath;
248 } else if (! line.isEmpty() && QChar(line.at(0)).isSpace()) {
249 HeaderPath::Kind thisHeaderKind = kind;
271 line = line.trimmed();
272 if (line.endsWith('\n'))
273 line.chop(1);
250274
251 line = line.trimmed();
252 if (line.endsWith('\n'))
253 line.chop(1);
254
255 int index = line.indexOf(" (framework directory)");
256 if (index != -1) {
257 line = line.left(index);
258 thisHeaderKind = HeaderPath::FrameworkHeaderPath;
259 }
260
261 m_systemHeaderPaths.append(HeaderPath(QFile::decodeName(line), thisHeaderKind));
262 } else if (line.startsWith("End of search list.")) {
263 break;
264 } else {
265 qWarning() << "ignore line:" << line;
275 const int index = line.indexOf(" (framework directory)");
276 if (index != -1) {
277 line.truncate(index);
278 thisHeaderKind = HeaderPath::FrameworkHeaderPath;
266279 }
280
281 systemHeaderPaths.append(HeaderPath(QFile::decodeName(line), thisHeaderKind));
282 } else if (line.startsWith("End of search list.")) {
283 break;
284 } else {
285 qWarning() << "ignore line:" << line;
267286 }
268287 }
269288 }
289 return systemHeaderPaths;
290}
291
292QList<HeaderPath> GccToolChain::systemHeaderPaths()
293{
294 if (m_systemHeaderPaths.isEmpty()) {
295 ProjectExplorer::Environment env = ProjectExplorer::Environment::systemEnvironment();
296 addToEnvironment(env);
297 m_systemHeaderPaths = gccSystemHeaderPaths(m_gcc, env);
298 }
270299 return m_systemHeaderPaths;
271300}
272301
619619 return file;
620620}
621621
622QByteArray MSVCToolChain::predefinedMacros()
622// Run MSVC 'cl' compiler to obtain #defines.
623static QByteArray msvcPredefinedMacros(const QStringList &env)
623624{
624 if (m_predefinedMacros.isEmpty()) {
625 m_predefinedMacros += "#define __MSVCRT__\n"
626 "#define __w64\n"
627 "#define __int64 long long\n"
628 "#define __int32 long\n"
629 "#define __int16 short\n"
630 "#define __int8 char\n"
631 "#define __ptr32\n"
632 "#define __ptr64\n";
625 QByteArray predefinedMacros = "#define __MSVCRT__\n"
626 "#define __w64\n"
627 "#define __int64 long long\n"
628 "#define __int32 long\n"
629 "#define __int16 short\n"
630 "#define __int8 char\n"
631 "#define __ptr32\n"
632 "#define __ptr64\n";
633633
634 QString tmpFilePath;
635 {
636 // QTemporaryFile is buggy and will not unlock the file for cl.exe
637 QTemporaryFile tmpFile(QDir::tempPath()+"/envtestXXXXXX.cpp");
638 tmpFile.setAutoRemove(false);
639 if (!tmpFile.open())
640 return m_predefinedMacros;
641 tmpFilePath = QFileInfo(tmpFile).canonicalFilePath();
642 tmpFile.write(msvcCompilationFile());
643 tmpFile.close();
634 QString tmpFilePath;
635 {
636 // QTemporaryFile is buggy and will not unlock the file for cl.exe
637 QTemporaryFile tmpFile(QDir::tempPath()+"/envtestXXXXXX.cpp");
638 tmpFile.setAutoRemove(false);
639 if (!tmpFile.open())
640 return predefinedMacros;
641 tmpFilePath = QFileInfo(tmpFile).canonicalFilePath();
642 tmpFile.write(msvcCompilationFile());
643 tmpFile.close();
644 }
645 QProcess cpp;
646 cpp.setEnvironment(env);
647 cpp.setWorkingDirectory(QDir::tempPath());
648 QStringList arguments;
649 const QString binary = QLatin1String("cl.exe");
650 arguments << QLatin1String("/EP") << QDir::toNativeSeparators(tmpFilePath);
651 cpp.start(QLatin1String("cl.exe"), arguments);
652 if (!cpp.waitForStarted()) {
653 qWarning("Cannot start '%s': %s", qPrintable(binary), qPrintable(cpp.errorString()));
654 return predefinedMacros;
655 }
656 cpp.closeWriteChannel();
657 if (!cpp.waitForFinished()) {
658 Utils::SynchronousProcess::stopProcess(cpp);
659 qWarning("Timeout running '%s'.", qPrintable(binary));
660 return predefinedMacros;
661 }
662 const QList<QByteArray> output = cpp.readAllStandardOutput().split('\n');
663 foreach (const QByteArray& line, output) {
664 if (line.startsWith('V')) {
665 QList<QByteArray> split = line.split('=');
666 const QByteArray key = split.at(0).mid(1);
667 QByteArray value = split.at(1);
668 if (!value.isEmpty()) {
669 value.chop(1); //remove '\n'
670 }
671 predefinedMacros += "#define ";
672 predefinedMacros += key;
673 predefinedMacros += ' ';
674 predefinedMacros += value;
675 predefinedMacros += '\n';
644676 }
677 }
678 QFile::remove(tmpFilePath);
679 return predefinedMacros;
680}
681
682QByteArray MSVCToolChain::predefinedMacros()
683{
684 if (m_predefinedMacros.isEmpty()) {
645685 ProjectExplorer::Environment env = ProjectExplorer::Environment::systemEnvironment();
646686 addToEnvironment(env);
647 QProcess cpp;
648 cpp.setEnvironment(env.toStringList());
649 cpp.setWorkingDirectory(QDir::tempPath());
650 QStringList arguments;
651 arguments << "/EP" << QDir::toNativeSeparators(tmpFilePath);
652 cpp.start(QLatin1String("cl.exe"), arguments);
653 cpp.closeWriteChannel();
654 cpp.waitForFinished();
655 QList<QByteArray> output = cpp.readAllStandardOutput().split('\n');
656 foreach (const QByteArray& line, output) {
657 if (line.startsWith('V')) {
658 QList<QByteArray> split = line.split('=');
659 QByteArray key = split.at(0).mid(1);
660 QByteArray value = split.at(1);
661 if (!value.isEmpty()) {
662 value.chop(1); //remove '\n'
663 }
664 QByteArray newDefine = "#define " + key + ' ' + value + '\n';
665 m_predefinedMacros.append(newDefine);
666 }
667 }
668 QFile::remove(tmpFilePath);
687 m_predefinedMacros = msvcPredefinedMacros(env.toStringList());
669688 }
670689 return m_predefinedMacros;
671690}
763763 }
764764 call += "\r\n";
765765 tf.write(call);
766 QString redirect = "set > \"" + tempOutputFileName + "\"\r\n";
767 tf.write(redirect.toLocal8Bit());
766 const QByteArray redirect = "set > \"" + QDir::toNativeSeparators(tempOutputFileName).toLocal8Bit() + "\"\r\n";
767 tf.write(redirect);
768768 tf.flush();
769769 tf.waitForBytesWritten(30000);
770770
771771 QProcess run;
772772 run.setEnvironment(env.toStringList());
773773 const QString cmdPath = QString::fromLocal8Bit(qgetenv("COMSPEC"));
774 run.start(cmdPath, QStringList()<< QLatin1String("/c")<<filename);
775 if (!run.waitForStarted() || !run.waitForFinished())
774 run.start(cmdPath, QStringList()<< QLatin1String("/c")<<QDir::toNativeSeparators(filename));
775 if (!run.waitForStarted()) {
776 qWarning("Unable to run '%s': %s", qPrintable(varsBat), qPrintable(run.errorString()));
776777 return StringStringPairList();
778 }
779 if (!run.waitForFinished()) {
780 qWarning("Timeout running '%s'", qPrintable(varsBat));
781 Utils::SynchronousProcess::stopProcess(run);
782 return StringStringPairList();
783 }
777784 tf.close();
778785
779786 QFile varsFile(tempOutputFileName);
  
4141#include <projectexplorer/projectexplorer.h>
4242#include <projectexplorer/projectexplorerconstants.h>
4343#include <projectexplorer/cesdkhandler.h>
44#include <utils/synchronousprocess.h>
4445#include <coreplugin/coreconstants.h>
4546#include <coreplugin/icore.h>
4647#include <coreplugin/helpmanager.h>
10611061 return result;
10621062}
10631063
1064void QtVersion::updateVersionInfo() const
1064static bool queryQMakeVariables(const QString &binary, QHash<QString, QString> *versionInfo)
10651065{
1066 if (m_versionInfoUpToDate)
1067 return;
1068
1069 // extract data from qmake executable
1070 m_versionInfo.clear();
1071 m_notInstalled = false;
1072 m_hasExamples = false;
1073 m_hasDocumentation = false;
1074 m_hasDebuggingHelper = false;
1075
1076 QFileInfo qmake(qmakeCommand());
1077 if (qmake.exists() && qmake.isExecutable()) {
1078 static const char * const variables[] = {
1066 const int timeOutMS = 30000; // Might be slow on some machines.
1067 QFileInfo qmake(binary);
1068 if (!qmake.exists() || !qmake.isExecutable())
1069 return false;
1070 static const char * const variables[] = {
10791071 "QT_VERSION",
10801072 "QT_INSTALL_DATA",
10811073 "QT_INSTALL_LIBS",
10821082 "QT_INSTALL_PREFIX",
10831083 "QMAKEFEATURES"
10841084 };
1085 QStringList args;
1086 for (uint i = 0; i < sizeof variables / sizeof variables[0]; ++i)
1087 args << "-query" << variables[i];
1088 QProcess process;
1089 process.start(qmake.absoluteFilePath(), args, QIODevice::ReadOnly);
1090 if (process.waitForFinished(10000)) {
1091 QByteArray output = process.readAllStandardOutput();
1092 QTextStream stream(&output);
1093 while (!stream.atEnd()) {
1094 const QString line = stream.readLine();
1095 const int index = line.indexOf(QLatin1Char(':'));
1096 if (index != -1) {
1097 QString value = QDir::fromNativeSeparators(line.mid(index+1));
1098 if (value != "**Unknown**")
1099 m_versionInfo.insert(line.left(index), value);
1100 }
1101 }
1085 QStringList args;
1086 for (uint i = 0; i < sizeof variables / sizeof variables[0]; ++i)
1087 args << "-query" << variables[i];
1088 QProcess process;
1089 process.start(qmake.absoluteFilePath(), args, QIODevice::ReadOnly);
1090 if (!process.waitForStarted()) {
1091 qWarning("Cannot start '%s': %s", qPrintable(binary), qPrintable(process.errorString()));
1092 return false;
1093 }
1094 if (!process.waitForFinished(timeOutMS)) {
1095 Utils::SynchronousProcess::stopProcess(process);
1096 qWarning("Timeout running '%s' (%dms).", qPrintable(binary), timeOutMS);
1097 return false;
1098 }
1099 QByteArray output = process.readAllStandardOutput();
1100 QTextStream stream(&output);
1101 while (!stream.atEnd()) {
1102 const QString line = stream.readLine();
1103 const int index = line.indexOf(QLatin1Char(':'));
1104 if (index != -1) {
1105 const QString value = QDir::fromNativeSeparators(line.mid(index+1));
1106 if (value != "**Unknown**")
1107 versionInfo->insert(line.left(index), value);
11021108 }
1109 }
1110 return true;
1111}
11031112
1104 if (m_versionInfo.contains("QT_INSTALL_DATA")) {
1105 QString qtInstallData = m_versionInfo.value("QT_INSTALL_DATA");
1106 m_versionInfo.insert("QMAKE_MKSPECS", QDir::cleanPath(qtInstallData+"/mkspecs"));
1113void QtVersion::updateVersionInfo() const
1114{
1115 if (m_versionInfoUpToDate)
1116 return;
11071117
1108 if (!qtInstallData.isEmpty())
1109 m_hasDebuggingHelper = !DebuggingHelperLibrary::debuggingHelperLibraryByInstallData(qtInstallData).isEmpty();
1110 }
1118 // extract data from qmake executable
1119 m_versionInfo.clear();
1120 m_notInstalled = false;
1121 m_hasExamples = false;
1122 m_hasDocumentation = false;
1123 m_hasDebuggingHelper = false;
11111124
1112 // Now check for a qt that is configured with a prefix but not installed
1113 if (m_versionInfo.contains("QT_INSTALL_BINS")) {
1114 QFileInfo fi(m_versionInfo.value("QT_INSTALL_BINS"));
1115 if (!fi.exists())
1116 m_notInstalled = true;
1117 }
1118 if (m_versionInfo.contains("QT_INSTALL_HEADERS")){
1119 QFileInfo fi(m_versionInfo.value("QT_INSTALL_HEADERS"));
1120 if (!fi.exists())
1121 m_notInstalled = true;
1122 }
1123 if (m_versionInfo.contains("QT_INSTALL_DOCS")){
1124 QFileInfo fi(m_versionInfo.value("QT_INSTALL_DOCS"));
1125 if (fi.exists())
1126 m_hasDocumentation = true;
1127 }
1128 if (m_versionInfo.contains("QT_INSTALL_EXAMPLES")){
1129 QFileInfo fi(m_versionInfo.value("QT_INSTALL_EXAMPLES"));
1130 if (fi.exists())
1131 m_hasExamples = true;
1132 }
1133 if (m_versionInfo.contains("QT_INSTALL_DEMOS")){
1134 QFileInfo fi(m_versionInfo.value("QT_INSTALL_DEMOS"));
1135 if (fi.exists())
1136 m_hasDemos = true;
1137 }
1125 if (!queryQMakeVariables(qmakeCommand(), &m_versionInfo))
1126 return;
1127
1128 if (m_versionInfo.contains("QT_INSTALL_DATA")) {
1129 QString qtInstallData = m_versionInfo.value("QT_INSTALL_DATA");
1130 m_versionInfo.insert("QMAKE_MKSPECS", QDir::cleanPath(qtInstallData+"/mkspecs"));
1131
1132 if (!qtInstallData.isEmpty())
1133 m_hasDebuggingHelper = !DebuggingHelperLibrary::debuggingHelperLibraryByInstallData(qtInstallData).isEmpty();
1134 }
1135
1136 // Now check for a qt that is configured with a prefix but not installed
1137 if (m_versionInfo.contains("QT_INSTALL_BINS")) {
1138 QFileInfo fi(m_versionInfo.value("QT_INSTALL_BINS"));
1139 if (!fi.exists())
1140 m_notInstalled = true;
1141 }
1142 if (m_versionInfo.contains("QT_INSTALL_HEADERS")){
1143 QFileInfo fi(m_versionInfo.value("QT_INSTALL_HEADERS"));
1144 if (!fi.exists())
1145 m_notInstalled = true;
1146 }
1147 if (m_versionInfo.contains("QT_INSTALL_DOCS")){
1148 QFileInfo fi(m_versionInfo.value("QT_INSTALL_DOCS"));
1149 if (fi.exists())
1150 m_hasDocumentation = true;
1151 }
1152 if (m_versionInfo.contains("QT_INSTALL_EXAMPLES")){
1153 QFileInfo fi(m_versionInfo.value("QT_INSTALL_EXAMPLES"));
1154 if (fi.exists())
1155 m_hasExamples = true;
1156 }
1157 if (m_versionInfo.contains("QT_INSTALL_DEMOS")){
1158 QFileInfo fi(m_versionInfo.value("QT_INSTALL_DEMOS"));
1159 if (fi.exists())
1160 m_hasDemos = true;
11381161 }
11391162
11401163 m_versionInfoUpToDate = true;