Commit 49c17942 authored by Philip Müller's avatar Philip Müller

Merge branch 'master' of https://github.com/calamares/calamares into development

parents 717ccf91 69c2d089
......@@ -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"
......@@ -117,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 )
{
......@@ -175,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 )
{
QDir dir( path );
if ( dir.exists() && dir.isReadable() )
{
importPath = dir;
found = true;
break;
}
}
if ( !found || !importPath.exists() || !importPath.isReadable() )
#ifdef WITH_QML
if ( !CalamaresUtils::initQmlModulesDir() )
{
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
}
......
......@@ -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 );
......
......@@ -22,18 +22,22 @@
* bindings.
*/
#include "modulesystem/Module.h"
#include "utils/Logger.h"
#include "utils/Yaml.h"
#include "Branding.h"
#include "CppJob.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"
#ifdef WITH_QML
#include "utils/Qml.h"
#endif
#include "utils/Yaml.h"
#include "viewpages/ExecutionViewStep.h"
#include <QApplication>
#include <QCommandLineOption>
......@@ -42,6 +46,7 @@
#include <QFileInfo>
#include <QLabel>
#include <QMainWindow>
#include <QThread>
#include <memory>
......@@ -80,6 +85,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 +99,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 <module> path.\n";
parser.showHelp();
......@@ -116,20 +124,161 @@ 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 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, unsigned long t = 3 )
: m_name( name )
, m_delay( t )
{
}
virtual ~ExecViewJob() override;
QString prettyName() const override { return m_name; }
Calamares::JobResult exec() override
{
QThread::sleep( m_delay );
return Calamares::JobResult::ok();
}
void setConfigurationMap( const QVariantMap& ) override {}
private:
QString m_name;
unsigned long m_delay;
};
ExecViewJob::~ExecViewJob() {}
/** @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()
{
// 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" );
}
ExecViewModule::~ExecViewModule() {}
void
ExecViewModule::initFrom( const QVariantMap& )
{
}
void
ExecViewModule::loadSelf()
{
auto* viewStep = new Calamares::ExecutionViewStep();
viewStep->setModuleInstanceKey( instanceKey() );
viewStep->setConfigurationMap( m_configurationMap );
viewStep->appendJobModuleInstanceKey( instanceKey().toString() );
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
{
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( "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;
}
static Calamares::Module*
load_module( const ModuleConfig& moduleConfig )
{
QString moduleName = moduleConfig.moduleName();
if ( moduleName == "-" )
{
return new ExecViewModule;
}
QFileInfo fi;
bool ok = false;
......@@ -188,6 +337,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 +363,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 );
......@@ -241,6 +402,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 )
......@@ -252,7 +417,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 );
......@@ -261,8 +430,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() )
......
......@@ -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
......@@ -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();
......
......@@ -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()
......
......@@ -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();
......
......@@ -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.
......
......@@ -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";
}
}
......
......@@ -207,6 +207,10 @@ unmarginLayout( QLayout* layout )
int
defaultFontSize()
{
if ( s_defaultFontSize <= 0 )
{
s_defaultFontSize = QFont().pointSize();
}
return s_defaultFontSize;
}
......
......@@ -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 <QByteArray>
......@@ -30,11 +32,81 @@
#include <QString>
#include <QVariant>
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;
}
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 )
callQmlFunction( QQuickItem* qmlObject, const char* method )
{