From c2cfa6dd833f60d75cc7aafe7282d052cdae5257 Mon Sep 17 00:00:00 2001 From: Tim Jenssen Date: Fri, 31 May 2013 16:40:12 +0200 Subject: adding ConsumeOutputOperation - With that operation we can save the original qmake output, before the installer does the patching. So we can use the old values for modules patching if they will be installed later. - added a autotest which does that with the current used qmake Change-Id: Ie07d273bf64d1813b7af3374a0db35a9e1215503 Reviewed-by: Niels Weber Reviewed-by: Karsten Heimrich --- src/libs/installer/consumeoutputoperation.cpp | 158 ++++++++++++++++++++++++++ src/libs/installer/consumeoutputoperation.h | 65 +++++++++++ src/libs/installer/init.cpp | 2 + src/libs/installer/installer.pro | 2 + src/libs/installer/qtpatch.cpp | 57 +++------- src/libs/installer/qtpatch.h | 1 + src/libs/installer/qtpatchoperation.cpp | 68 +++++++---- src/libs/installer/qtpatchoperation.h | 2 +- src/libs/installer/utils.cpp | 34 ++++++ src/libs/installer/utils.h | 1 + 10 files changed, 328 insertions(+), 62 deletions(-) create mode 100644 src/libs/installer/consumeoutputoperation.cpp create mode 100644 src/libs/installer/consumeoutputoperation.h (limited to 'src') diff --git a/src/libs/installer/consumeoutputoperation.cpp b/src/libs/installer/consumeoutputoperation.cpp new file mode 100644 index 000000000..2d3bc1ec0 --- /dev/null +++ b/src/libs/installer/consumeoutputoperation.cpp @@ -0,0 +1,158 @@ +/************************************************************************** +** +** Copyright (C) 2012-2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#include "consumeoutputoperation.h" +#include "packagemanagercore.h" +#include "utils.h" + +#include +#include +#include +#include + +using namespace QInstaller; + +ConsumeOutputOperation::ConsumeOutputOperation() +{ + setName(QLatin1String("ConsumeOutput")); +} + +void ConsumeOutputOperation::backup() +{ +} + +bool ConsumeOutputOperation::performOperation() +{ + // Arguments: + // 1. key where the output will be saved + // 2. executable path + // 3. argument for the executable + // 4. more arguments possible ... + if (arguments().count() < 3) { + setError(InvalidArguments); + setErrorString(tr("Invalid arguments in %0: %1 arguments given, %2 expected%3.").arg(name()).arg( + arguments().count()).arg(tr("at least 2"), QLatin1String("(, " + ", [argument1], [argument2], ...)"))); + return false; + } + + PackageManagerCore *const core = value(QLatin1String("installer")).value(); + if (!core) { + setError(UserDefinedError); + setErrorString(tr("Needed installer object in %1 operation is empty.").arg(name())); + return false; + } + + const QString installerKeyName = arguments().at(0); + if (installerKeyName.isEmpty()) { + setError(UserDefinedError); + setErrorString(tr("Can not save the output of %1 to an empty installer key value.").arg( + arguments().at(1))); + return false; + } + + QString executablePath = arguments().at(1); +#ifdef Q_OS_WIN + if (!QFile::exists(executablePath)) + executablePath = executablePath + QLatin1String(".exe"); +#endif + + const QFileInfo executable(executablePath); + if (!executable.exists() || !executable.isExecutable()) { + setError(UserDefinedError); + setErrorString(tr("File '%1' does not exist or is not an executable binary.").arg( + QDir::toNativeSeparators(executable.absoluteFilePath()))); + return false; + } + + QByteArray executableOutput; + QProcess process; + + + const QStringList processArguments = arguments().mid(2); + // in some cases it is not runable, because another process is blocking it(filewatcher ...) + int waitCount = 0; + while (executableOutput.isEmpty() && waitCount < 60) { + + process.start(executable.absoluteFilePath(), processArguments, QIODevice::ReadOnly); + if (process.waitForFinished(2000)) { + if (process.exitStatus() == QProcess::CrashExit) { + setError(UserDefinedError); + setErrorString(tr("Running '%1' resulted in a crash.").arg( + QDir::toNativeSeparators(executable.absoluteFilePath()))); + return false; + } + executableOutput.append(process.readAllStandardOutput()); + } + if (executableOutput.isEmpty()) { + ++waitCount; + static const int waitTimeInMilliSeconds = 500; + uiDetachedWait(waitTimeInMilliSeconds); + } + if (process.state() > QProcess::NotRunning ) { + qWarning() << executable.absoluteFilePath() << "process is still running, need to kill it."; + process.kill(); + } + + } + if (executableOutput.isEmpty()) { + qWarning() << QString::fromLatin1("Can't get any query output from executable: '%1'").arg( + executable.absoluteFilePath()); + } + core->setValue(installerKeyName, QString::fromLatin1(executableOutput)); + return true; +} + +bool ConsumeOutputOperation::undoOperation() +{ + return true; +} + +bool ConsumeOutputOperation::testOperation() +{ + return true; +} + +Operation *ConsumeOutputOperation::clone() const +{ + return new ConsumeOutputOperation(); +} + diff --git a/src/libs/installer/consumeoutputoperation.h b/src/libs/installer/consumeoutputoperation.h new file mode 100644 index 000000000..f776cf35a --- /dev/null +++ b/src/libs/installer/consumeoutputoperation.h @@ -0,0 +1,65 @@ +/************************************************************************** +** +** Copyright (C) 2012-2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Installer Framework. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +**************************************************************************/ + +#ifndef CONSUMEOUTPUTOPERATION_H +#define CONSUMEOUTPUTOPERATION_H + +#include "qinstallerglobal.h" + +namespace QInstaller { + +class INSTALLER_EXPORT ConsumeOutputOperation : public Operation +{ +public: + ConsumeOutputOperation(); + + void backup(); + bool performOperation(); + bool undoOperation(); + bool testOperation(); + Operation *clone() const; + +private: +}; + +} // namespace QInstaller + +#endif // CONSUMEOUTPUTOPERATION_H diff --git a/src/libs/installer/init.cpp b/src/libs/installer/init.cpp index 0fcc3c6a7..a8a7f54d8 100644 --- a/src/libs/installer/init.cpp +++ b/src/libs/installer/init.cpp @@ -62,6 +62,7 @@ // QtSDK specific #include "qtpatchoperation.h" +#include "consumeoutputoperation.h" #include "setdemospathonqtoperation.h" #include "setexamplespathonqtoperation.h" #include "setpluginpathonqtcoreoperation.h" @@ -243,6 +244,7 @@ void QInstaller::init() factory.registerUpdateOperation(QLatin1String("MinimumProgress")); factory.registerUpdateOperation(QLatin1String("License")); factory.registerUpdateOperation(QLatin1String("ApplyProductKey")); + factory.registerUpdateOperation(QLatin1String("ConsumeOutput")); // QtSDK specific factory.registerUpdateOperation(QLatin1String("RegisterQtInCreatorQNX")); diff --git a/src/libs/installer/installer.pro b/src/libs/installer/installer.pro index ec884e5a3..ab28aa72b 100644 --- a/src/libs/installer/installer.pro +++ b/src/libs/installer/installer.pro @@ -55,6 +55,7 @@ HEADERS += packagemanagercore.h \ persistentsettings.h \ projectexplorer_export.h \ qtpatchoperation.h \ + consumeoutputoperation.h \ setpathonqtcoreoperation.h \ setdemospathonqtoperation.h \ setexamplespathonqtoperation.h \ @@ -128,6 +129,7 @@ HEADERS += packagemanagercore.h \ qtpatch.cpp \ persistentsettings.cpp \ qtpatchoperation.cpp \ + consumeoutputoperation.cpp \ setpathonqtcoreoperation.cpp \ setdemospathonqtoperation.cpp \ setexamplespathonqtoperation.cpp \ diff --git a/src/libs/installer/qtpatch.cpp b/src/libs/installer/qtpatch.cpp index 02795b34b..6581815c2 100644 --- a/src/libs/installer/qtpatch.cpp +++ b/src/libs/installer/qtpatch.cpp @@ -40,6 +40,7 @@ **************************************************************************/ #include "qtpatch.h" +#include "utils.h" #include #include @@ -52,35 +53,20 @@ #include #include -#ifdef Q_OS_WIN -#include // for Sleep -#endif -#ifdef Q_OS_UNIX -#include -#include -#include -#endif - -static void sleepCopiedFromQTest(int ms) +QHash QtPatch::readQmakeOutput(const QByteArray &data) { - if (ms < 0) - return; -#ifdef Q_OS_WIN - Sleep(uint(ms)); -#else - struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; - nanosleep(&ts, NULL); -#endif -} - -static void uiDetachedWait(int ms) -{ - QTime timer; - timer.start(); - do { - QCoreApplication::processEvents(QEventLoop::AllEvents, ms); - sleepCopiedFromQTest(10); - } while (timer.elapsed() < ms); + QHash qmakeValueHash; + QTextStream stream(data, QIODevice::ReadOnly); + while (!stream.atEnd()) { + const QString line = stream.readLine(); + const int index = line.indexOf(QLatin1Char(':')); + if (index != -1) { + QString value = line.mid(index+1); + if (value != QLatin1String("**Unknown**") ) + qmakeValueHash.insert(line.left(index), value.toUtf8()); + } + } + return qmakeValueHash; } QHash QtPatch::qmakeValues(const QString &qmakePath, QByteArray *qmakeOutput) @@ -113,21 +99,12 @@ QHash QtPatch::qmakeValues(const QString &qmakePath, QByteA } QByteArray output = process.readAllStandardOutput(); qmakeOutput->append(output); - QTextStream stream(&output); - while (!stream.atEnd()) { - const QString line = stream.readLine(); - const int index = line.indexOf(QLatin1Char(':')); - if (index != -1) { - QString value = line.mid(index+1); - if (value != QLatin1String("**Unknown**") ) - qmakeValueHash.insert(line.left(index), value.toUtf8()); - } - } + qmakeValueHash = readQmakeOutput(output); } if (qmakeValueHash.isEmpty()) { ++waitCount; static const int waitTimeInMilliSeconds = 500; - uiDetachedWait(waitTimeInMilliSeconds); + QInstaller::uiDetachedWait(waitTimeInMilliSeconds); } if (process.state() > QProcess::NotRunning ) { qDebug() << "qmake process is still running, need to kill it."; @@ -232,7 +209,7 @@ bool QtPatch::openFileForPatching(QFile *file) while (!file->open(QFile::ReadWrite) && waitCount < 60) { ++waitCount; static const int waitTimeInMilliSeconds = 500; - uiDetachedWait(waitTimeInMilliSeconds); + QInstaller::uiDetachedWait(waitTimeInMilliSeconds); } return file->openMode() == QFile::ReadWrite; } diff --git a/src/libs/installer/qtpatch.h b/src/libs/installer/qtpatch.h index 5868e6ce2..13c7df84a 100644 --- a/src/libs/installer/qtpatch.h +++ b/src/libs/installer/qtpatch.h @@ -50,6 +50,7 @@ namespace QtPatch { +QHash INSTALLER_EXPORT readQmakeOutput(const QByteArray &data); QHash INSTALLER_EXPORT qmakeValues(const QString &qmakePath, QByteArray *qmakeOutput); bool INSTALLER_EXPORT patchBinaryFile(const QString &fileName, diff --git a/src/libs/installer/qtpatchoperation.cpp b/src/libs/installer/qtpatchoperation.cpp index 018d4e24a..997b92b77 100644 --- a/src/libs/installer/qtpatchoperation.cpp +++ b/src/libs/installer/qtpatchoperation.cpp @@ -176,7 +176,24 @@ bool QtPatchOperation::performOperation() return false; } - QString type = arguments().at(0); + QStringList args = arguments(); + QString qmakeOutputInstallerKey; + QStringList filteredQmakeOutputInstallerKey = args.filter(QLatin1String("QmakeOutputInstallerKey="), + Qt::CaseInsensitive); + PackageManagerCore *const core = value(QLatin1String("installer")).value(); + if (!filteredQmakeOutputInstallerKey.isEmpty()) { + if (!core) { + setError(UserDefinedError); + setErrorString(tr("Needed installer object in \"%1\" operation is empty.").arg(name())); + return false; + } + QString qmakeOutputInstallerKeyArgument = filteredQmakeOutputInstallerKey.at(0); + qmakeOutputInstallerKey = qmakeOutputInstallerKeyArgument; + qmakeOutputInstallerKey.replace(QLatin1String("QmakeOutputInstallerKey="), QString(), Qt::CaseInsensitive); + args.removeAll(qmakeOutputInstallerKeyArgument); + } + + QString type = args.at(0); bool isPlatformSupported = type.contains(QLatin1String("linux"), Qt::CaseInsensitive) || type.contains(QLatin1String("windows"), Qt::CaseInsensitive) || type.contains(QLatin1String("mac"), Qt::CaseInsensitive); @@ -187,31 +204,42 @@ bool QtPatchOperation::performOperation() return false; } - const QString newQtPathStr = QDir::toNativeSeparators(arguments().at(1)); - const QByteArray newQtPath = newQtPathStr.toUtf8(); + if (!filteredQmakeOutputInstallerKey.isEmpty() && core->value(qmakeOutputInstallerKey).isEmpty()) { + setError(UserDefinedError); + setErrorString(tr("Could not find the needed QmakeOutputInstallerKey(%1) value on the installer " + "object. The ConsumeOutput operation on the valid qmake needs to be called first.").arg( + qmakeOutputInstallerKey)); + return false; + } + const QString newQtPathStr = QDir::toNativeSeparators(args.at(1)); + const QByteArray newQtPath = newQtPathStr.toUtf8(); QString qmakePath = QString::fromUtf8(newQtPath) + QLatin1String("/bin/qmake"); #ifdef Q_OS_WIN qmakePath = qmakePath + QLatin1String(".exe"); #endif - if (!QFile::exists(qmakePath)) { - setError(UserDefinedError); - setErrorString(tr("QMake from the current Qt version \n(%1)is not existing. Please file a bugreport " - "with this dialog at https://bugreports.qt-project.org.").arg(QDir::toNativeSeparators(qmakePath))); - return false; + QHash qmakeValueHash; + if (!core->value(qmakeOutputInstallerKey).isEmpty()) { + qmakeValueHash = QtPatch::readQmakeOutput(core->value(qmakeOutputInstallerKey).toLatin1()); + } else { + if (!QFile::exists(qmakePath)) { + setError(UserDefinedError); + setErrorString(tr("QMake from the current Qt version \n(%1)is not existing. Please file a bugreport " + "with this dialog at https://bugreports.qt-project.org.").arg(QDir::toNativeSeparators(qmakePath))); + return false; + } + QByteArray qmakeOutput; + qmakeValueHash = QtPatch::qmakeValues(qmakePath, &qmakeOutput); + if (qmakeValueHash.isEmpty()) { + setError(UserDefinedError); + setErrorString(tr("The output of \n%1 -query\nis not parseable. Please file a bugreport with this " + "dialog https://bugreports.qt-project.org.\noutput: \"%2\"").arg(QDir::toNativeSeparators(qmakePath), + QString::fromUtf8(qmakeOutput))); + return false; + } } - QByteArray qmakeOutput; - QHash qmakeValueHash = QtPatch::qmakeValues(qmakePath, &qmakeOutput); - - if (qmakeValueHash.isEmpty()) { - setError(UserDefinedError); - setErrorString(tr("The output of \n%1 -query\nis not parseable. Please file a bugreport with this " - "dialog https://bugreports.qt-project.org.\noutput: \"%2\"").arg(QDir::toNativeSeparators(qmakePath), - QString::fromUtf8(qmakeOutput))); - return false; - } const QByteArray oldQtPath = qmakeValueHash.value(QLatin1String("QT_INSTALL_PREFIX")); bool oldQtPathFromQMakeIsEmpty = oldQtPath.isEmpty(); @@ -246,13 +274,11 @@ bool QtPatchOperation::performOperation() fileName = QString::fromLatin1(":/files-to-patch-windows"); else if (type == QLatin1String("linux")) fileName = QString::fromLatin1(":/files-to-patch-linux"); - else if (type == QLatin1String("linux-emb-arm")) - fileName = QString::fromLatin1(":/files-to-patch-linux-emb-arm"); else if (type == QLatin1String("mac")) fileName = QString::fromLatin1(":/files-to-patch-macx"); QFile patchFileListFile(fileName); - QString version = arguments().value(2).toLower(); + QString version = args.value(2).toLower(); if (!version.isEmpty()) patchFileListFile.setFileName(fileName + QLatin1Char('-') + version); diff --git a/src/libs/installer/qtpatchoperation.h b/src/libs/installer/qtpatchoperation.h index f1919f4c6..346466c43 100644 --- a/src/libs/installer/qtpatchoperation.h +++ b/src/libs/installer/qtpatchoperation.h @@ -46,7 +46,7 @@ namespace QInstaller { -class QtPatchOperation : public Operation +class INSTALLER_EXPORT QtPatchOperation : public Operation { public: QtPatchOperation(); diff --git a/src/libs/installer/utils.cpp b/src/libs/installer/utils.cpp index 1112bcebb..2b5491f62 100644 --- a/src/libs/installer/utils.cpp +++ b/src/libs/installer/utils.cpp @@ -45,6 +45,7 @@ #include #include #include +#include #if defined(Q_OS_WIN32) || defined(Q_OS_WINCE) # include "qt_windows.h" @@ -54,6 +55,39 @@ #include #include + +#ifdef Q_OS_WIN +#include // for Sleep +#endif +#ifdef Q_OS_UNIX +#include +#include +#include +#endif + +namespace { +void sleepCopiedFromQTest(int ms) +{ + if (ms < 0) + return; +#ifdef Q_OS_WIN + Sleep(uint(ms)); +#else + struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; + nanosleep(&ts, NULL); +#endif +} +} +void QInstaller::uiDetachedWait(int ms) +{ + QTime timer; + timer.start(); + do { + QCoreApplication::processEvents(QEventLoop::AllEvents, ms); + sleepCopiedFromQTest(10); + } while (timer.elapsed() < ms); +} + static bool verb = false; void QInstaller::setVerbose(bool v) diff --git a/src/libs/installer/utils.h b/src/libs/installer/utils.h index e6aca456d..229dfe95a 100644 --- a/src/libs/installer/utils.h +++ b/src/libs/installer/utils.h @@ -57,6 +57,7 @@ class QIODevice; QT_END_NAMESPACE namespace QInstaller { + void INSTALLER_EXPORT uiDetachedWait(int ms); QByteArray INSTALLER_EXPORT calculateHash(QIODevice *device, QCryptographicHash::Algorithm algo); QByteArray INSTALLER_EXPORT calculateHash(const QString &path, QCryptographicHash::Algorithm algo); -- cgit v1.2.3