diff --git a/doc/installerfw.qdoc b/doc/installerfw.qdoc index e65deb05..669fc66f 100644 --- a/doc/installerfw.qdoc +++ b/doc/installerfw.qdoc @@ -758,7 +758,7 @@ You can also create hybrid installers that store some components locally and receive others via a network connection. For more information, see - ###TODO insert link here. + \l {Reducing Installer Size}. For information about how to implement data integration into the installer binary, see QInstaller::BinaryContent. diff --git a/examples/dependencies/packages/componentA/data/installcontentA.txt b/examples/dependencies/packages/componentA/data/installcontentA.txt new file mode 100644 index 00000000..f4000198 --- /dev/null +++ b/examples/dependencies/packages/componentA/data/installcontentA.txt @@ -0,0 +1,2 @@ +This file will be installed into the target directory.... + diff --git a/examples/dependencies/packages/componentB/data/installcontentB.txt b/examples/dependencies/packages/componentB/data/installcontentB.txt new file mode 100644 index 00000000..f4000198 --- /dev/null +++ b/examples/dependencies/packages/componentB/data/installcontentB.txt @@ -0,0 +1,2 @@ +This file will be installed into the target directory.... + diff --git a/examples/dependencies/packages/componentC/data/installcontentC.txt b/examples/dependencies/packages/componentC/data/installcontentC.txt new file mode 100644 index 00000000..f4000198 --- /dev/null +++ b/examples/dependencies/packages/componentC/data/installcontentC.txt @@ -0,0 +1,2 @@ +This file will be installed into the target directory.... + diff --git a/examples/dependencies/packages/componentD/data/installcontentD.txt b/examples/dependencies/packages/componentD/data/installcontentD.txt new file mode 100644 index 00000000..f4000198 --- /dev/null +++ b/examples/dependencies/packages/componentD/data/installcontentD.txt @@ -0,0 +1,2 @@ +This file will be installed into the target directory.... + diff --git a/examples/dependencies/packages/componentE/data/installcontentE.txt b/examples/dependencies/packages/componentE/data/installcontentE.txt new file mode 100644 index 00000000..f4000198 --- /dev/null +++ b/examples/dependencies/packages/componentE/data/installcontentE.txt @@ -0,0 +1,2 @@ +This file will be installed into the target directory.... + diff --git a/examples/dependencies/packages/componentF.subcomponent1.subsubcomponent1/data/installcontentF_1_1.txt b/examples/dependencies/packages/componentF.subcomponent1.subsubcomponent1/data/installcontentF_1_1.txt new file mode 100644 index 00000000..f4000198 --- /dev/null +++ b/examples/dependencies/packages/componentF.subcomponent1.subsubcomponent1/data/installcontentF_1_1.txt @@ -0,0 +1,2 @@ +This file will be installed into the target directory.... + diff --git a/examples/dependencies/packages/componentF.subcomponent1.subsubcomponent2/data/installcontentF_1_2.txt b/examples/dependencies/packages/componentF.subcomponent1.subsubcomponent2/data/installcontentF_1_2.txt new file mode 100644 index 00000000..f4000198 --- /dev/null +++ b/examples/dependencies/packages/componentF.subcomponent1.subsubcomponent2/data/installcontentF_1_2.txt @@ -0,0 +1,2 @@ +This file will be installed into the target directory.... + diff --git a/examples/dependencies/packages/componentF.subcomponent1/data/installcontentF_1.txt b/examples/dependencies/packages/componentF.subcomponent1/data/installcontentF_1.txt new file mode 100644 index 00000000..f4000198 --- /dev/null +++ b/examples/dependencies/packages/componentF.subcomponent1/data/installcontentF_1.txt @@ -0,0 +1,2 @@ +This file will be installed into the target directory.... + diff --git a/examples/dependencies/packages/componentF.subcomponent2.subsubcomponent1/data/installcontentF_2_1.txt b/examples/dependencies/packages/componentF.subcomponent2.subsubcomponent1/data/installcontentF_2_1.txt new file mode 100644 index 00000000..f4000198 --- /dev/null +++ b/examples/dependencies/packages/componentF.subcomponent2.subsubcomponent1/data/installcontentF_2_1.txt @@ -0,0 +1,2 @@ +This file will be installed into the target directory.... + diff --git a/examples/dependencies/packages/componentF.subcomponent2.subsubcomponent2/data/installcontentF_2_2.txt b/examples/dependencies/packages/componentF.subcomponent2.subsubcomponent2/data/installcontentF_2_2.txt new file mode 100644 index 00000000..f4000198 --- /dev/null +++ b/examples/dependencies/packages/componentF.subcomponent2.subsubcomponent2/data/installcontentF_2_2.txt @@ -0,0 +1,2 @@ +This file will be installed into the target directory.... + diff --git a/examples/dependencies/packages/componentF.subcomponent2/data/installcontentF_2.txt b/examples/dependencies/packages/componentF.subcomponent2/data/installcontentF_2.txt new file mode 100644 index 00000000..f4000198 --- /dev/null +++ b/examples/dependencies/packages/componentF.subcomponent2/data/installcontentF_2.txt @@ -0,0 +1,2 @@ +This file will be installed into the target directory.... + diff --git a/examples/dependencies/packages/componentF/data/installcontentF.txt b/examples/dependencies/packages/componentF/data/installcontentF.txt new file mode 100644 index 00000000..f4000198 --- /dev/null +++ b/examples/dependencies/packages/componentF/data/installcontentF.txt @@ -0,0 +1,2 @@ +This file will be installed into the target directory.... + diff --git a/examples/dependencies/packages/componentG/data/installcontentG.txt b/examples/dependencies/packages/componentG/data/installcontentG.txt new file mode 100644 index 00000000..f4000198 --- /dev/null +++ b/examples/dependencies/packages/componentG/data/installcontentG.txt @@ -0,0 +1,2 @@ +This file will be installed into the target directory.... + diff --git a/src/libs/installer/component_p.h b/src/libs/installer/component_p.h index b9f50b55..05b169d4 100644 --- a/src/libs/installer/component_p.h +++ b/src/libs/installer/component_p.h @@ -100,7 +100,8 @@ public: Install, Uninstall, KeepInstalled, - KeepUninstalled + KeepUninstalled, + AutodependUninstallation }; enum Column { diff --git a/src/libs/installer/componentmodel.cpp b/src/libs/installer/componentmodel.cpp index 8becdb19..8201248f 100644 --- a/src/libs/installer/componentmodel.cpp +++ b/src/libs/installer/componentmodel.cpp @@ -584,9 +584,13 @@ QSet ComponentModel::updateCheckedState(const ComponentSet &compone if (node->value(scCheckable, scTrue).toLower() == scFalse) { checkable = false; } - - if ((!node->isCheckable() && checkable) || !node->isEnabled() || !node->autoDependencies().isEmpty()) + // Let the check state to be checked up if the node is installed even if the component is not + // selectable/enabled or is installed as autodependency. Otherwise the node might not be selected + // and installer thinks it should be uninstalled. + if (!node->isInstalled() && + ((!node->isCheckable() && checkable) || !node->isEnabled() || !node->autoDependencies().isEmpty())) { continue; + } Qt::CheckState newState = state; const Qt::CheckState recentState = node->checkState(); diff --git a/src/libs/installer/metadatajob.cpp b/src/libs/installer/metadatajob.cpp index 0c674e31..bd577fec 100644 --- a/src/libs/installer/metadatajob.cpp +++ b/src/libs/installer/metadatajob.cpp @@ -37,6 +37,7 @@ #include "testrepository.h" #include +#include namespace QInstaller { @@ -52,6 +53,8 @@ MetadataJob::MetadataJob(QObject *parent) : Job(parent) , m_core(0) , m_addCompressedPackages(false) + , m_downloadableChunkSize(1000) + , m_taskNumber(0) { setCapabilities(Cancelable); connect(&m_xmlTask, &QFutureWatcherBase::finished, this, &MetadataJob::xmlTaskFinished); @@ -325,12 +328,7 @@ void MetadataJob::xmlTaskFinished() return; if (status == XmlDownloadSuccess) { - setProcessedAmount(0); - DownloadFileTask *const metadataTask = new DownloadFileTask(m_packages); - metadataTask->setProxyFactory(m_core->proxyFactory()); - m_metadataTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, metadataTask)); - setProgressTotalAmount(100); - emit infoMessage(this, tr("Retrieving meta information from remote repository...")); + fetchMetaDataPackages(); } else if (status == XmlDownloadRetry) { QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection); } else { @@ -379,21 +377,23 @@ void MetadataJob::metadataTaskFinished() { try { m_metadataTask.waitForFinished(); - QFuture future = m_metadataTask.future(); - if (future.resultCount() > 0) { - emit infoMessage(this, tr("Extracting meta information...")); - foreach (const FileTaskResult &result, future.results()) { - const FileTaskItem item = result.value(TaskRole::TaskItem).value(); - UnzipArchiveTask *task = new UnzipArchiveTask(result.target(), - item.value(TaskRole::UserRole).toString()); + m_metadataResult.append(m_metadataTask.future().results()); + if (!fetchMetaDataPackages()) { + if (m_metadataResult.count() > 0) { + emit infoMessage(this, tr("Extracting meta information...")); + foreach (const FileTaskResult &result, m_metadataResult) { + const FileTaskItem item = result.value(TaskRole::TaskItem).value(); + UnzipArchiveTask *task = new UnzipArchiveTask(result.target(), + item.value(TaskRole::UserRole).toString()); - QFutureWatcher *watcher = new QFutureWatcher(); - m_unzipTasks.insert(watcher, qobject_cast (task)); - connect(watcher, &QFutureWatcherBase::finished, this, &MetadataJob::unzipTaskFinished); - watcher->setFuture(QtConcurrent::run(&UnzipArchiveTask::doTask, task)); + QFutureWatcher *watcher = new QFutureWatcher(); + m_unzipTasks.insert(watcher, qobject_cast (task)); + connect(watcher, &QFutureWatcherBase::finished, this, &MetadataJob::unzipTaskFinished); + watcher->setFuture(QtConcurrent::run(&UnzipArchiveTask::doTask, task)); + } + } else { + emitFinished(); } - } else { - emitFinished(); } } catch (const TaskException &e) { reset(); @@ -410,6 +410,30 @@ void MetadataJob::metadataTaskFinished() // -- private +bool MetadataJob::fetchMetaDataPackages() +{ + //Download files in chunks. QtConcurrent will choke if too many task is given to it + int chunkSize = qMin(m_packages.length(), m_downloadableChunkSize); + QList tempPackages = m_packages.mid(0, chunkSize); + m_packages = m_packages.mid(chunkSize, m_packages.length()); + if (tempPackages.length() > 0) { + m_taskNumber++; + setProcessedAmount(0); + DownloadFileTask *const metadataTask = new DownloadFileTask(tempPackages); + metadataTask->setProxyFactory(m_core->proxyFactory()); + m_metadataTask.setFuture(QtConcurrent::run(&DownloadFileTask::doTask, metadataTask)); + setProgressTotalAmount(100); + QString metaInformation; + if (m_totalTaskCount > 1) + metaInformation = tr("Retrieving meta information from remote repository... %1/%2 ").arg(m_taskNumber).arg(m_totalTaskCount); + else + metaInformation = tr("Retrieving meta information from remote repository... "); + emit infoMessage(this, metaInformation); + return true; + } + return false; +} + void MetadataJob::reset() { m_packages.clear(); @@ -615,6 +639,9 @@ MetadataJob::Status MetadataJob::parseUpdatesXml(const QList &re } } } + double taskCount = m_packages.length()/static_cast(m_downloadableChunkSize); + m_totalTaskCount = qCeil(taskCount); + return XmlDownloadSuccess; } diff --git a/src/libs/installer/metadatajob.h b/src/libs/installer/metadatajob.h index c6d5ad35..f3f480f6 100644 --- a/src/libs/installer/metadatajob.h +++ b/src/libs/installer/metadatajob.h @@ -79,6 +79,7 @@ private slots: void startXMLTask(const QList items); private: + bool fetchMetaDataPackages(); void startUnzipRepositoryTask(const Repository &repo); void reset(); void resetCompressedFetch(); @@ -96,6 +97,10 @@ private: QHash *, QObject*> m_unzipRepositoryTasks; bool m_addCompressedPackages; QList m_unzipRepositoryitems; + QList m_metadataResult; + int m_downloadableChunkSize; + int m_taskNumber; + int m_totalTaskCount; }; } // namespace QInstaller diff --git a/src/libs/installer/packagemanagercore_p.cpp b/src/libs/installer/packagemanagercore_p.cpp index 57f43077..2577f09f 100644 --- a/src/libs/installer/packagemanagercore_p.cpp +++ b/src/libs/installer/packagemanagercore_p.cpp @@ -378,11 +378,15 @@ bool PackageManagerCorePrivate::buildComponentTree(QHash &c // now we can preselect components in the tree foreach (QInstaller::Component *component, components) { // set the checked state for all components without child (means without tristate) + // set checked state also for installed virtual tristate componets as otherwise + // those will be uninstalled if (component->isCheckable() && !component->isTristate()) { if (component->isDefault() && isInstaller()) component->setCheckState(Qt::Checked); else if (component->isInstalled()) component->setCheckState(Qt::Checked); + } else if (component->isVirtual() && component->isInstalled() && component->isTristate()) { + component->setCheckState(Qt::Checked); } } diff --git a/src/libs/installer/uninstallercalculator.cpp b/src/libs/installer/uninstallercalculator.cpp index 9e669c8c..20ad3c04 100644 --- a/src/libs/installer/uninstallercalculator.cpp +++ b/src/libs/installer/uninstallercalculator.cpp @@ -99,13 +99,21 @@ void UninstallerCalculator::appendComponentsToUninstall(const QList const QString replaces = c->value(scReplaces); const QStringList possibleNames = replaces.split(QInstaller::commaRegExp(), QString::SkipEmptyParts) << c->name(); - foreach (const QString &possibleName, possibleNames) - autoDependencies.removeAll(possibleName); + foreach (const QString &possibleName, possibleNames) { + + Component *cc = PackageManagerCore::componentByName(possibleName, m_installedComponents); + if (cc && (cc->installAction() != ComponentModelHelper::AutodependUninstallation)) { + autoDependencies.removeAll(possibleName); + + } + } } - // A component requested auto installation, keep it to resolve their dependencies as well. - if (!autoDependencies.isEmpty()) + // A component requested auto uninstallation, keep it to resolve their dependencies as well. + if (!autoDependencies.isEmpty()) { autoDependOnList.append(component); + component->setInstallAction(ComponentModelHelper::AutodependUninstallation); + } } }