From 3e51fe4651b08b0f5fd7f6c47db027c2f5956f76 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 19 May 2020 15:05:05 +0200 Subject: [PATCH 01/16] [partition] Remove spurious Q_FUNC_INFO from logging - The Q_FUNC_INFO was integrated into regular logging with commit 5248a37eb3ac162d8fc86f3bf4c2f9314842f467 --- src/modules/partition/gui/ChoicePage.cpp | 2 +- src/modules/partition/gui/PartitionSplitterWidget.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/modules/partition/gui/ChoicePage.cpp b/src/modules/partition/gui/ChoicePage.cpp index 5c90ea7b0..69a740d20 100644 --- a/src/modules/partition/gui/ChoicePage.cpp +++ b/src/modules/partition/gui/ChoicePage.cpp @@ -560,7 +560,7 @@ ChoicePage::doAlongsideSetupSplitter( const QModelIndex& current, Partition* part = modl->partitionForIndex( current ); if ( !part ) { - cDebug() << Q_FUNC_INFO << "Partition not found for index" << current; + cDebug() << "Partition not found for index" << current; return; } diff --git a/src/modules/partition/gui/PartitionSplitterWidget.cpp b/src/modules/partition/gui/PartitionSplitterWidget.cpp index bcc80b65a..5d2803b76 100644 --- a/src/modules/partition/gui/PartitionSplitterWidget.cpp +++ b/src/modules/partition/gui/PartitionSplitterWidget.cpp @@ -120,10 +120,10 @@ PartitionSplitterWidget::setSplitPartition( const QString& path, qint64 maxSize, qint64 preferredSize ) { - cDebug() << Q_FUNC_INFO << "path:" << path - << "\nminSize:" << minSize - << "\nmaxSize:" << maxSize - << "\nprfSize:" << preferredSize; + cDebug() << "path:" << path + << Logger::Continuation << "minSize:" << minSize + << Logger::Continuation << "maxSize:" << maxSize + << Logger::Continuation << "prfSize:" << preferredSize; if ( m_itemToResize && m_itemToResizeNext ) { From 8db8752a41c70f65d6bc9e1ecdef09bad3898364 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 19 May 2020 15:06:43 +0200 Subject: [PATCH 02/16] [libcalamaresui] Remove spurious Q_FUNC_INFO --- src/libcalamaresui/modulesystem/CppJobModule.cpp | 4 ++-- src/libcalamaresui/modulesystem/ViewModule.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libcalamaresui/modulesystem/CppJobModule.cpp b/src/libcalamaresui/modulesystem/CppJobModule.cpp index 2eddeda86..632a9dcb8 100644 --- a/src/libcalamaresui/modulesystem/CppJobModule.cpp +++ b/src/libcalamaresui/modulesystem/CppJobModule.cpp @@ -52,14 +52,14 @@ CppJobModule::loadSelf() CalamaresPluginFactory* pf = qobject_cast< CalamaresPluginFactory* >( m_loader->instance() ); if ( !pf ) { - cDebug() << Q_FUNC_INFO << m_loader->errorString(); + cDebug() << "Could not load module:" << m_loader->errorString(); return; } CppJob* cppJob = pf->create< Calamares::CppJob >(); if ( !cppJob ) { - cDebug() << Q_FUNC_INFO << m_loader->errorString(); + cDebug() << "Could not load module:" << m_loader->errorString(); return; } // cDebug() << "CppJobModule loading self for instance" << instanceKey() diff --git a/src/libcalamaresui/modulesystem/ViewModule.cpp b/src/libcalamaresui/modulesystem/ViewModule.cpp index 02c771ee2..54a79ab66 100644 --- a/src/libcalamaresui/modulesystem/ViewModule.cpp +++ b/src/libcalamaresui/modulesystem/ViewModule.cpp @@ -53,14 +53,14 @@ ViewModule::loadSelf() CalamaresPluginFactory* pf = qobject_cast< CalamaresPluginFactory* >( m_loader->instance() ); if ( !pf ) { - cWarning() << Q_FUNC_INFO << "No factory:" << m_loader->errorString(); + cWarning() << "No factory:" << m_loader->errorString(); return; } m_viewStep = pf->create< Calamares::ViewStep >(); if ( !m_viewStep ) { - cWarning() << Q_FUNC_INFO << "create() failed" << m_loader->errorString(); + cWarning() << "create() failed" << m_loader->errorString(); return; } } @@ -76,7 +76,7 @@ ViewModule::loadSelf() } else { - cWarning() << Q_FUNC_INFO << "No view step was created"; + cWarning() << "No view step was created"; } } From ec2fc5a7637d4f50315d7c6ccfc2f6a93943abd3 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 19 May 2020 16:25:30 +0200 Subject: [PATCH 03/16] [libcalamaresui] Better default font size - Previously, unless setDefaultFontSize() was called explicitly, the default size would be 0, leading to unexpected and weird displays (and a warning on stderr). - If setDefaultFontSize() is not called, get a sensible size instead (like defaultFontHeight() was already trying to do). --- src/libcalamaresui/utils/CalamaresUtilsGui.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libcalamaresui/utils/CalamaresUtilsGui.cpp b/src/libcalamaresui/utils/CalamaresUtilsGui.cpp index bd15d7a68..680673a22 100644 --- a/src/libcalamaresui/utils/CalamaresUtilsGui.cpp +++ b/src/libcalamaresui/utils/CalamaresUtilsGui.cpp @@ -207,6 +207,10 @@ unmarginLayout( QLayout* layout ) int defaultFontSize() { + if ( s_defaultFontSize <= 0 ) + { + s_defaultFontSize = QFont().pointSize(); + } return s_defaultFontSize; } From bd73981c5f41c5d2ad162764da75b38507821efd Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 19 May 2020 14:09:12 +0200 Subject: [PATCH 04/16] [calamares] Add -s option to module-tester - The -s will run the slideshow with a bogus job-queue, allowing easier testing of the slideshow. This is more convenient than having a Calamares with an empty show and a bogus exec section. - The -s option for running the slideshow / execution phase of Calamares needs to create a bogus Module for the ExecutionViewStep. --- src/calamares/testmain.cpp | 111 +++++++++++++++++++++++++++++++++---- 1 file changed, 101 insertions(+), 10 deletions(-) diff --git a/src/calamares/testmain.cpp b/src/calamares/testmain.cpp index f353fa6d5..77c86a59a 100644 --- a/src/calamares/testmain.cpp +++ b/src/calamares/testmain.cpp @@ -22,18 +22,18 @@ * bindings. */ -#include "modulesystem/Module.h" -#include "utils/Logger.h" -#include "utils/Yaml.h" - #include "Branding.h" #include "GlobalStorage.h" #include "Job.h" #include "JobQueue.h" #include "Settings.h" #include "ViewManager.h" - +#include "modulesystem/Module.h" #include "modulesystem/ModuleManager.h" +#include "modulesystem/ViewModule.h" +#include "utils/Logger.h" +#include "utils/Yaml.h" +#include "viewpages/ExecutionViewStep.h" #include #include @@ -80,6 +80,8 @@ handle_args( QCoreApplication& a ) "src/branding/default/branding.desc" ); QCommandLineOption uiOption( QStringList() << QStringLiteral( "U" ) << QStringLiteral( "ui" ), QStringLiteral( "Enable UI" ) ); + QCommandLineOption slideshowOption( QStringList() << QStringLiteral( "s" ) << QStringLiteral( "slideshow" ), + QStringLiteral( "Run slideshow module" ) ); QCommandLineParser parser; parser.setApplicationDescription( "Calamares module tester" ); @@ -92,13 +94,14 @@ handle_args( QCoreApplication& a ) parser.addOption( langOption ); parser.addOption( brandOption ); parser.addOption( uiOption ); + parser.addOption( slideshowOption ); parser.addPositionalArgument( "module", "Path or name of module to run." ); parser.addPositionalArgument( "job.yaml", "Path of job settings document to use.", "[job.yaml]" ); parser.process( a ); const QStringList args = parser.positionalArguments(); - if ( args.isEmpty() ) + if ( args.isEmpty() && !parser.isSet( slideshowOption ) ) { cError() << "Missing path.\n"; parser.showHelp(); @@ -116,20 +119,92 @@ handle_args( QCoreApplication& a ) jobSettings = args.at( 1 ); } - return ModuleConfig { args.first(), + return ModuleConfig { parser.isSet( slideshowOption ) ? QStringLiteral( "-" ) : args.first(), jobSettings, parser.value( globalOption ), parser.value( langOption ), parser.value( brandOption ), - parser.isSet( uiOption ) }; + parser.isSet( slideshowOption ) || parser.isSet( uiOption ) }; } } +/** @brief Bogus module for --slideshow option + * + * Normally the slideshow -- displayed by ExecutionViewStep -- is not + * associated with any particular module in the Calamares configuration. + * It is added internally by the module manager. For the module-loader + * testing application, we need something that pretends to be the + * module for the ExecutionViewStep. + */ +class ExecViewModule : public Calamares::Module +{ +public: + ExecViewModule(); + ~ExecViewModule() override; + + void loadSelf() override; + + virtual Type type() const override; + virtual Interface interface() const override; + + virtual Calamares::JobList jobs() const override; + + +protected: + void initFrom( const QVariantMap& ) override; +}; + +ExecViewModule::ExecViewModule() + : Calamares::Module() +{ +} + +ExecViewModule::~ExecViewModule() {} + +void +ExecViewModule::initFrom( const QVariantMap& ) +{ +} + +void +ExecViewModule::loadSelf() +{ + auto* viewStep = new Calamares::ExecutionViewStep(); + viewStep->setModuleInstanceKey( instanceKey() ); + viewStep->setConfigurationMap( m_configurationMap ); + Calamares::ViewManager::instance()->addViewStep( viewStep ); + m_loaded = true; +} + +Calamares::Module::Type +ExecViewModule::type() const +{ + return Module::Type::View; +} + + +Calamares::Module::Interface +ExecViewModule::interface() const +{ + return Module::Interface::QtPlugin; +} + +Calamares::JobList +ExecViewModule::jobs() const +{ + return Calamares::JobList(); +} + static Calamares::Module* load_module( const ModuleConfig& moduleConfig ) { QString moduleName = moduleConfig.moduleName(); + if ( moduleName == "-" ) + { + return new ExecViewModule; + } + QFileInfo fi; bool ok = false; @@ -188,6 +263,18 @@ load_module( const ModuleConfig& moduleConfig ) return module; } +static bool +is_ui_option( const char* s ) +{ + return !qstrcmp( s, "--ui" ) || !qstrcmp( s, "-U" ); +} + +static bool +is_slideshow_option( const char* s ) +{ + return !qstrcmp( s, "--slideshow" ) || !qstrcmp( s, "-s" ); +} + /** @brief Create the right kind of QApplication * * Does primitive parsing of argv[] to find the --ui option and returns @@ -202,7 +289,7 @@ createApplication( int& argc, char* argv[] ) { for ( int i = 1; i < argc; ++i ) { - if ( !qstrcmp( argv[ i ], "--ui" ) || !qstrcmp( argv[ i ], "-U" ) ) + if ( is_slideshow_option( argv[ i ] ) || is_ui_option( argv[ i ] ) ) { auto* aw = new QApplication( argc, argv ); aw->setQuitOnLastWindowClosed( true ); @@ -252,7 +339,11 @@ main( int argc, char* argv[] ) cDebug() << " .. got" << m->name() << m->typeString() << m->interfaceString(); if ( m->type() == Calamares::Module::Type::View ) { - if ( !qobject_cast< QApplication* >(aw) ) + // If we forgot the --ui, any ViewModule will core dump as it + // tries to create the widget **which won't be used anyway**. + // + // To avoid that crash, re-create the QApplication, now with GUI + if ( !qobject_cast< QApplication* >( aw ) ) { auto* replace_app = new QApplication( argc, argv ); replace_app->setQuitOnLastWindowClosed( true ); From ae861f7ec0134c3c50c32118e72e1ffb5a36f934 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 19 May 2020 16:43:26 +0200 Subject: [PATCH 05/16] [calamares] Give slideshow-test some jobs to run --- src/calamares/testmain.cpp | 43 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/calamares/testmain.cpp b/src/calamares/testmain.cpp index 77c86a59a..3405b0530 100644 --- a/src/calamares/testmain.cpp +++ b/src/calamares/testmain.cpp @@ -23,6 +23,7 @@ */ #include "Branding.h" +#include "CppJob.h" #include "GlobalStorage.h" #include "Job.h" #include "JobQueue.h" @@ -42,6 +43,7 @@ #include #include #include +#include #include @@ -128,6 +130,39 @@ handle_args( QCoreApplication& a ) } } +/** @brief Bogus Job for --slideshow option + * + * Generally one would use DummyCppJob for this kind of dummy + * job, but that class lives in a module so isn't available + * in this test application. + * + * This bogus job just sleeps for 3. + */ +class ExecViewJob : public Calamares::CppJob +{ +public: + explicit ExecViewJob( const QString& name ) + : m_name( name ) + { + } + virtual ~ExecViewJob() override; + + QString prettyName() const override { return m_name; } + + Calamares::JobResult exec() override + { + QThread::sleep( 3 ); + return Calamares::JobResult::ok(); + } + + void setConfigurationMap( const QVariantMap& ) override {} + +private: + QString m_name; +}; + +ExecViewJob::~ExecViewJob() {} + /** @brief Bogus module for --slideshow option * * Normally the slideshow -- displayed by ExecutionViewStep -- is not @@ -192,10 +227,13 @@ ExecViewModule::interface() const Calamares::JobList ExecViewModule::jobs() const { - return Calamares::JobList(); + Calamares::JobList l; + l.append( Calamares::job_ptr( new ExecViewJob( QStringLiteral( "step 1" ) ) ) ); + l.append( Calamares::job_ptr( new ExecViewJob( QStringLiteral( "step two" ) ) ) ); + l.append( Calamares::job_ptr( new ExecViewJob( QStringLiteral( "step III" ) ) ) ); + return l; } - static Calamares::Module* load_module( const ModuleConfig& moduleConfig ) { @@ -376,6 +414,7 @@ main( int argc, char* argv[] ) mw->setCentralWidget( w ); w->show(); mw->show(); + vm->currentStep()->onActivate(); return aw->exec(); } From 0947da3d41f8a861020cca3a778899cd6f5244aa Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 19 May 2020 17:08:49 +0200 Subject: [PATCH 06/16] [libcalamaresui] Report on QML errors - If the slideshow fails to load entirely, say so --- src/libcalamaresui/viewpages/Slideshow.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/libcalamaresui/viewpages/Slideshow.cpp b/src/libcalamaresui/viewpages/Slideshow.cpp index 85551f797..db994029c 100644 --- a/src/libcalamaresui/viewpages/Slideshow.cpp +++ b/src/libcalamaresui/viewpages/Slideshow.cpp @@ -23,7 +23,9 @@ #include "Branding.h" #include "utils/Dirs.h" #include "utils/Logger.h" +#ifdef WITH_QML #include "utils/Qml.h" +#endif #include "utils/Retranslator.h" #include @@ -50,6 +52,8 @@ SlideshowQML::SlideshowQML( QWidget* parent ) , m_qmlComponent( nullptr ) , m_qmlObject( nullptr ) { + CalamaresUtils::registerCalamaresModels(); + m_qmlShow->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); m_qmlShow->setResizeMode( QQuickWidget::SizeRootObjectToView ); m_qmlShow->engine()->addImportPath( CalamaresUtils::qmlModulesDir().absolutePath() ); @@ -126,6 +130,21 @@ SlideshowQML::loadQmlV2Complete() } } } + else + { + if ( m_qmlObject ) + { + cWarning() << "QML object already created"; + } + else if ( !m_qmlComponent ) + { + cWarning() << "QML component does not exist"; + } + else if ( m_qmlComponent && !m_qmlComponent->isReady() ) + { + cWarning() << "QML component not ready:" << m_qmlComponent->errors(); + } + } } /* From 40dd34c7d029308ffeffc29dc95564d2cd5a4f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20PORTAY?= Date: Tue, 19 May 2020 12:46:16 -0400 Subject: [PATCH 07/16] [rawfs] Fix crash if bogus is unset - fixes: 12:44:25 [6]: Python Error: 'builtin_function_or_method' object is not subscriptable File "/usr/lib/calamares/modules/rawfs/main.py", line 188, in run item.copy(filesystems.index(item), len(filesystems)) File "/usr/lib/calamares/modules/rawfs/main.py", line 99, in copy if libcalamares.job.configuration["bogus"]: --- src/modules/rawfs/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/rawfs/main.py b/src/modules/rawfs/main.py index 1898d8fea..68f3c444e 100644 --- a/src/modules/rawfs/main.py +++ b/src/modules/rawfs/main.py @@ -96,7 +96,7 @@ class RawFSItem: count = 0 libcalamares.utils.debug("Copying {} to {}".format(self.source, self.destination)) - if libcalamares.job.configuration["bogus"]: + if libcalamares.job.configuration.get("bogus", False): return srcsize, srcblksize = get_device_size(self.source) From 4491fb8c2758e3ae0e28de511f50211877c81285 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 19 May 2020 17:17:14 +0200 Subject: [PATCH 08/16] [libcalamaresui] Name QML-wrangling functions consistently - Use "Qml" in camel-cased names --- src/calamares/CalamaresWindow.cpp | 4 ++-- src/libcalamaresui/utils/Qml.cpp | 4 ++-- src/libcalamaresui/utils/Qml.h | 4 ++-- src/libcalamaresui/viewpages/QmlViewStep.cpp | 4 ++-- src/libcalamaresui/viewpages/Slideshow.cpp | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/calamares/CalamaresWindow.cpp b/src/calamares/CalamaresWindow.cpp index 2ab7cd5fa..a3775c44e 100644 --- a/src/calamares/CalamaresWindow.cpp +++ b/src/calamares/CalamaresWindow.cpp @@ -208,7 +208,7 @@ CalamaresWindow::getWidgetNavigation( QWidget* parent ) QWidget* CalamaresWindow::getQmlSidebar( QWidget* parent, int ) { - CalamaresUtils::registerCalamaresModels(); + CalamaresUtils::registerQmlModels(); QQuickWidget* w = new QQuickWidget( parent ); w->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); w->setResizeMode( QQuickWidget::SizeRootObjectToView ); @@ -220,7 +220,7 @@ CalamaresWindow::getQmlSidebar( QWidget* parent, int ) QWidget* CalamaresWindow::getQmlNavigation( QWidget* parent ) { - CalamaresUtils::registerCalamaresModels(); + CalamaresUtils::registerQmlModels(); QQuickWidget* w = new QQuickWidget( parent ); w->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); w->setResizeMode( QQuickWidget::SizeRootObjectToView ); diff --git a/src/libcalamaresui/utils/Qml.cpp b/src/libcalamaresui/utils/Qml.cpp index 5f3264da9..2837dfb5e 100644 --- a/src/libcalamaresui/utils/Qml.cpp +++ b/src/libcalamaresui/utils/Qml.cpp @@ -34,7 +34,7 @@ namespace CalamaresUtils { void -callQMLFunction( QQuickItem* qmlObject, const char* method ) +callQmlFunction( QQuickItem* qmlObject, const char* method ) { QByteArray methodSignature( method ); methodSignature.append( "()" ); @@ -149,7 +149,7 @@ qmlSearchNames() } void -registerCalamaresModels() +registerQmlModels() { static bool done = false; if ( !done ) diff --git a/src/libcalamaresui/utils/Qml.h b/src/libcalamaresui/utils/Qml.h index 33b44b9ea..d9d226034 100644 --- a/src/libcalamaresui/utils/Qml.h +++ b/src/libcalamaresui/utils/Qml.h @@ -40,7 +40,7 @@ namespace CalamaresUtils * Additionally, modules based on QmlViewStep have a context * property `config` referring to that module's configuration (if any). */ -UIDLLEXPORT void registerCalamaresModels(); +UIDLLEXPORT void registerQmlModels(); /** @brief Calls the QML method @p method on @p qmlObject * @@ -50,7 +50,7 @@ UIDLLEXPORT void registerCalamaresModels(); * * If there is a return value from the QML method, it is logged (but not otherwise used). */ -UIDLLEXPORT void callQMLFunction( QQuickItem* qmlObject, const char* method ); +UIDLLEXPORT void callQmlFunction( QQuickItem* qmlObject, const char* method ); /** @brief Search modes for loading Qml files. * diff --git a/src/libcalamaresui/viewpages/QmlViewStep.cpp b/src/libcalamaresui/viewpages/QmlViewStep.cpp index f92ef47e7..2234c230a 100644 --- a/src/libcalamaresui/viewpages/QmlViewStep.cpp +++ b/src/libcalamaresui/viewpages/QmlViewStep.cpp @@ -58,7 +58,7 @@ changeQMLState( QMLAction action, QQuickItem* item ) static const char propertyName[] = "activatedInCalamares"; bool activate = action == QMLAction::Start; - CalamaresUtils::callQMLFunction( item, activate ? "onActivate" : "onLeave" ); + CalamaresUtils::callQmlFunction( item, activate ? "onActivate" : "onLeave" ); auto property = item->property( propertyName ); if ( property.isValid() && ( property.type() == QVariant::Bool ) && ( property.toBool() != activate ) ) @@ -76,7 +76,7 @@ QmlViewStep::QmlViewStep( QObject* parent ) , m_spinner( new WaitingWidget( tr( "Loading ..." ) ) ) , m_qmlWidget( new QQuickWidget ) { - CalamaresUtils::registerCalamaresModels(); + CalamaresUtils::registerQmlModels(); QVBoxLayout* layout = new QVBoxLayout( m_widget ); layout->addWidget( m_spinner ); diff --git a/src/libcalamaresui/viewpages/Slideshow.cpp b/src/libcalamaresui/viewpages/Slideshow.cpp index db994029c..baed36252 100644 --- a/src/libcalamaresui/viewpages/Slideshow.cpp +++ b/src/libcalamaresui/viewpages/Slideshow.cpp @@ -52,7 +52,7 @@ SlideshowQML::SlideshowQML( QWidget* parent ) , m_qmlComponent( nullptr ) , m_qmlObject( nullptr ) { - CalamaresUtils::registerCalamaresModels(); + CalamaresUtils::registerQmlModels(); m_qmlShow->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); m_qmlShow->setResizeMode( QQuickWidget::SizeRootObjectToView ); @@ -163,7 +163,7 @@ SlideshowQML::changeSlideShowState( Action state ) if ( Branding::instance()->slideshowAPI() == 2 ) { // The QML was already loaded in the constructor, need to start it - CalamaresUtils::callQMLFunction( m_qmlObject, activate ? "onActivate" : "onLeave" ); + CalamaresUtils::callQmlFunction( m_qmlObject, activate ? "onActivate" : "onLeave" ); } else if ( !Calamares::Branding::instance()->slideshowPath().isEmpty() ) { From 6dffec27304d0eb90595234545788529d5291514 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 19 May 2020 17:39:57 +0200 Subject: [PATCH 09/16] [libcalamaresui] Move QML-related directory functions to Qml.cpp --- src/calamares/CalamaresApplication.cpp | 3 +++ src/libcalamares/utils/Dirs.cpp | 15 --------------- src/libcalamares/utils/Dirs.h | 4 ---- src/libcalamaresui/utils/Qml.cpp | 14 ++++++++++++++ src/libcalamaresui/utils/Qml.h | 11 ++++++++++- 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/calamares/CalamaresApplication.cpp b/src/calamares/CalamaresApplication.cpp index 5ef97a6a3..e90a67bb3 100644 --- a/src/calamares/CalamaresApplication.cpp +++ b/src/calamares/CalamaresApplication.cpp @@ -32,6 +32,9 @@ #include "utils/CalamaresUtilsSystem.h" #include "utils/Dirs.h" #include "utils/Logger.h" +#ifdef WITH_QML +#include "utils/Qml.h" +#endif #include "utils/Retranslator.h" #include "viewpages/ViewStep.h" diff --git a/src/libcalamares/utils/Dirs.cpp b/src/libcalamares/utils/Dirs.cpp index ca569490f..bb48477da 100644 --- a/src/libcalamares/utils/Dirs.cpp +++ b/src/libcalamares/utils/Dirs.cpp @@ -42,7 +42,6 @@ namespace CalamaresUtils { static QDir s_appDataDir( CMAKE_INSTALL_FULL_DATADIR ); -static QDir s_qmlModulesDir( QString( CMAKE_INSTALL_FULL_DATADIR ) + "/qml" ); static bool s_isAppDataDirOverridden = false; static bool s_haveExtraDirs = false; @@ -79,13 +78,6 @@ isWritableDir( const QDir& dir ) } -QDir -qmlModulesDir() -{ - return s_qmlModulesDir; -} - - void setAppDataDir( const QDir& dir ) { @@ -200,11 +192,4 @@ appLogDir() return QDir::temp(); } - -void -setQmlModulesDir( const QDir& dir ) -{ - s_qmlModulesDir = dir; -} - } // namespace CalamaresUtils diff --git a/src/libcalamares/utils/Dirs.h b/src/libcalamares/utils/Dirs.h index a63e679da..79c07a957 100644 --- a/src/libcalamares/utils/Dirs.h +++ b/src/libcalamares/utils/Dirs.h @@ -31,8 +31,6 @@ namespace CalamaresUtils { -DLLEXPORT QDir qmlModulesDir(); - /** * @brief appDataDir returns the directory with common application data. * Defaults to CMAKE_INSTALL_FULL_DATADIR (usually /usr/share/calamares). @@ -57,8 +55,6 @@ DLLEXPORT QDir systemLibDir(); DLLEXPORT void setAppDataDir( const QDir& dir ); DLLEXPORT bool isAppDataDirOverridden(); -DLLEXPORT void setQmlModulesDir( const QDir& dir ); - /** @brief Setup extra config and data dirs from the XDG variables. */ DLLEXPORT void setXdgDirs(); diff --git a/src/libcalamaresui/utils/Qml.cpp b/src/libcalamaresui/utils/Qml.cpp index 2837dfb5e..eabbf8d33 100644 --- a/src/libcalamaresui/utils/Qml.cpp +++ b/src/libcalamaresui/utils/Qml.cpp @@ -30,8 +30,22 @@ #include #include +static QDir s_qmlModulesDir( QString( CMAKE_INSTALL_FULL_DATADIR ) + "/qml" ); + namespace CalamaresUtils { +QDir +qmlModulesDir() +{ + return s_qmlModulesDir; +} + +void +setQmlModulesDir( const QDir& dir ) +{ + s_qmlModulesDir = dir; +} + void callQmlFunction( QQuickItem* qmlObject, const char* method ) diff --git a/src/libcalamaresui/utils/Qml.h b/src/libcalamaresui/utils/Qml.h index d9d226034..2da36633a 100644 --- a/src/libcalamaresui/utils/Qml.h +++ b/src/libcalamaresui/utils/Qml.h @@ -24,10 +24,19 @@ #include "modulesystem/InstanceKey.h" #include "utils/NamedEnum.h" +#include + class QQuickItem; namespace CalamaresUtils { +/// @brief the extra directory where Calamares searches for QML files +UIDLLEXPORT QDir qmlModulesDir(); +/// @brief sets specific directory for searching for QML files +UIDLLEXPORT void setQmlModulesDir( const QDir& dir ); + + + /** @brief Sets up global Calamares models for QML * * This needs to be called at least once to make the global Calamares @@ -66,7 +75,7 @@ enum class QmlSearch Both }; -///@brief Names for the search terms (in config files) +/// @brief Names for the search terms (in config files) UIDLLEXPORT const NamedEnumTable< QmlSearch >& qmlSearchNames(); /** @brief Find a suitable QML file, given the search method and name hints From 1fec95ac48717942be1be673119710d795f5a774 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Tue, 19 May 2020 21:25:05 +0200 Subject: [PATCH 10/16] [libcalamares] Move QML search-path initialization - QML files need to be searched in specific places; this was initialized by Calamares, but not for the text application. Move initialization into the library. --- src/calamares/CalamaresApplication.cpp | 60 ++----------------------- src/calamares/testmain.cpp | 7 +++ src/libcalamaresui/utils/Qml.cpp | 62 +++++++++++++++++++++++++- src/libcalamaresui/utils/Qml.h | 8 +++- 4 files changed, 77 insertions(+), 60 deletions(-) diff --git a/src/calamares/CalamaresApplication.cpp b/src/calamares/CalamaresApplication.cpp index e90a67bb3..43a48881c 100644 --- a/src/calamares/CalamaresApplication.cpp +++ b/src/calamares/CalamaresApplication.cpp @@ -120,34 +120,6 @@ CalamaresApplication::mainWindow() } -static QStringList -qmlDirCandidates( bool assumeBuilddir ) -{ - static const char QML[] = "qml"; - - QStringList qmlDirs; - if ( CalamaresUtils::isAppDataDirOverridden() ) - { - qmlDirs << CalamaresUtils::appDataDir().absoluteFilePath( QML ); - } - else - { - if ( assumeBuilddir ) - { - qmlDirs << QDir::current().absoluteFilePath( "src/qml" ); // In build-dir - } - if ( CalamaresUtils::haveExtraDirs() ) - for ( auto s : CalamaresUtils::extraDataDirs() ) - { - qmlDirs << ( s + QML ); - } - qmlDirs << CalamaresUtils::appDataDir().absoluteFilePath( QML ); - } - - return qmlDirs; -} - - static QStringList brandingFileCandidates( bool assumeBuilddir, const QString& brandingFilename ) { @@ -178,38 +150,12 @@ brandingFileCandidates( bool assumeBuilddir, const QString& brandingFilename ) void CalamaresApplication::initQmlPath() { - QDir importPath; // Right now, current-dir - QStringList qmlDirCandidatesByPriority = qmlDirCandidates( isDebug() ); - bool found = false; - - foreach ( const QString& path, qmlDirCandidatesByPriority ) +#ifdef WITH_QML + if ( !CalamaresUtils::initQmlModulesDir() ) { - QDir dir( path ); - if ( dir.exists() && dir.isReadable() ) - { - importPath = dir; - found = true; - break; - } - } - - if ( !found || !importPath.exists() || !importPath.isReadable() ) - { - cError() << "Cowardly refusing to continue startup without a QML directory." - << Logger::DebugList( qmlDirCandidatesByPriority ); - if ( CalamaresUtils::isAppDataDirOverridden() ) - { - cError() << "FATAL: explicitly configured application data directory is missing qml/"; - } - else - { - cError() << "FATAL: none of the expected QML paths exist."; - } ::exit( EXIT_FAILURE ); } - - cDebug() << "Using Calamares QML directory" << importPath.absolutePath(); - CalamaresUtils::setQmlModulesDir( importPath ); +#endif } diff --git a/src/calamares/testmain.cpp b/src/calamares/testmain.cpp index 3405b0530..57da890cc 100644 --- a/src/calamares/testmain.cpp +++ b/src/calamares/testmain.cpp @@ -33,6 +33,9 @@ #include "modulesystem/ModuleManager.h" #include "modulesystem/ViewModule.h" #include "utils/Logger.h" +#ifdef WITH_QML +#include "utils/Qml.h" +#endif #include "utils/Yaml.h" #include "viewpages/ExecutionViewStep.h" @@ -366,6 +369,10 @@ main( int argc, char* argv[] ) gs->insert( "localeConf", vm ); } +#ifdef WITH_QML + CalamaresUtils::initQmlModulesDir(); // don't care if failed +#endif + cDebug() << "Calamares module-loader testing" << module.moduleName(); Calamares::Module* m = load_module( module ); if ( !m ) diff --git a/src/libcalamaresui/utils/Qml.cpp b/src/libcalamaresui/utils/Qml.cpp index eabbf8d33..4f53aa317 100644 --- a/src/libcalamaresui/utils/Qml.cpp +++ b/src/libcalamaresui/utils/Qml.cpp @@ -21,7 +21,9 @@ #include "Branding.h" #include "GlobalStorage.h" #include "JobQueue.h" +#include "Settings.h" #include "ViewManager.h" +#include "utils/Dirs.h" #include "utils/Logger.h" #include @@ -46,6 +48,62 @@ setQmlModulesDir( const QDir& dir ) s_qmlModulesDir = dir; } +static QStringList +qmlDirCandidates( bool assumeBuilddir ) +{ + static const char QML[] = "qml"; + + QStringList qmlDirs; + if ( CalamaresUtils::isAppDataDirOverridden() ) + { + qmlDirs << CalamaresUtils::appDataDir().absoluteFilePath( QML ); + } + else + { + if ( assumeBuilddir ) + { + qmlDirs << QDir::current().absoluteFilePath( "src/qml" ); // In build-dir + } + if ( CalamaresUtils::haveExtraDirs() ) + for ( auto s : CalamaresUtils::extraDataDirs() ) + { + qmlDirs << ( s + QML ); + } + qmlDirs << CalamaresUtils::appDataDir().absoluteFilePath( QML ); + } + + return qmlDirs; +} + +bool +initQmlModulesDir() +{ + QStringList qmlDirCandidatesByPriority + = qmlDirCandidates( Calamares::Settings::instance() && Calamares::Settings::instance()->debugMode() ); + + for ( const QString& path : qmlDirCandidatesByPriority ) + { + QDir dir( path ); + if ( dir.exists() && dir.isReadable() ) + { + cDebug() << "Using Calamares QML directory" << dir.absolutePath(); + CalamaresUtils::setQmlModulesDir( dir ); + return true; + } + } + + cError() << "Cowardly refusing to continue startup without a QML directory." + << Logger::DebugList( qmlDirCandidatesByPriority ); + if ( CalamaresUtils::isAppDataDirOverridden() ) + { + cError() << "FATAL: explicitly configured application data directory is missing qml/"; + } + else + { + cError() << "FATAL: none of the expected QML paths exist."; + } + return false; +} void callQmlFunction( QQuickItem* qmlObject, const char* method ) @@ -85,14 +143,14 @@ addExpansions( QmlSearch method, QStringList& candidates, const QStringList& nam std::transform( names.constBegin(), names.constEnd(), std::back_inserter( candidates ), - [ & ]( const QString& s ) { return s.isEmpty() ? QString() : bPath.arg( brandDir, s ); } ); + [&]( const QString& s ) { return s.isEmpty() ? QString() : bPath.arg( brandDir, s ); } ); } if ( ( method == QmlSearch::Both ) || ( method == QmlSearch::QrcOnly ) ) { std::transform( names.constBegin(), names.constEnd(), std::back_inserter( candidates ), - [ & ]( const QString& s ) { return s.isEmpty() ? QString() : qrPath.arg( s ); } ); + [&]( const QString& s ) { return s.isEmpty() ? QString() : qrPath.arg( s ); } ); } } diff --git a/src/libcalamaresui/utils/Qml.h b/src/libcalamaresui/utils/Qml.h index 2da36633a..a3fc6d114 100644 --- a/src/libcalamaresui/utils/Qml.h +++ b/src/libcalamaresui/utils/Qml.h @@ -35,7 +35,13 @@ UIDLLEXPORT QDir qmlModulesDir(); /// @brief sets specific directory for searching for QML files UIDLLEXPORT void setQmlModulesDir( const QDir& dir ); - +/** @brief initialize QML search path with branding directories + * + * Picks a suitable branding directory (from the build-dir in debug mode, + * otherwise based on the branding directory) and adds it to the + * QML modules directory; returns @c false if none is found. + */ +UIDLLEXPORT bool initQmlModulesDir(); /** @brief Sets up global Calamares models for QML * From c7d0df223a64cd4d0adda30930e4791bced6a156 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 20 May 2020 10:36:42 +0200 Subject: [PATCH 11/16] [libcalamaresui] Expose registering-a-single-module - For testing purposes, it's useful to load a module externally and then register it to the ModuleManager (this hands off ownership). - Refactor overall module loading to use the exposed single-module method. --- .../modulesystem/ModuleManager.cpp | 52 +++++++++++++++---- .../modulesystem/ModuleManager.h | 8 +++ 2 files changed, 49 insertions(+), 11 deletions(-) diff --git a/src/libcalamaresui/modulesystem/ModuleManager.cpp b/src/libcalamaresui/modulesystem/ModuleManager.cpp index 3a3174935..0b83e4d9b 100644 --- a/src/libcalamaresui/modulesystem/ModuleManager.cpp +++ b/src/libcalamaresui/modulesystem/ModuleManager.cpp @@ -300,22 +300,12 @@ ModuleManager::loadModules() continue; } - if ( !checkModuleDependencies( *thisModule ) ) + if ( !addModule( thisModule ) ) { // Error message is already printed failedModules.append( instanceKey.toString() ); continue; } - - // If it's a ViewModule, it also appends the ViewStep to the ViewManager. - thisModule->loadSelf(); - m_loadedModulesByInstanceKey.insert( instanceKey, thisModule ); - if ( !thisModule->isLoaded() ) - { - cError() << "Module" << instanceKey.toString() << "loading FAILED."; - failedModules.append( instanceKey.toString() ); - continue; - } } // At this point we most certainly have a pointer to a loaded module in @@ -345,6 +335,40 @@ ModuleManager::loadModules() } } +bool +ModuleManager::addModule( Module *module ) +{ + if ( !module ) + { + return false; + } + if ( !module->instanceKey().isValid() ) + { + cWarning() << "Module" << module->location() << '@' << (void*)module << "has invalid instance key."; + return false; + } + if ( !checkModuleDependencies( *module ) ) + { + return false; + } + + if ( !module->isLoaded() ) + { + module->loadSelf(); + } + + // Even if the load failed, we keep the module, so that if it tried to + // get loaded **again**, we already know. + m_loadedModulesByInstanceKey.insert( module->instanceKey(), module ); + if ( !module->isLoaded() ) + { + cError() << "Module" << module->instanceKey().toString() << "loading FAILED."; + return false; + } + + return true; +} + void ModuleManager::checkRequirements() { @@ -414,6 +438,12 @@ ModuleManager::checkDependencies() bool ModuleManager::checkModuleDependencies( const Module& m ) { + if ( !m_availableDescriptorsByModuleName.contains( m.name() ) ) + { + cWarning() << "Module" << m.name() << "loaded externally, no dependency information."; + return true; + } + bool allRequirementsFound = true; QStringList requiredModules = m_availableDescriptorsByModuleName[ m.name() ].value( "requiredModules" ).toStringList(); diff --git a/src/libcalamaresui/modulesystem/ModuleManager.h b/src/libcalamaresui/modulesystem/ModuleManager.h index 0c8a4bdaf..2c51e70f7 100644 --- a/src/libcalamaresui/modulesystem/ModuleManager.h +++ b/src/libcalamaresui/modulesystem/ModuleManager.h @@ -85,6 +85,14 @@ public: */ void loadModules(); + /** + * @brief Adds a single module (loaded by some other means) + * + * Returns @c true on success (that is, the module's dependencies + * are satisfied, it wasn't already loaded, ...). + */ + bool addModule( Module* ); + /** * @brief Starts asynchronous requirements checking for each module. * When this is done, the signal requirementsComplete is emitted. From 28500de2f8b6ca2d925cd225d4d5ff1745127324 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 20 May 2020 10:54:48 +0200 Subject: [PATCH 12/16] [calamares] Register modules in test-loader - When a viewmodule is loaded, register it with the module manager (especially relevant for the slideshow module). --- src/calamares/testmain.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calamares/testmain.cpp b/src/calamares/testmain.cpp index 57da890cc..2b004ed7f 100644 --- a/src/calamares/testmain.cpp +++ b/src/calamares/testmain.cpp @@ -397,8 +397,9 @@ main( int argc, char* argv[] ) mw = module.m_ui ? new QMainWindow() : nullptr; (void)new Calamares::Branding( module.m_branding ); - (void)new Calamares::ModuleManager( QStringList(), nullptr ); + auto* modulemanager = new Calamares::ModuleManager( QStringList(), nullptr ); (void)Calamares::ViewManager::instance( mw ); + modulemanager->addModule( m ); } if ( !m->isLoaded() ) From 376cb3c042121f3b3a99f6dbdd59bd04fc481af0 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 20 May 2020 10:59:43 +0200 Subject: [PATCH 13/16] [calamares] Give the fake ExecutionViewModule a name - This is needed for addModule() so the module manager knows there is a module by the name x@x. - Tell the ExecutionViewStep to run jobs from x@x. --- src/calamares/testmain.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/calamares/testmain.cpp b/src/calamares/testmain.cpp index 2b004ed7f..a6b126d62 100644 --- a/src/calamares/testmain.cpp +++ b/src/calamares/testmain.cpp @@ -187,7 +187,6 @@ public: virtual Calamares::JobList jobs() const override; - protected: void initFrom( const QVariantMap& ) override; }; @@ -195,6 +194,9 @@ protected: ExecViewModule::ExecViewModule() : Calamares::Module() { + QVariantMap m; + m.insert( "name", "x" ); + Calamares::Module::initFrom(m, "x" ); } ExecViewModule::~ExecViewModule() {} @@ -210,6 +212,7 @@ ExecViewModule::loadSelf() auto* viewStep = new Calamares::ExecutionViewStep(); viewStep->setModuleInstanceKey( instanceKey() ); viewStep->setConfigurationMap( m_configurationMap ); + viewStep->appendJobModuleInstanceKey( "x@x" ); Calamares::ViewManager::instance()->addViewStep( viewStep ); m_loaded = true; } From 39b5dd4e6ec826c1513f953a3174a8deec29937e Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 20 May 2020 11:36:44 +0200 Subject: [PATCH 14/16] [libcalamaresui] Avoid deadlock - When loading QML V2, both loadQmlV2Complete() and changeSlideShowState() lock the same mutex, introduced in e7f4479df150efa21a3c225916ee5d6580b17064. - Explicitly unlock when loading is done and we need to change the state immediately. --- src/libcalamaresui/viewpages/Slideshow.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libcalamaresui/viewpages/Slideshow.cpp b/src/libcalamaresui/viewpages/Slideshow.cpp index baed36252..0c75dc390 100644 --- a/src/libcalamaresui/viewpages/Slideshow.cpp +++ b/src/libcalamaresui/viewpages/Slideshow.cpp @@ -125,7 +125,11 @@ SlideshowQML::loadQmlV2Complete() if ( isActive() ) { // We're alreay visible! Must have been slow QML loading, and we - // passed onActivate already. + // passed onActivate already. changeSlideShowState() locks + // the same mutex: we could set up a workaround to call + // changeSlideShowState() later after destruction of l. + // + l.unlock(); changeSlideShowState( Slideshow::Start ); } } From d51a545fcf513b176aa8b4a5acf8b927b202f541 Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 20 May 2020 11:44:27 +0200 Subject: [PATCH 15/16] [calamares] onInitComplete() already activates first step - In the test application, there is only one viewstep, so it is already activated; avoid double-activation. --- src/calamares/testmain.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/calamares/testmain.cpp b/src/calamares/testmain.cpp index a6b126d62..5c3dd5c95 100644 --- a/src/calamares/testmain.cpp +++ b/src/calamares/testmain.cpp @@ -425,7 +425,6 @@ main( int argc, char* argv[] ) mw->setCentralWidget( w ); w->show(); mw->show(); - vm->currentStep()->onActivate(); return aw->exec(); } From 738a6a9019eb900769e916a0176551135f231e7f Mon Sep 17 00:00:00 2001 From: Adriaan de Groot Date: Wed, 20 May 2020 12:12:11 +0200 Subject: [PATCH 16/16] [calamares] Make module-tester configurable in slideshow mode - Uses global storage to steer the jobs that are created, in case the slideshow needs to be tweaked by percentages or whatever. - While here, add some code docs and apply coding style. --- src/calamares/testmain.cpp | 40 +++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/calamares/testmain.cpp b/src/calamares/testmain.cpp index 5c3dd5c95..a29c8ce91 100644 --- a/src/calamares/testmain.cpp +++ b/src/calamares/testmain.cpp @@ -144,8 +144,9 @@ handle_args( QCoreApplication& a ) class ExecViewJob : public Calamares::CppJob { public: - explicit ExecViewJob( const QString& name ) + explicit ExecViewJob( const QString& name, unsigned long t = 3 ) : m_name( name ) + , m_delay( t ) { } virtual ~ExecViewJob() override; @@ -154,7 +155,7 @@ public: Calamares::JobResult exec() override { - QThread::sleep( 3 ); + QThread::sleep( m_delay ); return Calamares::JobResult::ok(); } @@ -162,6 +163,7 @@ public: private: QString m_name; + unsigned long m_delay; }; ExecViewJob::~ExecViewJob() {} @@ -194,9 +196,12 @@ protected: ExecViewModule::ExecViewModule() : Calamares::Module() { + // Normally the module-loader gives the module an instance key + // (out of the settings file, or the descriptor of the module). + // We don't have one, so build one -- this gives us "x@x". QVariantMap m; m.insert( "name", "x" ); - Calamares::Module::initFrom(m, "x" ); + Calamares::Module::initFrom( m, "x" ); } ExecViewModule::~ExecViewModule() {} @@ -212,7 +217,7 @@ ExecViewModule::loadSelf() auto* viewStep = new Calamares::ExecutionViewStep(); viewStep->setModuleInstanceKey( instanceKey() ); viewStep->setConfigurationMap( m_configurationMap ); - viewStep->appendJobModuleInstanceKey( "x@x" ); + viewStep->appendJobModuleInstanceKey( instanceKey().toString() ); Calamares::ViewManager::instance()->addViewStep( viewStep ); m_loaded = true; } @@ -234,9 +239,34 @@ Calamares::JobList ExecViewModule::jobs() const { Calamares::JobList l; + const auto* gs = Calamares::JobQueue::instance()->globalStorage(); + if ( gs && gs->contains( "jobs" ) ) + { + QVariantList joblist = gs->value( "jobs" ).toList(); + for ( const auto& jd : joblist ) + { + QVariantMap jobdescription = jd.toMap(); + if ( jobdescription.contains( "name" ) && jobdescription.contains( "delay" ) ) + { + l.append( Calamares::job_ptr( new ExecViewJob( jobdescription.value( "name" ).toString(), + jobdescription.value( "delay" ).toULongLong() ) ) ); + } + } + } + if ( l.count() > 0 ) + { + return l; + } + l.append( Calamares::job_ptr( new ExecViewJob( QStringLiteral( "step 1" ) ) ) ); l.append( Calamares::job_ptr( new ExecViewJob( QStringLiteral( "step two" ) ) ) ); - l.append( Calamares::job_ptr( new ExecViewJob( QStringLiteral( "step III" ) ) ) ); + l.append( Calamares::job_ptr( new ExecViewJob( QStringLiteral( "locking mutexes" ), 20 ) ) ); + l.append( Calamares::job_ptr( new ExecViewJob( QStringLiteral( "unlocking mutexes" ), 1 ) ) ); + for ( const QString& s : QStringList { "Harder", "Better", "Faster", "Stronger" } ) + { + l.append( Calamares::job_ptr( new ExecViewJob( s, 0 ) ) ); + } + l.append( Calamares::job_ptr( new ExecViewJob( QStringLiteral( "cleaning up" ), 20 ) ) ); return l; }