diff options
42 files changed, 546 insertions, 244 deletions
@@ -1,3 +1,11 @@ +1.4.1 +- Added support to pass a query string when downloading archives. (QTIFW-329) +- Fixed progress display for redirected HTTP Downloads. (QTIFW-267) +- Add support to repogen to update only newer components. (QTIFW-234) +- Added more autotests. +- Improved documentation. +- Minor bugfixes. + 1.4 - Force updating of Essential components. (QTIFW-38, QTIFW-155) - Display release date in Updater and Packagemanager. (QTIFW-25) @@ -41,6 +49,7 @@ - Minor documentation fixes and additions. - Added more autotests. - Bugfixes +- Added Japanese translation. - Updated translations - Cleaned up the Code. diff --git a/doc/installerfw.qdoc b/doc/installerfw.qdoc index baf6f1261..9cfb94d06 100644 --- a/doc/installerfw.qdoc +++ b/doc/installerfw.qdoc @@ -199,10 +199,10 @@ \o Filename for a watermark used as \a QWizard::WatermarkPixmap. \row \o Banner - \o Filename for a banner used as \a QWizard::BannerPixmap. + \o Filename for a banner used as \a QWizard::BannerPixmap (only used by ModernStyle). \row \o Background - \o Filename for an image used as \a QWizard::BackgroundPixmap. + \o Filename for an image used as \a QWizard::BackgroundPixmap (only used by MacStyle). \row \o RunProgram \o Command executed after the installer is done if the user accepts @@ -265,6 +265,10 @@ \o List of language codes to be used for translating the user interface. To add several language variants, specify several Translation sections that each specify the name of a language variant. Optional. For more information, see \l{Translating Pages}. + \row + \o UrlQueryString + \o This string needs to be in the form "key=value" and will be appended to archive download + requests. This can be used to transmit information to the webserver hosting the repository. \endtable @@ -445,6 +449,7 @@ \row \o Virtual \o Set to \c true to hide the component from the installer. + Note that setting this on a root component does not work. \row \o SortingPriority \o Priority of the component in the tree. The tree is sorted from @@ -1007,34 +1012,35 @@ Updates.xml file in the current repository. You can add, replace, or remove repositories. + \code + <RepositoryUpdate> + <Repository action="..." OPTIONS /> + <Repository action="..." OPTIONS /> + </RepositoryUpdate> + \endcode + \section2 Adding Repositories - To update a repository, add the following code to Updates.xml: + To update a repository, add the following code to the RepositoryUpdate section: \code - <RepositoryUpdate> - <Repository action="add" url="http://www.example.com/repository" name="user" password="password" + <Repository action="add" url="http://www.example.com/repository" name="user" password="password" displayname="Example Repository" /> - </RepositoryUpdate> \endcode \section2 Removing Repositories - To remove a repository, add the following code to Updates.xml: + To remove a repository, add the following code to the RepositoryUpdate section: \code - <RepositoryUpdate> - <Repository action="remove" url="http://www.example.com/repository" /> - </RepositoryUpdate> + <Repository action="remove" url="http://www.example.com/repository" /> \endcode \section2 Replacing repositories - To replace one repository with another, add the following code to Updates.xml: + To replace one repository with another, add the following code to the RepositoryUpdate section: \code - <RepositoryUpdate> - <Repository action="replace" oldurl="http://www.example.com/repository" - newurl="http://www.example.com/newrepository" name="user" password="password" - displayname="New Example Repository" /> - </RepositoryUpdate> + <Repository action="replace" oldurl="http://www.example.com/repository" + newurl="http://www.example.com/newrepository" name="user" password="password" + displayname="New Example Repository" /> \endcode */ diff --git a/installerfw.pri b/installerfw.pri index ad559c410..62a82b6d9 100644 --- a/installerfw.pri +++ b/installerfw.pri @@ -56,6 +56,9 @@ unix:INCLUDEPATH += $$IFW_SOURCE_TREE/src/libs/7zip/unix/CPP LIBS += -L$$IFW_LIB_PATH # The order is important. The linker needs to parse archives in reversed dependency order. equals(TEMPLATE, app):LIBS += -linstaller +win32:equals(TEMPLATE, app) { + LIBS += -luser32 +} unix:!macx:LIBS += -lutil macx:LIBS += -framework Carbon -framework Security @@ -106,6 +109,9 @@ static { LIBS += -l7z win32-g++*: LIBS += -lmpr -luuid - win32:exists($$IFW_LIB_PATH/installer.lib):POST_TARGETDEPS += $$IFW_LIB_PATH/installer.lib - unix:exists($$IFW_LIB_PATH/libinstaller.a):POST_TARGETDEPS += $$IFW_LIB_PATH/libinstaller.a + equals(TEMPLATE, app) { + win32-msvc*:POST_TARGETDEPS += $$IFW_LIB_PATH/installer.lib $$IFW_LIB_PATH/7z.lib + win32-g++*:POST_TARGETDEPS += $$IFW_LIB_PATH/libinstaller.a $$IFW_LIB_PATH/lib7z.a + unix:POST_TARGETDEPS += $$IFW_LIB_PATH/libinstaller.a $$IFW_LIB_PATH/lib7z.a + } } diff --git a/src/libs/7zip/7zip.pro b/src/libs/7zip/7zip.pro index 549778e67..01b69dad2 100644 --- a/src/libs/7zip/7zip.pro +++ b/src/libs/7zip/7zip.pro @@ -1,8 +1,7 @@ -include(../../../installerfw.pri) - QT = core TARGET = 7z TEMPLATE = lib +include(../../../installerfw.pri) INCLUDEPATH += . .. CONFIG += staticlib DESTDIR = $$IFW_LIB_PATH diff --git a/src/libs/installer/binaryformat.cpp b/src/libs/installer/binaryformat.cpp index b07cc90de..df2366946 100644 --- a/src/libs/installer/binaryformat.cpp +++ b/src/libs/installer/binaryformat.cpp @@ -55,6 +55,7 @@ #include <QTemporaryFile> #include <errno.h> +#include <string.h> using namespace QInstaller; using namespace QInstallerCreator; @@ -220,33 +221,26 @@ qint64 QInstaller::findMagicCookie(QFile *in, quint64 magicCookie) Q_ASSERT(in); Q_ASSERT(in->isOpen()); Q_ASSERT(in->isReadable()); - const qint64 oldPos = in->pos(); - const qint64 MAX_SEARCH = 1024 * 1024; // stop searching after one MB - qint64 searched = 0; - try { - while (searched < MAX_SEARCH) { - const qint64 pos = in->size() - searched - sizeof(qint64); - if (pos < 0) - throw Error(QObject::tr("Searched whole file, no marker found")); - if (!in->seek(pos)) { - throw Error(QObject::tr("Could not seek to %1 in file %2: %3").arg(QString::number(pos), - in->fileName(), in->errorString())); - } - const quint64 num = static_cast<quint64>(retrieveInt64(in)); - if (num == magicCookie) { - in->seek(oldPos); - return pos; - } - searched += 1; - } - throw Error(QObject::tr("No marker found, stopped after %1.").arg(humanReadableSize(MAX_SEARCH))); - } catch (const Error& err) { - in->seek(oldPos); - throw err; - } catch (...) { - in->seek(oldPos); - throw Error(QObject::tr("No marker found, unknown exception caught.")); + + const qint64 fileSize = in->size(); + const size_t markerSize = sizeof(qint64); + + // Search through 1MB, if smaller through the whole file. Note: QFile::map() does not change QFile::pos(). + const qint64 maxSearch = qMin((1024LL * 1024LL), fileSize); + const uchar *const mapped = in->map(fileSize - maxSearch, maxSearch); + if (!mapped) { + throw Error(QObject::tr("Could not map %1 from file %2: %3").arg(QString::number(maxSearch), + in->fileName(), in->errorString())); + } + + qint64 searched = maxSearch - markerSize; + while (searched >= 0) { + if (memcmp(&magicCookie, (mapped + searched), markerSize) == 0) + return (fileSize - maxSearch) + searched; + --searched; } + throw Error(QObject::tr("No marker found, stopped after %1.").arg(humanReadableSize(maxSearch))); + return -1; // never reached } @@ -802,7 +796,7 @@ BinaryContentPrivate::BinaryContentPrivate(const BinaryContentPrivate &other) BinaryContentPrivate::~BinaryContentPrivate() { foreach (const QByteArray &rccData, m_resourceMappings) - QResource::unregisterResource((const uchar*)rccData.constData()); + QResource::unregisterResource((const uchar*)rccData.constData(), QLatin1String(":/metadata")); m_resourceMappings.clear(); } @@ -1144,6 +1138,29 @@ int BinaryContent::registerEmbeddedQResources() } /*! + Registers the passed file as default resource content. If the embedded resources are already mapped into + memory, it will replace the first with the new content. +*/ +void BinaryContent::registerAsDefaultQResource(const QString &path) +{ + QFile resource(path); + bool success = resource.open(QIODevice::ReadOnly); + if (success && (d->m_resourceMappings.count() > 0)) { + success = QResource::unregisterResource((const uchar*)d->m_resourceMappings.first().constData(), + QLatin1String(":/metadata")); + if (success) + d->m_resourceMappings.remove(0); + } + + if (success) { + d->m_resourceMappings.prepend(addResourceFromBinary(&resource, Range<qint64>::fromStartAndEnd(0, + resource.size()))); + } else { + qWarning() << QString::fromLatin1("Could not register '%1' as default resource.").arg(path); + } +} + +/*! Returns the binary component index as read from the file. */ QInstallerCreator::ComponentIndex BinaryContent::componentIndex() const diff --git a/src/libs/installer/binaryformat.h b/src/libs/installer/binaryformat.h index d255ea8a9..0abe78030 100644 --- a/src/libs/installer/binaryformat.h +++ b/src/libs/installer/binaryformat.h @@ -242,6 +242,7 @@ public: qint64 magicMarker() const; int registerEmbeddedQResources(); + void registerAsDefaultQResource(const QString &path); QInstallerCreator::ComponentIndex componentIndex() const; private: diff --git a/src/libs/installer/component.cpp b/src/libs/installer/component.cpp index 0ac69aaf8..1dd70294b 100644 --- a/src/libs/installer/component.cpp +++ b/src/libs/installer/component.cpp @@ -211,6 +211,7 @@ Component::Component(PackageManagerCore *core) setPrivate(d); connect(this, SIGNAL(valueChanged(QString, QString)), this, SLOT(updateModelData(QString, QString))); + qRegisterMetaType<QList<QInstaller::Component*> >("QList<QInstaller::Component*>"); } /*! @@ -899,6 +900,7 @@ OperationList Component::operations() const if (!d->m_minimumProgressOperation) { d->m_minimumProgressOperation = KDUpdater::UpdateOperationFactory::instance() .create(QLatin1String("MinimumProgress")); + d->m_minimumProgressOperation->setValue(QLatin1String("component"), name()); d->m_operations.append(d->m_minimumProgressOperation); } @@ -906,6 +908,7 @@ OperationList Component::operations() const d->m_licenseOperation = KDUpdater::UpdateOperationFactory::instance() .create(QLatin1String("License")); d->m_licenseOperation->setValue(QLatin1String("installer"), QVariant::fromValue(d->m_core)); + d->m_licenseOperation->setValue(QLatin1String("component"), name()); QVariantMap licenses; const QList<QPair<QString, QString> > values = d->m_licenses.values(); diff --git a/src/libs/installer/downloadarchivesjob.cpp b/src/libs/installer/downloadarchivesjob.cpp index 24fba689a..82f9b365c 100644 --- a/src/libs/installer/downloadarchivesjob.cpp +++ b/src/libs/installer/downloadarchivesjob.cpp @@ -182,7 +182,7 @@ void DownloadArchivesJob::fetchNextArchive() if (m_downloader != 0) m_downloader->deleteLater(); - m_downloader = setupDownloader(); + m_downloader = setupDownloader(QString(), m_core->value(QLatin1String("UrlQueryString"))); if (!m_downloader) { m_archivesToDownload.removeFirst(); QMetaObject::invokeMethod(this, "fetchNextArchive", Qt::QueuedConnection); @@ -292,13 +292,16 @@ void DownloadArchivesJob::finishWithError(const QString &error) emitFinishedWithError(QInstaller::DownloadError, msg.arg(error, m_downloader->url().toString())); } -KDUpdater::FileDownloader *DownloadArchivesJob::setupDownloader(const QString &suffix) +KDUpdater::FileDownloader *DownloadArchivesJob::setupDownloader(const QString &suffix, const QString &queryString) { KDUpdater::FileDownloader *downloader = 0; const QFileInfo fi = QFileInfo(m_archivesToDownload.first().first); const Component *const component = m_core->componentByName(QFileInfo(fi.path()).fileName()); if (component) { - const QUrl url(m_archivesToDownload.first().second + suffix); + QString fullQueryString; + if (!queryString.isEmpty()) + fullQueryString = QLatin1String("?") + queryString; + const QUrl url(m_archivesToDownload.first().second + suffix + fullQueryString); const QString &scheme = url.scheme(); downloader = FileDownloaderFactory::instance().create(scheme, this); diff --git a/src/libs/installer/downloadarchivesjob.h b/src/libs/installer/downloadarchivesjob.h index 9cf904fae..765d2c564 100644 --- a/src/libs/installer/downloadarchivesjob.h +++ b/src/libs/installer/downloadarchivesjob.h @@ -91,7 +91,7 @@ protected Q_SLOTS: void emitDownloadProgress(double progress); private: - KDUpdater::FileDownloader *setupDownloader(const QString &suffix = QString()); + KDUpdater::FileDownloader *setupDownloader(const QString &suffix = QString(), const QString &queryString = QString()); private: PackageManagerCore *m_core; diff --git a/src/libs/installer/extractarchiveoperation.cpp b/src/libs/installer/extractarchiveoperation.cpp index 48c5db2c9..3897de4e4 100644 --- a/src/libs/installer/extractarchiveoperation.cpp +++ b/src/libs/installer/extractarchiveoperation.cpp @@ -75,13 +75,12 @@ bool ExtractArchiveOperation::performOperation() Receiver receiver; Callback callback; - // usually we have to connect it as queued connection but then some blocking work is in the main thread - connect(&callback, SIGNAL(progressChanged(QString)), this, SLOT(slotProgressChanged(QString)), - Qt::DirectConnection); + connect(&callback, SIGNAL(currentFileChanged(QString)), this, SLOT(fileFinished(QString))); + connect(&callback, SIGNAL(progressChanged(double)), this, SIGNAL(progressChanged(double))); if (PackageManagerCore *core = this->value(QLatin1String("installer")).value<PackageManagerCore*>()) { connect(core, SIGNAL(statusChanged(QInstaller::PackageManagerCore::Status)), &callback, - SLOT(statusChanged(QInstaller::PackageManagerCore::Status)), Qt::QueuedConnection); + SLOT(statusChanged(QInstaller::PackageManagerCore::Status))); } //Runnable is derived from QRunable which will be deleted by the ThreadPool -> no parent is needed @@ -125,8 +124,8 @@ bool ExtractArchiveOperation::undoOperation() const QStringList files = value(QLatin1String("files")).toStringList(); WorkerThread *const thread = new WorkerThread(this, files); - connect(thread, SIGNAL(outputTextChanged(QString)), this, SIGNAL(outputTextChanged(QString)), - Qt::QueuedConnection); + connect(thread, SIGNAL(currentFileChanged(QString)), this, SIGNAL(outputTextChanged(QString))); + connect(thread, SIGNAL(progressChanged(double)), this, SIGNAL(progressChanged(double))); QEventLoop loop; connect(thread, SIGNAL(finished()), &loop, SLOT(quit()), Qt::QueuedConnection); @@ -149,7 +148,7 @@ Operation *ExtractArchiveOperation::clone() const /*! This slot is direct connected to the caller so please don't call it from another thread in the same time. */ -void ExtractArchiveOperation::slotProgressChanged(const QString &filename) +void ExtractArchiveOperation::fileFinished(const QString &filename) { QStringList files = value(QLatin1String("files")).toStringList(); files.prepend(filename); diff --git a/src/libs/installer/extractarchiveoperation.h b/src/libs/installer/extractarchiveoperation.h index 818672fd3..53df81e73 100644 --- a/src/libs/installer/extractarchiveoperation.h +++ b/src/libs/installer/extractarchiveoperation.h @@ -64,9 +64,10 @@ public: Q_SIGNALS: void outputTextChanged(const QString &progress); + void progressChanged(double); private Q_SLOTS: - void slotProgressChanged(const QString &progress); + void fileFinished(const QString &progress); private: class Callback; diff --git a/src/libs/installer/extractarchiveoperation_p.h b/src/libs/installer/extractarchiveoperation_p.h index 4e0632830..c7284d9c2 100644 --- a/src/libs/installer/extractarchiveoperation_p.h +++ b/src/libs/installer/extractarchiveoperation_p.h @@ -71,9 +71,12 @@ public: ExtractArchiveOperation *const op = m_op;//dynamic_cast< ExtractArchiveOperation* >(parent()); Q_ASSERT(op != 0); + int removedCounter = 0; foreach (const QString &file, m_files) { + removedCounter++; const QFileInfo fi(file); - emit outputTextChanged(file); + emit currentFileChanged(file); + emit progressChanged(double(removedCounter) / m_files.count()); if (fi.isFile() || fi.isSymLink()) { op->deleteFileNowOrLater(fi.absoluteFilePath()); } else if (fi.isDir()) { @@ -85,7 +88,8 @@ public: } Q_SIGNALS: - void outputTextChanged(const QString &filename); + void currentFileChanged(const QString &filename); + void progressChanged(double); private: QStringList m_files; @@ -105,7 +109,8 @@ public: Callback() : state(S_OK), createBackups(true) {} Q_SIGNALS: - void progressChanged(const QString &filename); + void currentFileChanged(const QString &filename); + void progressChanged(double progress); public Q_SLOTS: void statusChanged(QInstaller::PackageManagerCore::Status status) @@ -130,7 +135,7 @@ public Q_SLOTS: protected: void setCurrentFile(const QString &filename) { - emit progressChanged(QDir::toNativeSeparators(filename)); + emit currentFileChanged(QDir::toNativeSeparators(filename)); } static QString generateBackupName(const QString &fn) @@ -161,8 +166,9 @@ protected: return true; } - HRESULT setCompleted(quint64 /*completed*/, quint64 /*total*/) + HRESULT setCompleted(quint64 completed, quint64 total) { + emit progressChanged(double(completed) / total); return state; } }; diff --git a/src/libs/installer/fileutils.cpp b/src/libs/installer/fileutils.cpp index 010c30621..c7c7436bb 100644 --- a/src/libs/installer/fileutils.cpp +++ b/src/libs/installer/fileutils.cpp @@ -289,8 +289,7 @@ void QInstaller::removeDirectory(const QString &path, bool ignoreErrors) QDirIterator it(path, QDir::NoDotAndDotDot | QDir::Dirs | QDir::NoSymLinks | QDir::Hidden, QDirIterator::Subdirectories); while (it.hasNext()) { - it.next(); - dirs.prepend(it.filePath()); + dirs.prepend(it.next()); removeFiles(dirs.at(0), ignoreErrors); } @@ -461,14 +460,18 @@ QString QInstaller::generateTemporaryFileName(const QString &templ) return f.fileName(); } -QString QInstaller::createTemporaryDirectory(const QString &templ) +QString QInstaller::createTemporaryDirectory(const QString &templateName) { - const QString t = QDir::tempPath() + QLatin1String("/") + templ + QLatin1String("XXXXXX"); - QTemporaryFile f(t); - if (!f.open()) - throw Error(QObject::tr("Could not create temporary folder for template %1: %2").arg(t, f.errorString())); - const QString path = f.fileName() + QLatin1String("meta"); - qDebug() << "\nCreating meta data directory at" << path; + QString path = QDir::tempPath() + QLatin1String("/") + templateName + QLatin1String("XXXXXX"); + { + QTemporaryFile f(path); + if (!f.open()) { + throw Error(QObject::tr("Could not create temporary directory %1: %2").arg(f.fileName(), + f.errorString())); + } + path = f.fileName(); + } + qDebug() << "\nCreating temporary directory at:" << path; QInstaller::mkpath(path); return path; diff --git a/src/libs/installer/fileutils.h b/src/libs/installer/fileutils.h index 9bbcec72a..c22aa36b6 100644 --- a/src/libs/installer/fileutils.h +++ b/src/libs/installer/fileutils.h @@ -108,7 +108,7 @@ private: Creates a temporary directory @throws QInstaller::Error if creating the temporary directory fails */ - QString INSTALLER_EXPORT createTemporaryDirectory(const QString &templ=QString()); + QString INSTALLER_EXPORT createTemporaryDirectory(const QString &templateName = QString()); QString INSTALLER_EXPORT generateTemporaryFileName(const QString &templ=QString()); diff --git a/src/libs/installer/getrepositorymetainfojob.cpp b/src/libs/installer/getrepositorymetainfojob.cpp index 1c8d07525..14bcf26f2 100644 --- a/src/libs/installer/getrepositorymetainfojob.cpp +++ b/src/libs/installer/getrepositorymetainfojob.cpp @@ -49,6 +49,8 @@ #include "kdupdaterfiledownloader.h" #include "kdupdaterfiledownloaderfactory.h" +#include "productkeycheck.h" + #include <QTimer> @@ -197,14 +199,14 @@ void GetRepositoryMetaInfoJob::startUpdatesXmlDownload() } if (!url.isValid()) { - finished(QInstaller::InvalidUrl, tr("Invalid repository URL: %1").arg(url.toString())); + finished(QInstaller::InvalidUrl, tr("Invalid repository URL: %1").arg(m_repository.displayname())); return; } m_downloader = FileDownloaderFactory::instance().create(url.scheme(), this); if (!m_downloader) { finished(QInstaller::InvalidUrl, tr("URL scheme not supported: %1 (%2)").arg(url.scheme(), - url.toString())); + m_repository.displayname())); return; } @@ -244,20 +246,7 @@ void GetRepositoryMetaInfoJob::updatesXmlDownloadFinished() Q_ASSERT(!fn.isEmpty()); Q_ASSERT(QFile::exists(fn)); - try { - m_temporaryDirectory = createTemporaryDirectory(QLatin1String("remoterepo")); - m_tempDirDeleter.add(m_temporaryDirectory); - } catch (const QInstaller::Error& e) { - finished(QInstaller::ExtractionError, e.message()); - return; - } - QFile updatesFile(fn); - if (!updatesFile.rename(m_temporaryDirectory + QLatin1String("/Updates.xml"))) { - finished(QInstaller::DownloadError, tr("Could not move Updates.xml to target location. Error: %1") - .arg(updatesFile.errorString())); - return; - } if (!updatesFile.open(QIODevice::ReadOnly)) { finished(QInstaller::DownloadError, tr("Could not open Updates.xml for reading. Error: %1") @@ -272,7 +261,7 @@ void GetRepositoryMetaInfoJob::updatesXmlDownloadFinished() if (!success) { const QString msg = tr("Could not fetch a valid version of Updates.xml from repository: %1. " - "Error: %2").arg(m_repository.url().toString(), err); + "Error: %2").arg(m_repository.displayname(), err); const QMessageBox::StandardButton b = MessageBoxHandler::critical(MessageBoxHandler::currentBestSuitParent(), @@ -302,15 +291,16 @@ void GetRepositoryMetaInfoJob::updatesXmlDownloadFinished() repository.setUsername(el.attribute(QLatin1String("username"))); repository.setPassword(el.attribute(QLatin1String("password"))); repository.setDisplayName(el.attribute(QLatin1String("displayname"))); - repositoryUpdates.insertMulti(action, qMakePair(repository, Repository())); - - qDebug() << "Repository to add:" << repository.url().toString(); + if (ProductKeyCheck::instance()->isValidRepository(repository)) { + repositoryUpdates.insertMulti(action, qMakePair(repository, Repository())); + qDebug() << "Repository to add:" << repository.displayname(); + } } else if (action == QLatin1String("remove")) { // remove possible default repositories using the given server url Repository repository(el.attribute(QLatin1String("url")), true); repositoryUpdates.insertMulti(action, qMakePair(repository, Repository())); - qDebug() << "Repository to remove:" << repository.url().toString(); + qDebug() << "Repository to remove:" << repository.displayname(); } else if (action == QLatin1String("replace")) { // replace possible default repositories using the given server url Repository oldRepository(el.attribute(QLatin1String("oldUrl")), true); @@ -319,19 +309,42 @@ void GetRepositoryMetaInfoJob::updatesXmlDownloadFinished() newRepository.setPassword(el.attribute(QLatin1String("password"))); newRepository.setDisplayName(el.attribute(QLatin1String("displayname"))); - // store the new repository and the one old it replaces - repositoryUpdates.insertMulti(action, qMakePair(newRepository, oldRepository)); - qDebug() << "Replace repository:" << oldRepository.url().toString() << "with:" - << newRepository.url().toString(); + if (ProductKeyCheck::instance()->isValidRepository(newRepository)) { + // store the new repository and the one old it replaces + repositoryUpdates.insertMulti(action, qMakePair(newRepository, oldRepository)); + qDebug() << "Replace repository:" << oldRepository.displayname() << "with:" + << newRepository.displayname(); + } } else { qDebug() << "Invalid additional repositories action set in Updates.xml fetched from:" - << m_repository.url().toString() << "Line:" << el.lineNumber(); + << m_repository.displayname() << "Line:" << el.lineNumber(); } } } if (!repositoryUpdates.isEmpty()) { - if (m_core->settings().updateDefaultRepositories(repositoryUpdates) == Settings::UpdatesApplied) { + const QSet<Repository> temporaries = m_core->settings().temporaryRepositories(); + // in case the temp repository introduced something new, we only want that temporary + if (temporaries.contains(m_repository)) { + + QSet<Repository> childTempRepositories; + typedef QPair<Repository, Repository> RepositoryPair; + + QList<RepositoryPair> values = repositoryUpdates.values(QLatin1String("add")); + foreach (const RepositoryPair &value, values) + childTempRepositories.insert(value.first); + + values = repositoryUpdates.values(QLatin1String("replace")); + foreach (const RepositoryPair &value, values) + childTempRepositories.insert(value.first); + + QSet<Repository> newChildTempRepositories = childTempRepositories.subtract(temporaries); + if (newChildTempRepositories.count() > 0) { + m_core->settings().addTemporaryRepositories(newChildTempRepositories, true); + finished(QInstaller::RepositoryUpdatesReceived, tr("Repository updates received.")); + return; + } + } else if (m_core->settings().updateDefaultRepositories(repositoryUpdates) == Settings::UpdatesApplied) { if (m_core->isUpdater() || m_core->isPackageManager()) m_core->writeMaintenanceConfigFiles(); finished(QInstaller::RepositoryUpdatesReceived, tr("Repository updates received.")); @@ -358,6 +371,19 @@ void GetRepositoryMetaInfoJob::updatesXmlDownloadFinished() } } + try { + m_temporaryDirectory = createTemporaryDirectory(QLatin1String("remoterepo-")); + m_tempDirDeleter.add(m_temporaryDirectory); + } catch (const QInstaller::Error& e) { + finished(QInstaller::ExtractionError, e.message()); + return; + } + if (!updatesFile.rename(m_temporaryDirectory + QLatin1String("/Updates.xml"))) { + finished(QInstaller::DownloadError, tr("Could not move Updates.xml to target location. Error: %1") + .arg(updatesFile.errorString())); + return; + } + setTotalAmount(m_packageNames.count() + 1); setProcessedAmount(1); emit infoMessage(this, tr("Finished updating component meta information.")); @@ -427,7 +453,7 @@ void GetRepositoryMetaInfoJob::fetchNextMetaInfo() if (!m_downloader) { m_currentPackageName.clear(); m_currentPackageVersion.clear(); - qWarning() << "Scheme not supported:" << url.toString(); + qWarning() << "Scheme not supported:" << m_repository.displayname(); QMetaObject::invokeMethod(this, "fetchNextMetaInfo", Qt::QueuedConnection); return; } diff --git a/src/libs/installer/packagemanagercore.cpp b/src/libs/installer/packagemanagercore.cpp index c617dd239..5f33d941a 100644 --- a/src/libs/installer/packagemanagercore.cpp +++ b/src/libs/installer/packagemanagercore.cpp @@ -421,7 +421,7 @@ void PackageManagerCore::writeMaintenanceConfigFiles() void PackageManagerCore::reset(const QHash<QString, QString> ¶ms) { d->m_completeUninstall = false; - d->m_forceRestart = false; + d->m_needsHardRestart = false; d->m_status = PackageManagerCore::Unfinished; d->m_installerBaseBinaryUnreplaced.clear(); @@ -596,11 +596,17 @@ int PackageManagerCore::downloadNeededArchives(double partProgressSize) If a component marked as important was installed during update process true is returned. */ -bool PackageManagerCore::needsRestart() const +bool PackageManagerCore::needsHardRestart() const { - return d->m_forceRestart; + return d->m_needsHardRestart; } +void PackageManagerCore::setNeedsHardRestart(bool needsHardRestart) +{ + d->m_needsHardRestart = needsHardRestart; +} + + void PackageManagerCore::rollBackInstallation() { emit titleMessageChanged(tr("Cancelling the Installer")); diff --git a/src/libs/installer/packagemanagercore.h b/src/libs/installer/packagemanagercore.h index 20a3293fa..557c4d9ed 100644 --- a/src/libs/installer/packagemanagercore.h +++ b/src/libs/installer/packagemanagercore.h @@ -248,7 +248,8 @@ public: int downloadNeededArchives(double partProgressSize); - bool needsRestart() const; + bool needsHardRestart() const; + void setNeedsHardRestart(bool needsHardRestart = true); bool finishedWithSuccess() const; public Q_SLOTS: diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp index f102d79ed..aac392d47 100644 --- a/src/libs/installer/packagemanagercore_p.cpp +++ b/src/libs/installer/packagemanagercore_p.cpp @@ -225,7 +225,7 @@ PackageManagerCorePrivate::PackageManagerCorePrivate(PackageManagerCore *core, q , m_updaterApplication(new DummyConfigurationInterface) , m_FSEngineClientHandler(initFSEngineClientHandler()) , m_status(PackageManagerCore::Unfinished) - , m_forceRestart(false) + , m_needsHardRestart(false) , m_testChecksum(false) , m_launchedAsRoot(AdminAuthorization::hasAdminRights()) , m_completeUninstall(false) @@ -1094,7 +1094,25 @@ void PackageManagerCorePrivate::writeUninstallerBinaryData(QIODevice *output, QF const qint64 dataBlockStart = output->pos(); QVector<Range<qint64> >resourceSegments; - foreach (const Range<qint64> &segment, layout.metadataResourceSegments) { + QVector<Range<qint64> >existingResourceSegments = layout.metadataResourceSegments; + + const QString newDefaultResource = m_core->value(QString::fromLatin1("DefaultResourceReplacement")); + if (!newDefaultResource.isEmpty()) { + QFile file(newDefaultResource); + if (file.open(QIODevice::ReadOnly)) { + resourceSegments.append(Range<qint64>::fromStartAndLength(output->pos(), file.size())); + appendData(output, &file, file.size()); + existingResourceSegments.remove(0); + + file.remove(); // clear all possible leftovers + m_core->setValue(QString::fromLatin1("DefaultResourceReplacement"), QString()); + } else { + qWarning() << QString::fromLatin1("Could not replace default resource with '%1'.") + .arg(newDefaultResource); + } + } + + foreach (const Range<qint64> &segment, existingResourceSegments) { input->seek(segment.start()); resourceSegments.append(Range<qint64>::fromStartAndLength(output->pos(), segment.length())); appendData(output, input, segment.length()); @@ -1156,8 +1174,6 @@ void PackageManagerCorePrivate::writeUninstaller(OperationList performedOperatio performedOperations.append(takeOwnedOperation(op)); } - writeMaintenanceConfigFiles(); - #ifdef Q_OS_MAC // if it is a bundle, we need some stuff in it... const QString sourceAppDirPath = QCoreApplication::applicationDirPath(); @@ -1264,7 +1280,7 @@ void PackageManagerCorePrivate::writeUninstaller(OperationList performedOperatio bool newBinaryWritten = false; bool replacementExists = false; - const QString installerBaseBinary = m_core->replaceVariables(m_installerBaseBinaryUnreplaced); + const QString installerBaseBinary = replaceVariables(m_installerBaseBinaryUnreplaced); if (!installerBaseBinary.isEmpty() && QFileInfo(installerBaseBinary).exists()) { qDebug() << "Got a replacement installer base binary:" << installerBaseBinary; @@ -1272,8 +1288,8 @@ void PackageManagerCorePrivate::writeUninstaller(OperationList performedOperatio try { openForRead(&replacementBinary, replacementBinary.fileName()); writeUninstallerBinary(&replacementBinary, replacementBinary.size(), true); + qDebug() << "Wrote the binary with the new replacement."; - m_forceRestart = true; newBinaryWritten = true; replacementExists = true; } catch (const Error &error) { @@ -1283,10 +1299,17 @@ void PackageManagerCorePrivate::writeUninstaller(OperationList performedOperatio if (!replacementBinary.remove()) { // Is there anything more sensible we can do with this error? I think not. It's not serious // enough for throwing / aborting the process. - qDebug() << QString::fromLatin1("Could not remove installer base binary (%1) after updating " + qDebug() << QString::fromLatin1("Could not remove installer base binary '%1' after updating " "the uninstaller: %2").arg(installerBaseBinary, replacementBinary.errorString()); + } else { + qDebug() << QString::fromLatin1("Removed installer base binary '%1' after updating the " + "uninstaller/ maintenance tool.").arg(installerBaseBinary); } m_installerBaseBinaryUnreplaced.clear(); + } else if (!installerBaseBinary.isEmpty() && !QFileInfo(installerBaseBinary).exists()) { + qWarning() << QString::fromLatin1("The current uninstaller/ maintenance tool could not be " + "updated. '%1' does not exist. Please fix the 'setInstallerBaseBinary(<temp_installer_base_" + "binary_path>)' call in your script.").arg(installerBaseBinary); } QFile input; @@ -1330,9 +1353,7 @@ void PackageManagerCorePrivate::writeUninstaller(OperationList performedOperatio #endif } - performedOperations = sortOperationsBasedOnComponentDependencies( - performedOperations); - + performedOperations = sortOperationsBasedOnComponentDependencies(performedOperations); m_core->setValue(QLatin1String("installedOperationAreSorted"), QLatin1String("true")); try { @@ -1363,10 +1384,11 @@ void PackageManagerCorePrivate::writeUninstaller(OperationList performedOperatio appendInt64(&file, MagicCookie); } input.close(); + writeMaintenanceConfigFiles(); deferredRename(dataFile + QLatin1String(".new"), dataFile, false); if (newBinaryWritten) { - const bool restart = replacementExists && isUpdater() && (!statusCanceledOrFailed()); + const bool restart = replacementExists && isUpdater() && (!statusCanceledOrFailed()) && m_needsHardRestart; deferredRename(uninstallerName() + QLatin1String(".new"), uninstallerName(), restart); qDebug() << "Maintenance tool restart:" << (restart ? "true." : "false."); } @@ -1888,7 +1910,7 @@ void PackageManagerCorePrivate::installComponent(Component *component, double pr throw Error(operation->errorString()); if (component->value(scEssential, scFalse) == scTrue) - m_forceRestart = true; + m_needsHardRestart = true; } registerPathesForUninstallation(component->pathesForUninstallation(), component->name()); @@ -2389,9 +2411,11 @@ OperationList PackageManagerCorePrivate::sortOperationsBasedOnComponentDependenc // create the complete component graph Graph<QString> componentGraph; + const QRegExp dash(QLatin1String("-.*")); foreach (const Component* componentNode, m_core->availableComponents()) { componentGraph.addNode(componentNode->name()); - componentGraph.addEdges(componentNode->name(), componentNode->dependencies()); + const QStringList dependencies = componentNode->dependencies().replaceInStrings(dash,QString()); + componentGraph.addEdges(componentNode->name(), dependencies); } foreach (const QString &componentName, componentGraph.sort()) diff --git a/src/libs/installer/packagemanagercore_p.h b/src/libs/installer/packagemanagercore_p.h index d5bdc78fd..445235a0d 100644 --- a/src/libs/installer/packagemanagercore_p.h +++ b/src/libs/installer/packagemanagercore_p.h @@ -205,7 +205,7 @@ public: int m_status; QString m_error; - bool m_forceRestart; + bool m_needsHardRestart; bool m_testChecksum; bool m_launchedAsRoot; bool m_completeUninstall; diff --git a/src/libs/installer/packagemanagercoredata.cpp b/src/libs/installer/packagemanagercoredata.cpp index 2294a8086..65123efba 100644 --- a/src/libs/installer/packagemanagercoredata.cpp +++ b/src/libs/installer/packagemanagercoredata.cpp @@ -138,7 +138,7 @@ QVariant PackageManagerCoreData::value(const QString &key, const QVariant &_defa if (key == scTargetDir) { QString dir = m_variables.value(key); if (dir.isEmpty()) - dir = m_settings.value(key, _default).toString(); + dir = replaceVariables(m_settings.value(key, _default).toString()); #ifdef Q_OS_WIN return QInstaller::normalizePathName(dir); #else diff --git a/src/libs/installer/packagemanagergui.cpp b/src/libs/installer/packagemanagergui.cpp index 2db2a9e4a..a514b3e82 100644 --- a/src/libs/installer/packagemanagergui.cpp +++ b/src/libs/installer/packagemanagergui.cpp @@ -128,7 +128,7 @@ public: setPixmap(QWizard::BannerPixmap, QPixmap()); setLayout(new QVBoxLayout); - setSubTitle(QString()); + setSubTitle(QLatin1String(" ")); setTitle(widget->windowTitle()); m_widget->setProperty("complete", true); m_widget->setProperty("final", false); @@ -248,7 +248,7 @@ PackageManagerGui::PackageManagerGui(PackageManagerCore *core, QWidget *parent) connect(m_core, SIGNAL(installationFinished()), this, SLOT(showFinishedPage()), Qt::QueuedConnection); connect(m_core, SIGNAL(uninstallationFinished()), this, SLOT(showFinishedPage()), Qt::QueuedConnection); - connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(slotCurrentPageChanged(int))); + connect(this, SIGNAL(currentIdChanged(int)), this, SLOT(executeControlScript(int))); connect(this, SIGNAL(currentIdChanged(int)), m_core, SIGNAL(currentPageChanged(int))); connect(button(QWizard::FinishButton), SIGNAL(clicked()), this, SIGNAL(finishButtonClicked())); connect(button(QWizard::FinishButton), SIGNAL(clicked()), m_core, SIGNAL(finishButtonClicked())); @@ -301,8 +301,8 @@ void PackageManagerGui::clickButton(int wb, int delay) { // transform the FinishButton to CancelButton, because of the needed misuse of the // CancelButton as a FinishButton to have some more control of closing the wizard - if (!m_core->isInstaller() && currentId() == PackageManagerCore::InstallationFinished && - wb == QWizard::FinishButton) { + if ((m_core->isUpdater() || m_core->isPackageManager()) && currentId() == + PackageManagerCore::InstallationFinished && wb == QWizard::FinishButton) { wb = QWizard::CancelButton; } if (QAbstractButton *b = button(static_cast<QWizard::WizardButton>(wb) )) @@ -315,8 +315,8 @@ bool PackageManagerGui::isButtonEnabled(int wb) { // transform the FinishButton to CancelButton, because of the needed misuse of the // CancelButton as a FinishButton to have some more control of closing the wizard - if (!m_core->isInstaller() && currentId() == PackageManagerCore::InstallationFinished && - wb == QWizard::FinishButton) { + if ((m_core->isUpdater() || m_core->isPackageManager()) && currentId() == + PackageManagerCore::InstallationFinished && wb == QWizard::FinishButton) { wb = QWizard::CancelButton; } if (QAbstractButton *b = button(static_cast<QWizard::WizardButton>(wb) )) @@ -353,12 +353,6 @@ void PackageManagerGui::loadControlScript(const QString &scriptPath) qDebug() << "Loaded control script" << scriptPath; } -void PackageManagerGui::slotCurrentPageChanged(int id) -{ - QMetaObject::invokeMethod(this, "delayedControlScriptExecution", Qt::QueuedConnection, - Q_ARG(int, id)); -} - void PackageManagerGui::callControlScriptMethod(const QString &methodName) { if (!d->m_controlScriptContext.isValid()) @@ -376,9 +370,9 @@ void PackageManagerGui::callControlScriptMethod(const QString &methodName) } } -void PackageManagerGui::delayedControlScriptExecution(int id) +void PackageManagerGui::executeControlScript(int pageId) { - if (PackageManagerPage *const p = qobject_cast<PackageManagerPage*> (page(id))) + if (PackageManagerPage *const p = qobject_cast<PackageManagerPage*> (page(pageId))) callControlScriptMethod(p->objectName() + QLatin1String("Callback")); } @@ -524,6 +518,7 @@ void PackageManagerGui::cancelButtonClicked() QDialog::reject(); } } else { + m_core->setNeedsHardRestart(false); QDialog::reject(); } } @@ -1870,7 +1865,7 @@ void FinishedPage::entering() if (!finishedText.isEmpty()) m_msgLabel->setText(finishedText); - if (!packageManagerCore()->value(scRunProgram).isEmpty()) { + if (!packageManagerCore()->isUninstaller() && !packageManagerCore()->value(scRunProgram).isEmpty()) { m_runItCheckBox->show(); m_runItCheckBox->setText(packageManagerCore()->value(scRunProgramDescription, tr("Run %1 now.")) .arg(productName())); @@ -1903,7 +1898,7 @@ void FinishedPage::handleFinishClicked() { const QString program = packageManagerCore()->replaceVariables(packageManagerCore()->value(scRunProgram)); const QStringList args = packageManagerCore()->replaceVariables( - packageManagerCore()->value(scRunProgramArguments)).split(QLatin1Char(' ')); + packageManagerCore()->value(scRunProgramArguments)).split(QLatin1Char(' '), QString::SkipEmptyParts); if (!m_runItCheckBox->isChecked() || program.isEmpty()) return; @@ -1942,7 +1937,7 @@ int RestartPage::nextId() const void RestartPage::entering() { - if (!packageManagerCore()->needsRestart()) { + if (!packageManagerCore()->needsHardRestart()) { if (QAbstractButton *finish = wizard()->button(QWizard::FinishButton)) finish->setVisible(false); QMetaObject::invokeMethod(this, "restart", Qt::QueuedConnection); diff --git a/src/libs/installer/packagemanagergui.h b/src/libs/installer/packagemanagergui.h index 5c428bf87..4f58a077d 100644 --- a/src/libs/installer/packagemanagergui.h +++ b/src/libs/installer/packagemanagergui.h @@ -116,8 +116,7 @@ protected Q_SLOTS: void wizardWidgetInsertionRequested(QWidget *widget, QInstaller::PackageManagerCore::WizardPage page); void wizardWidgetRemovalRequested(QWidget *widget); void wizardPageVisibilityChangeRequested(bool visible, int page); - void slotCurrentPageChanged(int id); - void delayedControlScriptExecution(int id); + void executeControlScript(int pageId); void setValidatorForCustomPageRequested(QInstaller::Component *component, const QString &name, const QString &callbackName); diff --git a/src/libs/installer/progresscoordinator.cpp b/src/libs/installer/progresscoordinator.cpp index 0850b39d0..e55f4b8bb 100644 --- a/src/libs/installer/progresscoordinator.cpp +++ b/src/libs/installer/progresscoordinator.cpp @@ -103,6 +103,14 @@ void ProgressCoordinator::registerPartProgress(QObject *sender, const char *sign Q_ASSERT(isConnected); } + +/*! + This slot gets the progress changed signals from different tasks. The values 0 and 1 are handled as + special values. + + 0 - is just ignored, so you can use a timer which gives the progress, e.g. like a downloader does. + 1 - means the task is finished, even if there comes another 1 from that task, so it will be ignored. +*/ void ProgressCoordinator::partProgressChanged(double fraction) { if (fraction < 0 || fraction > 1) { @@ -110,6 +118,16 @@ void ProgressCoordinator::partProgressChanged(double fraction) return; } + // no fraction no change + if (fraction == 0) + return; + + // ignore senders sending 100% multiple times + if (fraction == 1 && m_senderPendingCalculatedPercentageHash.contains(sender()) + && m_senderPendingCalculatedPercentageHash.value(sender()) == 0) { + return; + } + double partProgressSize = m_senderPartProgressSizeHash.value(sender(), 0); if (partProgressSize == 0) { qWarning() << "It seems that this sender was not registered in the right way:" << sender(); @@ -137,9 +155,9 @@ void ProgressCoordinator::partProgressChanged(double fraction) newCurrentCompletePercentage = 100; } - if (qRound(m_currentCompletePercentage) < qRound(newCurrentCompletePercentage)) { - qWarning("This should not happen!"); - } + // In undo mode, the progress has to go backward, new has to be smaller than current + if (qRound(m_currentCompletePercentage) < qRound(newCurrentCompletePercentage)) + qDebug("Something is wrong with the calculation of the progress."); m_currentCompletePercentage = newCurrentCompletePercentage; if (fraction == 1) { @@ -170,12 +188,13 @@ void ProgressCoordinator::partProgressChanged(double fraction) newCurrentCompletePercentage = 100; } - if (qRound(m_currentCompletePercentage) > qRound(newCurrentCompletePercentage)) { - qWarning("This should not happen!"); - } + // In normal mode, the progress has to go forward, new has to be larger than current + if (qRound(m_currentCompletePercentage) > qRound(newCurrentCompletePercentage)) + qDebug("Something is wrong with the calculation of the progress."); + m_currentCompletePercentage = newCurrentCompletePercentage; - if (fraction == 1 || fraction == 0) { + if (fraction == 1) { m_currentBasePercentage = m_currentBasePercentage + pendingCalculatedPartPercentage; m_senderPendingCalculatedPercentageHash.insert(sender(), 0); } else { diff --git a/src/libs/installer/resources/files-to-patch-linux-emb-arm b/src/libs/installer/resources/files-to-patch-linux-emb-arm index f6a4b59b7..8ea297b50 100644 --- a/src/libs/installer/resources/files-to-patch-linux-emb-arm +++ b/src/libs/installer/resources/files-to-patch-linux-emb-arm @@ -8,5 +8,5 @@ host-bin/qdoc *.la *.prl *.pc - - +*.pri +*.cmake diff --git a/src/libs/installer/resources/files-to-patch-linux-emb-arm-qt4 b/src/libs/installer/resources/files-to-patch-linux-emb-arm-qt4 new file mode 100644 index 000000000..f6a4b59b7 --- /dev/null +++ b/src/libs/installer/resources/files-to-patch-linux-emb-arm-qt4 @@ -0,0 +1,12 @@ +bin/qmake +bin/lrelease +bin/qdoc +host-bin/qmake +host-bin/lrelease +host-bin/qdoc +%% +*.la +*.prl +*.pc + + diff --git a/src/libs/installer/resources/files-to-patch-linux-emb-arm-qt5 b/src/libs/installer/resources/files-to-patch-linux-emb-arm-qt5 new file mode 100644 index 000000000..8ea297b50 --- /dev/null +++ b/src/libs/installer/resources/files-to-patch-linux-emb-arm-qt5 @@ -0,0 +1,12 @@ +bin/qmake +bin/lrelease +bin/qdoc +host-bin/qmake +host-bin/lrelease +host-bin/qdoc +%% +*.la +*.prl +*.pc +*.pri +*.cmake diff --git a/src/libs/installer/resources/files-to-patch-macx-emb-arm b/src/libs/installer/resources/files-to-patch-macx-emb-arm-qt5 index c01fe17a1..c01fe17a1 100644 --- a/src/libs/installer/resources/files-to-patch-macx-emb-arm +++ b/src/libs/installer/resources/files-to-patch-macx-emb-arm-qt5 diff --git a/src/libs/installer/resources/files-to-patch-windows-emb-arm b/src/libs/installer/resources/files-to-patch-windows-emb-arm index 674adaff6..a43229d18 100644 --- a/src/libs/installer/resources/files-to-patch-windows-emb-arm +++ b/src/libs/installer/resources/files-to-patch-windows-emb-arm @@ -8,4 +8,6 @@ host-bin/qdoc.exe *.la *.prl *.pc +*.pri +*.cmake diff --git a/src/libs/installer/resources/files-to-patch-windows-emb-arm-qt5 b/src/libs/installer/resources/files-to-patch-windows-emb-arm-qt5 new file mode 100644 index 000000000..a43229d18 --- /dev/null +++ b/src/libs/installer/resources/files-to-patch-windows-emb-arm-qt5 @@ -0,0 +1,13 @@ +bin/qmake.exe +bin/lrelease.exe +bin/qdoc.exe +host-bin/qmake.exe +host-bin/lrelease.exe +host-bin/qdoc.exe +%% +*.la +*.prl +*.pc +*.pri +*.cmake + diff --git a/src/libs/installer/resources/patch_file_lists.qrc b/src/libs/installer/resources/patch_file_lists.qrc index e898b9b23..b300b69ac 100644 --- a/src/libs/installer/resources/patch_file_lists.qrc +++ b/src/libs/installer/resources/patch_file_lists.qrc @@ -8,6 +8,8 @@ <file>files-to-patch-macx-qt5</file> <file>files-to-patch-linux-emb-arm</file> <file>files-to-patch-windows-emb-arm</file> - <file>files-to-patch-macx-emb-arm</file> + <file>files-to-patch-windows-emb-arm-qt5</file> + <file>files-to-patch-macx-emb-arm-qt5</file> + <file>files-to-patch-linux-emb-arm-qt5</file> </qresource> </RCC> diff --git a/src/libs/kdtools/kdupdaterupdateoperations.cpp b/src/libs/kdtools/kdupdaterupdateoperations.cpp index 5a4296242..be123fe61 100644 --- a/src/libs/kdtools/kdupdaterupdateoperations.cpp +++ b/src/libs/kdtools/kdupdaterupdateoperations.cpp @@ -65,29 +65,24 @@ static QString errnoToQString(int error) #endif } -static bool removeDirectory(const QString &path, QString *errorString, bool force = true) +static bool removeDirectory(const QString &path, QString *errorString, bool force) { Q_ASSERT(errorString); - const QFileInfoList entries = QDir(path).entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden); - for (QFileInfoList::const_iterator it = entries.constBegin(); it != entries.constEnd(); ++it) { - if (it->isDir() && !it->isSymLink()) { - removeDirectory(it->filePath(), errorString, force); - } else if (force) { - QFile f(it->filePath()); - if (!f.remove()) - return false; - } + + QDir dir = path; + const QFileInfoList entries = dir.entryInfoList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden); + foreach (const QFileInfo &entry, entries) { + if (entry.isDir() && (!entry.isSymLink())) + removeDirectory(entry.filePath(), errorString, force); + else if (force && (!QFile(entry.filePath()).remove())) + return false; } // even remove some hidden, OS-created files in there -#if defined Q_OS_MAC - QFile::remove(path + QLatin1String("/.DS_Store")); -#elif defined Q_OS_WIN - QFile::remove(path + QLatin1String("/Thumbs.db")); -#endif + QInstaller::removeSystemGeneratedFiles(path); errno = 0; - const bool success = QDir().rmdir(path); + const bool success = dir.rmdir(path); if (errno) *errorString = errnoToQString(errno); return success; @@ -170,7 +165,7 @@ bool CopyOperation::performOperation() QFile sourceFile(source); if (!sourceFile.exists()) { setError(UserDefinedError); - setErrorString(tr("Could not copy a none existing file: %1").arg(source)); + setErrorString(tr("Could not copy a non-existent file: %1").arg(source)); return false; } // If destination file exists, we cannot use QFile::copy() because it does not overwrite an existing diff --git a/src/sdk/installerbase.cpp b/src/sdk/installerbase.cpp index 870182e13..8d7518fcf 100644 --- a/src/sdk/installerbase.cpp +++ b/src/sdk/installerbase.cpp @@ -61,6 +61,7 @@ #include <kdrunoncechecker.h> #include <kdupdaterfiledownloaderfactory.h> +#include <QDirIterator> #include <QtCore/QTranslator> #include <QMessageBox> @@ -254,11 +255,10 @@ int main(int argc, char *argv[]) qDebug() << "Resource tree before loading the in-binary resource:"; qDebug() << "Language: " << QLocale().uiLanguages().value(0, QLatin1String("No UI language set")); - QDir dir(QLatin1String(":/")); - foreach (const QString &i, dir.entryList()) { - const QByteArray ba = i.toUtf8(); - qDebug().nospace() << " :/" << ba.constData(); - } + QDirIterator it(QLatin1String(":/"), QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden, + QDirIterator::Subdirectories); + while (it.hasNext()) + qDebug() << QString::fromLatin1(" %1").arg(it.next()); } // register custom operations before reading the binary content cause they may used in @@ -296,59 +296,6 @@ int main(int argc, char *argv[]) // instantiate the installer we are actually going to use QInstaller::PackageManagerCore core(content.magicMarker(), content.performedOperations()); - if (QInstaller::isVerbose()) { - qDebug() << "Resource tree after loading the in-binary resource:"; - - QDir dir = QDir(QLatin1String(":/")); - foreach (const QString &i, dir.entryList()) - qDebug() << QString::fromLatin1(" :/%1").arg(i); - - dir = QDir(QLatin1String(":/metadata/")); - foreach (const QString &i, dir.entryList()) - qDebug() << QString::fromLatin1(" :/metadata/%1").arg(i); - - dir = QDir(QLatin1String(":/translations/")); - foreach (const QString &i, dir.entryList()) - qDebug() << QString::fromLatin1(" :/translations/%1").arg(i); - } - - const QString directory = QLatin1String(":/translations"); - const QStringList translations = core.settings().translations(); - - // install the default Qt translator - QScopedPointer<QTranslator> translator(new QTranslator(&app)); - foreach (const QLocale locale, QLocale().uiLanguages()) { - // As there is no qt_en.qm, we simply end the search when the next - // preferred language is English. - if (locale.language() == QLocale::English) - break; - if (translator->load(locale, QLatin1String("qt"), QString::fromLatin1("_"), directory)) { - app.installTranslator(translator.take()); - break; - } - } - - translator.reset(new QTranslator(&app)); - // install English translation as fallback so that correct license button text is used - if (translator->load(QLatin1String("en_us"), directory)) - app.installTranslator(translator.take()); - - if (translations.isEmpty()) { - translator.reset(new QTranslator(&app)); - foreach (const QLocale locale, QLocale().uiLanguages()) { - if (translator->load(locale, QLatin1String(""), QLatin1String(""), directory)) { - app.installTranslator(translator.take()); - break; - } - } - } else { - foreach (const QString &translation, translations) { - translator.reset(new QTranslator(&app)); - if (translator->load(translation, QLatin1String(":/translations"))) - app.installTranslator(translator.take()); - } - } - QString controlScript; QHash<QString, QString> params; for (int i = 1; i < args.size(); ++i) { @@ -413,6 +360,56 @@ int main(int argc, char *argv[]) } } + // this needs to happen after we parse the arguments, but before we use the actual resources + const QString newDefaultResource = core.value(QString::fromLatin1("DefaultResourceReplacement")); + if (!newDefaultResource.isEmpty()) + content.registerAsDefaultQResource(newDefaultResource); + + if (QInstaller::isVerbose()) { + qDebug() << "Resource tree after loading the in-binary resource:"; + QDirIterator it(QLatin1String(":/"), QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden, + QDirIterator::Subdirectories); + while (it.hasNext()) + qDebug() << QString::fromLatin1(" %1").arg(it.next()); + } + + const QString directory = QLatin1String(":/translations"); + const QStringList translations = core.settings().translations(); + + // install the default Qt translator + QScopedPointer<QTranslator> translator(new QTranslator(&app)); + foreach (const QLocale locale, QLocale().uiLanguages()) { + // As there is no qt_en.qm, we simply end the search when the next + // preferred language is English. + if (locale.language() == QLocale::English) + break; + if (translator->load(locale, QLatin1String("qt"), QString::fromLatin1("_"), directory)) { + app.installTranslator(translator.take()); + break; + } + } + + translator.reset(new QTranslator(&app)); + // install English translation as fallback so that correct license button text is used + if (translator->load(QLatin1String("en_us"), directory)) + app.installTranslator(translator.take()); + + if (translations.isEmpty()) { + translator.reset(new QTranslator(&app)); + foreach (const QLocale locale, QLocale().uiLanguages()) { + if (translator->load(locale, QLatin1String(""), QLatin1String(""), directory)) { + app.installTranslator(translator.take()); + break; + } + } + } else { + foreach (const QString &translation, translations) { + translator.reset(new QTranslator(&app)); + if (translator->load(translation, QLatin1String(":/translations"))) + app.installTranslator(translator.take()); + } + } + // Create the wizard gui TabController controller(0); controller.setManager(&core); diff --git a/src/sdk/installerbase_p.cpp b/src/sdk/installerbase_p.cpp index 6e4002f30..4fcbe7b6e 100644 --- a/src/sdk/installerbase_p.cpp +++ b/src/sdk/installerbase_p.cpp @@ -63,6 +63,7 @@ #include <iostream> #ifdef Q_OS_WIN +# include <windows.h> # include <wincon.h> # ifndef ENABLE_INSERT_MODE @@ -135,6 +136,12 @@ public: m_oldCerr = std::cerr.rdbuf(); m_newCerr.open("CONOUT$"); std::cerr.rdbuf(m_newCerr.rdbuf()); +# ifndef Q_CC_MINGW + HMENU systemMenu = GetSystemMenu(GetConsoleWindow(), FALSE); + if (systemMenu != NULL) + RemoveMenu(systemMenu, SC_CLOSE, MF_BYCOMMAND); + DrawMenuBar(GetConsoleWindow()); +# endif #endif } ~MyApplicationConsole() diff --git a/src/sdk/installerbasecommons.cpp b/src/sdk/installerbasecommons.cpp index d2fb5e27b..a4e5047d0 100644 --- a/src/sdk/installerbasecommons.cpp +++ b/src/sdk/installerbasecommons.cpp @@ -75,16 +75,19 @@ IntroductionPageImpl::IntroductionPageImpl(QInstaller::PackageManagerCore *core) QVBoxLayout *layout = new QVBoxLayout(widget); m_packageManager = new QRadioButton(tr("Package manager"), this); + m_packageManager->setObjectName(QLatin1String("PackageManagerRadioButton")); layout->addWidget(m_packageManager); m_packageManager->setChecked(core->isPackageManager()); connect(m_packageManager, SIGNAL(toggled(bool)), this, SLOT(setPackageManager(bool))); m_updateComponents = new QRadioButton(tr("Update components"), this); + m_updateComponents->setObjectName(QLatin1String("UpdaterRadioButton")); layout->addWidget(m_updateComponents); m_updateComponents->setChecked(core->isUpdater()); connect(m_updateComponents, SIGNAL(toggled(bool)), this, SLOT(setUpdater(bool))); m_removeAllComponents = new QRadioButton(tr("Remove all components"), this); + m_removeAllComponents->setObjectName(QLatin1String("UninstallerRadioButton")); layout->addWidget(m_removeAllComponents); m_removeAllComponents->setChecked(core->isUninstaller()); connect(m_removeAllComponents, SIGNAL(toggled(bool)), this, SLOT(setUninstaller(bool))); diff --git a/src/sdk/tabcontroller.cpp b/src/sdk/tabcontroller.cpp index 720f6f292..770ea8007 100644 --- a/src/sdk/tabcontroller.cpp +++ b/src/sdk/tabcontroller.cpp @@ -150,7 +150,6 @@ int TabController::init() } d->m_gui->restart(); - d->m_gui->setWindowModality(Qt::WindowModal); d->m_gui->show(); onCurrentIdChanged(d->m_gui->currentId()); diff --git a/src/sdk/translations/ja_jp.ts b/src/sdk/translations/ja_jp.ts index 5e34e57e6..6af882705 100644 --- a/src/sdk/translations/ja_jp.ts +++ b/src/sdk/translations/ja_jp.ts @@ -99,7 +99,7 @@ <translation>無効な引数: %1個の引数が渡されましたが、必要なのは2個です。</translation> </message> <message> - <source>Could not copy a none existing file: %1</source> + <source>Could not copy a non-existent file: %1</source> <translation>存在しないファイルはコピーできません: %1</translation> </message> <message> diff --git a/tests/auto/installer/binaryformat/binaryformat.pro b/tests/auto/installer/binaryformat/binaryformat.pro new file mode 100644 index 000000000..24a7899ce --- /dev/null +++ b/tests/auto/installer/binaryformat/binaryformat.pro @@ -0,0 +1,5 @@ +include(../../qttest.pri) + +QT -= gui + +SOURCES += tst_binaryformat.cpp diff --git a/tests/auto/installer/binaryformat/tst_binaryformat.cpp b/tests/auto/installer/binaryformat/tst_binaryformat.cpp new file mode 100644 index 000000000..59f0e8e03 --- /dev/null +++ b/tests/auto/installer/binaryformat/tst_binaryformat.cpp @@ -0,0 +1,115 @@ +/************************************************************************** +** +** Copyright (C) 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 <binaryformat.h> +#include <errors.h> +#include <fileutils.h> + +#include <QTest> +#include <QTemporaryFile> + +static const qint64 scTinySize = 72704LL; +static const qint64 scSmallSize = 524288LL; +static const qint64 scLargeSize = 2097152LL; + +class tst_BinaryFormat : public QObject +{ + Q_OBJECT + +private slots: + void findMagicCookieSmallFile() + { + QTemporaryFile file; + file.open(); + + try { + QInstaller::blockingWrite(&file, QByteArray(scSmallSize, '1')); + QInstaller::appendInt64(&file, QInstaller::MagicCookie); + + QCOMPARE(QInstaller::findMagicCookie(&file, QInstaller::MagicCookie), scSmallSize); + } catch (const QInstaller::Error &error) { + QFAIL(qPrintable(error.message())); + } catch (...) { + QFAIL("Unexpected error."); + } + } + + void findMagicCookieLargeFile() + { + QTemporaryFile file; + file.open(); + + try { + QInstaller::blockingWrite(&file, QByteArray(scLargeSize, '1')); + QInstaller::appendInt64(&file, QInstaller::MagicCookie); + QInstaller::blockingWrite(&file, QByteArray(scTinySize, '2')); + + QCOMPARE(QInstaller::findMagicCookie(&file, QInstaller::MagicCookie), scLargeSize); + } catch (const QInstaller::Error &error) { + QFAIL(qPrintable(error.message())); + } catch (...) { + QFAIL("Unexpected error."); + } + } + + void testFindMagicCookieWithError() + { + QTest::ignoreMessage(QtDebugMsg, "create Error-Exception: \"No marker found, stopped after 71.00 KiB.\" "); + + QTemporaryFile file; + file.open(); + + try { + QInstaller::blockingWrite(&file, QByteArray(scTinySize, '1')); + + // throws + QInstaller::findMagicCookie(&file, QInstaller::MagicCookie); + } catch (const QInstaller::Error &error) { + QCOMPARE(qPrintable(error.message()), "No marker found, stopped after 71.00 KiB."); + } catch (...) { + QFAIL("Unexpected error."); + } + } +}; + +QTEST_MAIN(tst_BinaryFormat) + +#include "tst_binaryformat.moc" diff --git a/tests/auto/installer/installer.pro b/tests/auto/installer/installer.pro index 5873a8db8..414c952d7 100644 --- a/tests/auto/installer/installer.pro +++ b/tests/auto/installer/installer.pro @@ -12,5 +12,6 @@ SUBDIRS += \ consumeoutputoperationtest \ mkdiroperationtest \ copyoperationtest \ - solver + solver \ + binaryformat diff --git a/tests/auto/installer/scriptengine/tst_scriptengine.cpp b/tests/auto/installer/scriptengine/tst_scriptengine.cpp index 283d7bdad..b626d5a2e 100644 --- a/tests/auto/installer/scriptengine/tst_scriptengine.cpp +++ b/tests/auto/installer/scriptengine/tst_scriptengine.cpp @@ -26,9 +26,9 @@ public: virtual void init() {} - void callProtectedDelayedControlScriptExecution(int id) + void callProtectedDelayedExecuteControlScript(int id) { - delayedControlScriptExecution(id); + executeControlScript(id); } }; @@ -218,18 +218,16 @@ private slots: testGui.loadControlScript(":///data/auto-install.qs"); QCOMPARE(m_core.value("GuiTestValue"), QString("hello")); - testGui.show(); // show event calls automatically the first callback which does not exist setExpectedScriptOutput("Control script callback \"IntroductionPageCallback\" does not exist. "); - // give some time to the event triggering mechanism - qApp->processEvents(); + testGui.show(); // inside the auto-install script we are clicking the next button, with a not existing button QTest::ignoreMessage(QtWarningMsg, "Button with type: \"unknown button\" not found! "); - testGui.callProtectedDelayedControlScriptExecution(PackageManagerCore::ComponentSelection); + testGui.callProtectedDelayedExecuteControlScript(PackageManagerCore::ComponentSelection); setExpectedScriptOutput("FinishedPageCallback - OK"); - testGui.callProtectedDelayedControlScriptExecution(PackageManagerCore::InstallationFinished); + testGui.callProtectedDelayedExecuteControlScript(PackageManagerCore::InstallationFinished); } catch (const Error &error) { QFAIL(qPrintable(error.message())); } diff --git a/tests/downloadspeed/main.cpp b/tests/downloadspeed/main.cpp index 5a5b685aa..4edc0d8b6 100644 --- a/tests/downloadspeed/main.cpp +++ b/tests/downloadspeed/main.cpp @@ -164,6 +164,7 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; const QUrl url(a.arguments().value(1)); + KDUpdater::FileDownloaderFactory::setFollowRedirects(true); qDebug() << url.toString(); KDUpdater::FileDownloader *loader = KDUpdater::FileDownloaderFactory::instance().create(url.scheme(), 0); if (loader) { diff --git a/tools/binarycreator/binarycreator.cpp b/tools/binarycreator/binarycreator.cpp index a87cf46f6..5c7f4d34a 100644 --- a/tools/binarycreator/binarycreator.cpp +++ b/tools/binarycreator/binarycreator.cpp @@ -400,7 +400,7 @@ private: const QString oldPath; }; -static QString createBinaryResourceFile(const QString &directory) +static QString createBinaryResourceFile(const QString &directory, const QString &binaryName) { QTemporaryFile projectFile(directory + QLatin1String("/rccprojectXXXXXX.qrc")); if (!projectFile.open()) @@ -408,16 +408,19 @@ static QString createBinaryResourceFile(const QString &directory) projectFile.close(); const WorkingDirectoryChange wd(directory); - const QString binaryName = generateTemporaryFileName(); const QString projectFileName = QFileInfo(projectFile.fileName()).absoluteFilePath(); // 1. create the .qrc file - runRcc(QStringList() << QLatin1String("rcc") << QLatin1String("-project") - << QLatin1String("-o") << projectFileName); + if (runRcc(QStringList() << QLatin1String("rcc") << QLatin1String("-project") << QLatin1String("-o") + << projectFileName) != EXIT_SUCCESS) { + throw Error(QString::fromLatin1("Could not create rcc project file.")); + } // 2. create the binary resource file from the .qrc file - runRcc(QStringList() << QLatin1String("rcc") << QLatin1String("-binary") - << QLatin1String("-o") << binaryName << projectFileName); + if (runRcc(QStringList() << QLatin1String("rcc") << QLatin1String("-binary") << QLatin1String("-o") + << binaryName << projectFileName) != EXIT_SUCCESS) { + throw Error(QString::fromLatin1("Could not compile rcc project file.")); + } return binaryName; } @@ -467,7 +470,12 @@ static void printUsage() std::cout << " -r|--resources r1,.,rn include the given resource files into the binary" << std::endl; std::cout << " -v|--verbose Verbose output" << std::endl; - std::cout << std::endl << std::endl; + std::cout << " -rcc|--compile-resource Compiles the default resource and outputs the result into" + << std::endl; + std::cout << " 'update.rcc' in the current path." << std::endl; + std::cout << std::endl; + std::cout << "Packages are to be found in the current working directory and get listed as " + "their names" << std::endl << std::endl; std::cout << "Example (offline installer):" << std::endl; char sep = QDir::separator().toLatin1(); std::cout << " " << appName << " --offline-only -c installer-config" << sep << "config.xml -p " @@ -481,6 +489,9 @@ static void printUsage() std::cout << std::endl; std::cout << "Creates an installer for the SDK without qt and qt creator." << std::endl; std::cout << std::endl; + std::cout << "Example update.rcc:" << std::endl; + std::cout << " " << appName << " -c installer-config" << sep << "config.xml -p packages-directory " + "-rcc" << std::endl; } void copyConfigData(const QString &configFile, const QString &targetDir) @@ -576,6 +587,7 @@ int main(int argc, char **argv) QStringList resources; QStringList filteredPackages; QInstallerTools::FilterType ftype = QInstallerTools::Exclude; + bool compileResource = false; const QStringList args = app.arguments().mid(1); for (QStringList::const_iterator it = args.begin(); it != args.end(); ++it) { @@ -661,6 +673,8 @@ int main(int argc, char **argv) } else if (*it == QLatin1String("--ignore-translations") || *it == QLatin1String("--ignore-invalid-packages")) { continue; + } else if (*it == QLatin1String("-rcc") || *it == QLatin1String("--compile-resource")) { + compileResource = true; } else { if (it->startsWith(QLatin1String("-"))) { return printErrorAndUsageAndExit(QString::fromLatin1("Error: Unknown option \"%1\" used. Maybe you " @@ -689,7 +703,7 @@ int main(int argc, char **argv) ftype = QInstallerTools::Include; } - if (target.isEmpty()) + if (target.isEmpty() && !compileResource) return printErrorAndUsageAndExit(QString::fromLatin1("Error: Target parameter missing.")); if (configFile.isEmpty()) @@ -723,11 +737,11 @@ int main(int argc, char **argv) if (!target.endsWith(QLatin1String(".app")) && !target.endsWith(QLatin1String(".dmg"))) target += QLatin1String(".app"); #endif - { + if (!compileResource) { Input input; input.outputPath = target; input.installerExePath = templateBinary; - input.binaryResourcePath = createBinaryResourceFile(tmpMetaDir); + input.binaryResourcePath = createBinaryResourceFile(tmpMetaDir, generateTemporaryFileName()); input.binaryResources = createBinaryResourceFiles(resources); QInstallerTools::copyComponentData(packagesDirectories, tmpMetaDir, &packages); @@ -755,6 +769,9 @@ int main(int argc, char **argv) QFile::remove(input.binaryResourcePath); foreach (const QString &resource, input.binaryResources) QFile::remove(resource); + } else { + createBinaryResourceFile(tmpMetaDir, QDir::currentPath() + QLatin1String("/update.rcc")); + exitCode = EXIT_SUCCESS; } } catch (const Error &e) { std::cerr << "Caught exception: " << e.message() << std::endl; |