Merge branch 'issue-1176' into calamares

This doesn't actually implement the job-weight work, but lays important
groundwork in module instances and descriptors.

SEE #1176
FIXES #1473
FIXES #1474
main
Adriaan de Groot 4 years ago
commit e2f6817536

@ -193,7 +193,7 @@ DebugWindow::DebugWindow()
#endif
] {
QString moduleName = m_ui->modulesListView->currentIndex().data().toString();
Module* module = ModuleManager::instance()->moduleInstance( moduleName );
Module* module = ModuleManager::instance()->moduleInstance( ModuleSystem::InstanceKey::fromString( moduleName ) );
if ( module )
{
m_module = module->configurationMap();

@ -166,6 +166,11 @@ main( int argc, char* argv[] )
#endif
Calamares::Settings::init( is_debug );
if ( !Calamares::Settings::instance() || !Calamares::Settings::instance()->isValid() )
{
qCritical() << "Calamares has invalid settings, shutting down.";
return 78; // EX_CONFIG on FreeBSD
}
a.init();
return a.exec();
}

@ -184,13 +184,13 @@ public:
void loadSelf() override;
virtual Type type() const override;
virtual Interface interface() const override;
virtual Calamares::ModuleSystem::Type type() const override;
virtual Calamares::ModuleSystem::Interface interface() const override;
virtual Calamares::JobList jobs() const override;
protected:
void initFrom( const QVariantMap& ) override;
void initFrom( const Calamares::ModuleSystem::Descriptor& ) override;
};
ExecViewModule::ExecViewModule()
@ -201,13 +201,13 @@ ExecViewModule::ExecViewModule()
// 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( Calamares::ModuleSystem::Descriptor::fromDescriptorData(m), "x" );
}
ExecViewModule::~ExecViewModule() {}
void
ExecViewModule::initFrom( const QVariantMap& )
ExecViewModule::initFrom( const Calamares::ModuleSystem::Descriptor& )
{
}
@ -217,7 +217,7 @@ ExecViewModule::loadSelf()
auto* viewStep = new Calamares::ExecutionViewStep();
viewStep->setModuleInstanceKey( instanceKey() );
viewStep->setConfigurationMap( m_configurationMap );
viewStep->appendJobModuleInstanceKey( instanceKey().toString() );
viewStep->appendJobModuleInstanceKey( instanceKey() );
Calamares::ViewManager::instance()->addViewStep( viewStep );
m_loaded = true;
}
@ -332,7 +332,7 @@ load_module( const ModuleConfig& moduleConfig )
cDebug() << "Module" << moduleName << "job-configuration:" << configFile;
Calamares::Module* module = Calamares::moduleFromDescriptor( descriptor, name, configFile, moduleDirectory );
Calamares::Module* module = Calamares::moduleFromDescriptor( Calamares::ModuleSystem::Descriptor::fromDescriptorData( descriptor ), name, configFile, moduleDirectory );
return module;
}

@ -60,9 +60,9 @@ set( libSources
locale/TranslatableString.cpp
# Modules
modulesystem/Descriptor.cpp
modulesystem/InstanceKey.cpp
modulesystem/Module.cpp
modulesystem/Requirement.cpp
modulesystem/RequirementsChecker.cpp
modulesystem/RequirementsModel.cpp
@ -224,22 +224,28 @@ calamares_add_test(
)
calamares_add_test(
libcalamaresutilstest
libcalamaresgeoiptest
SOURCES
utils/Tests.cpp
geoip/GeoIPTests.cpp
${geoip_src}
)
calamares_add_test(
libcalamaresutilspathstest
libcalamareslocaletest
SOURCES
utils/TestPaths.cpp
locale/Tests.cpp
)
calamares_add_test(
libcalamaresgeoiptest
libcalamaresmodulesystemtest
SOURCES
geoip/GeoIPTests.cpp
${geoip_src}
modulesystem/Tests.cpp
)
calamares_add_test(
libcalamaresnetworktest
SOURCES
network/Tests.cpp
)
calamares_add_test(
@ -259,22 +265,17 @@ if( KPMcore_FOUND )
endif()
calamares_add_test(
libcalamareslocaletest
libcalamaresutilstest
SOURCES
locale/Tests.cpp
utils/Tests.cpp
)
calamares_add_test(
libcalamaresnetworktest
libcalamaresutilspathstest
SOURCES
network/Tests.cpp
utils/TestPaths.cpp
)
calamares_add_test(
libcalamaresmodulesystemtest
SOURCES
modulesystem/Tests.cpp
)
# This is not an actual test, it's a test / demo application
# for experimenting with GeoIP.

@ -1,5 +1,5 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
*
* SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
*

@ -1,6 +1,8 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
*
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2017 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -15,9 +17,6 @@
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
*
*/
#ifndef CALAMARES_JOB_H
#define CALAMARES_JOB_H
@ -114,7 +113,14 @@ public:
* For status and state information, see prettyStatusMessage().
*/
virtual QString prettyName() const = 0;
// TODO: Unused
/** @brief a longer human-readable description of what the job will do
*
* This **may** be used by view steps to fill in the summary
* messages for the summary page; at present, only the *partition*
* module does so.
*
* The default implementation returns an empty string.
*/
virtual QString prettyDescription() const;
/** @brief A human-readable status for progress reporting
*

@ -1,9 +1,10 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
*
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2019 Gabriel Craciunescu <crazy@frugalware.org>
* SPDX-FileCopyrightText: 2019 Dominic Hayes <ferenosdev@outlook.com>
* SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -18,9 +19,6 @@
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
*
*/
#include "Settings.h"
@ -75,22 +73,40 @@ requireBool( const YAML::Node& config, const char* key, bool d )
namespace Calamares
{
InstanceDescription::InstanceDescription( const QVariantMap& m )
: module( m.value( "module" ).toString() )
, id( m.value( "id" ).toString() )
, config( m.value( "config" ).toString() )
, weight( m.value( "weight" ).toInt() )
InstanceDescription::InstanceDescription( const Calamares::ModuleSystem::InstanceKey& key )
: m_instanceKey( key )
, m_weight( -1 )
{
if ( id.isEmpty() )
if ( !isValid() )
{
id = module;
m_weight = 0;
}
if ( config.isEmpty() )
else
{
config = module + QStringLiteral( ".conf" );
m_configFileName = key.module() + QStringLiteral( ".conf" );
}
}
InstanceDescription
InstanceDescription::fromSettings( const QVariantMap& m )
{
InstanceDescription r(
Calamares::ModuleSystem::InstanceKey( m.value( "module" ).toString(), m.value( "id" ).toString() ) );
if ( r.isValid() )
{
if ( m.value( "weight" ).isValid() )
{
int w = qBound( 1, m.value( "weight" ).toInt(), 100 );
r.m_weight = w;
}
weight = qBound( 1, weight, 100 );
QString c = m.value( "config" ).toString();
if ( !c.isEmpty() )
{
r.m_configFileName = c;
}
}
return r;
}
Settings* Settings::s_instance = nullptr;
@ -156,7 +172,13 @@ interpretInstances( const YAML::Node& node, Settings::InstanceDescriptionList& c
{
continue;
}
customInstances.append( InstanceDescription( instancesVListItem.toMap() ) );
auto description = InstanceDescription::fromSettings( instancesVListItem.toMap() );
if ( !description.isValid() )
{
cWarning() << "Invalid entry in *instances*" << instancesVListItem;
}
// Append it **anyway**, since this will bail out after Settings is constructed
customInstances.append( description );
}
}
}
@ -193,11 +215,23 @@ interpretSequence( const YAML::Node& node, Settings::ModuleSequence& moduleSeque
}
else
{
cDebug() << "Unknown action in *sequence*" << thisActionS;
continue;
}
QStringList thisActionRoster = sequenceVListItem.toMap().value( thisActionS ).toStringList();
moduleSequence.append( qMakePair( thisAction, thisActionRoster ) );
Calamares::ModuleSystem::InstanceKeyList roster;
roster.reserve( thisActionRoster.count() );
for ( const auto& s : thisActionRoster )
{
auto instanceKey = Calamares::ModuleSystem::InstanceKey::fromString( s );
if ( !instanceKey.isValid() )
{
cWarning() << "Invalid instance in *sequence*" << s;
}
roster.append( instanceKey );
}
moduleSequence.append( qMakePair( thisAction, roster ) );
}
}
else
@ -206,6 +240,16 @@ interpretSequence( const YAML::Node& node, Settings::ModuleSequence& moduleSeque
}
}
Settings::Settings( bool debugMode )
: QObject()
, m_debug( debugMode )
, m_doChroot( true )
, m_promptInstall( false )
, m_disableCancel( false )
, m_disableCancelDuringExec( false )
{
}
Settings::Settings( const QString& settingsFilePath, bool debugMode )
: QObject()
, m_debug( debugMode )
@ -218,30 +262,7 @@ Settings::Settings( const QString& settingsFilePath, bool debugMode )
QFile file( settingsFilePath );
if ( file.exists() && file.open( QFile::ReadOnly | QFile::Text ) )
{
QByteArray ba = file.readAll();
try
{
YAML::Node config = YAML::Load( ba.constData() );
Q_ASSERT( config.IsMap() );
interpretModulesSearch(
debugMode, CalamaresUtils::yamlToStringList( config[ "modules-search" ] ), m_modulesSearchPaths );
interpretInstances( config[ "instances" ], m_customModuleInstances );
interpretSequence( config[ "sequence" ], m_modulesSequence );
m_brandingComponentName = requireString( config, "branding" );
m_promptInstall = requireBool( config, "prompt-install", false );
m_doChroot = !requireBool( config, "dont-chroot", false );
m_isSetupMode = requireBool( config, "oem-setup", !m_doChroot );
m_disableCancel = requireBool( config, "disable-cancel", false );
m_disableCancelDuringExec = requireBool( config, "disable-cancel-during-exec", false );
m_quitAtEnd = requireBool( config, "quit-at-end", false );
}
catch ( YAML::Exception& e )
{
CalamaresUtils::explainYamlException( e, ba, file.fileName() );
}
setConfiguration( file.readAll(), file.fileName() );
}
else
{
@ -251,6 +272,61 @@ Settings::Settings( const QString& settingsFilePath, bool debugMode )
s_instance = this;
}
void
Settings::reconcileInstancesAndSequence()
{
// Since moduleFinder captures targetKey by reference, we can
// update targetKey to change what the finder lambda looks for.
Calamares::ModuleSystem::InstanceKey targetKey;
auto moduleFinder = [&targetKey]( const InstanceDescription& d ) { return d.isValid() && d.key() == targetKey; };
// Check the sequence against the existing instances (which so far are only custom)
for ( const auto& step : m_modulesSequence )
{
for ( const auto& instanceKey : step.second )
{
targetKey = instanceKey;
const auto it = std::find_if( m_moduleInstances.constBegin(), m_moduleInstances.constEnd(), moduleFinder );
if ( it == m_moduleInstances.constEnd() )
{
if ( instanceKey.isCustom() )
{
cWarning() << "Custom instance key" << instanceKey << "is not listed in the *instances*";
}
m_moduleInstances.append( InstanceDescription( instanceKey ) );
}
}
}
}
void
Settings::setConfiguration( const QByteArray& ba, const QString& explainName )
{
try
{
YAML::Node config = YAML::Load( ba.constData() );
Q_ASSERT( config.IsMap() );
interpretModulesSearch(
debugMode(), CalamaresUtils::yamlToStringList( config[ "modules-search" ] ), m_modulesSearchPaths );
interpretInstances( config[ "instances" ], m_moduleInstances );
interpretSequence( config[ "sequence" ], m_modulesSequence );
m_brandingComponentName = requireString( config, "branding" );
m_promptInstall = requireBool( config, "prompt-install", false );
m_doChroot = !requireBool( config, "dont-chroot", false );
m_isSetupMode = requireBool( config, "oem-setup", !m_doChroot );
m_disableCancel = requireBool( config, "disable-cancel", false );
m_disableCancelDuringExec = requireBool( config, "disable-cancel-during-exec", false );
m_quitAtEnd = requireBool( config, "quit-at-end", false );
reconcileInstancesAndSequence();
}
catch ( YAML::Exception& e )
{
CalamaresUtils::explainYamlException( e, ba, explainName );
}
}
QStringList
Settings::modulesSearchPaths() const
@ -260,9 +336,9 @@ Settings::modulesSearchPaths() const
Settings::InstanceDescriptionList
Settings::customModuleInstances() const
Settings::moduleInstances() const
{
return m_customModuleInstances;
return m_moduleInstances;
}
@ -368,4 +444,25 @@ Settings::init( const QString& path )
return new Calamares::Settings( path, true );
}
bool
Settings::isValid() const
{
if ( brandingComponentName().isEmpty() )
{
cWarning() << "No branding component is set";
return false;
}
const auto invalidDescriptor = []( const InstanceDescription& d ) { return !d.isValid(); };
const auto invalidDescriptorIt
= std::find_if( m_moduleInstances.constBegin(), m_moduleInstances.constEnd(), invalidDescriptor );
if ( invalidDescriptorIt != m_moduleInstances.constEnd() )
{
cWarning() << "Invalid module instance in *instances* or *sequence*";
return false;
}
return true;
}
} // namespace Calamares

@ -1,9 +1,10 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
*
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2019 Gabriel Craciunescu <crazy@frugalware.org>
* SPDX-FileCopyrightText: 2019 Dominic Hayes <ferenosdev@outlook.com>
* SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -18,9 +19,6 @@
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
*
*/
#ifndef SETTINGS_H
@ -28,6 +26,7 @@
#include "DllMacro.h"
#include "modulesystem/Actions.h"
#include "modulesystem/InstanceKey.h"
#include <QObject>
#include <QStringList>
@ -36,21 +35,63 @@
namespace Calamares
{
struct DLLEXPORT InstanceDescription
/** @brief Description of an instance as named in `settings.conf`
*
* An instance is an intended-step-in-sequence; it is not yet
* a loaded module. The instances have config-files and weights
* which are used by the module manager when loading modules
* and creating jobs.
*/
class DLLEXPORT InstanceDescription
{
InstanceDescription( const QVariantMap& );
using InstanceKey = Calamares::ModuleSystem::InstanceKey;
public:
/** @brief An invalid InstanceDescription
*
* Use `fromSettings()` to populate an InstanceDescription and
* check its validity.
*/
InstanceDescription() = default;
/** @brief An InstanceDescription with no special settings.
*
* Regardless of @p key being custom, sets weight to 1 and
* the configuration file to @c key.module() (plus the ".conf"
* extension).
*
* To InstanceDescription is custom if the key is.
*/
InstanceDescription( const InstanceKey& key );
QString module; ///< Module name (e.g. "welcome")
QString id; ///< Id, to distinguish multiple instances (e.g. "one", for "welcome@one")
QString config; ///< Config-file name (for multiple instances)
int weight;
static InstanceDescription fromSettings( const QVariantMap& );
bool isValid() const { return m_instanceKey.isValid(); }
const InstanceKey& key() const { return m_instanceKey; }
QString configFileName() const { return m_configFileName; }
bool isCustom() const { return m_instanceKey.isCustom(); }
int weight() const { return m_weight < 0 ? 1 : m_weight; }
bool explicitWeight() const { return m_weight > 0; }
private:
InstanceKey m_instanceKey;
QString m_configFileName;
int m_weight = 0;
};
class DLLEXPORT Settings : public QObject
{
Q_OBJECT
#ifdef BUILD_AS_TEST
public:
#endif
explicit Settings( bool debugMode );
explicit Settings( const QString& settingsFilePath, bool debugMode );
void setConfiguration( const QByteArray& configData, const QString& explainName );
void reconcileInstancesAndSequence();
public:
static Settings* instance();
/// @brief Find a settings.conf, following @p debugMode
@ -61,13 +102,34 @@ public:
QStringList modulesSearchPaths() const;
using InstanceDescriptionList = QList< InstanceDescription >;
InstanceDescriptionList customModuleInstances() const;
/** @brief All the module instances used
*
* Each module-instance mentioned in `settings.conf` has an entry
* in the moduleInstances list -- both custom entries that are
* in the *instances* section, and each module mentioned in the
* *sequence*.
*/
InstanceDescriptionList moduleInstances() const;
using ModuleSequence = QList< QPair< ModuleSystem::Action, QStringList > >;
using ModuleSequence = QList< QPair< ModuleSystem::Action, Calamares::ModuleSystem::InstanceKeyList > >;
/** @brief Representation of *sequence* of execution
*
* Each "section" of the *sequence* key in `settings.conf` gets an
* entry here, stating what kind of action is taken and which modules
* take part (in order). Each entry in the list is an instance key
* which can be found in the moduleInstances() list.
*/
ModuleSequence modulesSequence() const;
QString brandingComponentName() const;
/** @brief Are the settings consistent and valid?
*
* Checks that at least branding is set, and that the instances
* and sequence are valid.
*/
bool isValid() const;
/** @brief Is this a debugging run?
*
* Returns true if Calamares is in debug mode. In debug mode,
@ -113,7 +175,7 @@ private:
QStringList m_modulesSearchPaths;
InstanceDescriptionList m_customModuleInstances;
InstanceDescriptionList m_moduleInstances;
ModuleSequence m_modulesSequence;
QString m_brandingComponentName;

@ -20,6 +20,8 @@
*/
#include "GlobalStorage.h"
#include "Settings.h"
#include "modulesystem/InstanceKey.h"
#include "utils/Logger.h"
@ -39,6 +41,11 @@ private Q_SLOTS:
void testGSLoadSave();
void testGSLoadSave2();
void testGSLoadSaveYAMLStringList();
void testInstanceKey();
void testInstanceDescription();
void testSettings();
};
void
@ -177,6 +184,302 @@ TestLibCalamares::testGSLoadSaveYAMLStringList()
QCOMPARE( gs2.value( "dwarfs" ).toString(), QStringLiteral( "<QStringList>" ) ); // .. they're gone
}
void
TestLibCalamares::testInstanceKey()
{
using InstanceKey = Calamares::ModuleSystem::InstanceKey;
{
InstanceKey k;
QVERIFY( !k.isValid() );
QVERIFY( !k.isCustom() );
QVERIFY( k.module().isEmpty() );
}
{
InstanceKey k( QStringLiteral( "welcome" ), QString() );
QVERIFY( k.isValid() );
QVERIFY( !k.isCustom() );
QCOMPARE( k.module(), QStringLiteral( "welcome" ) );
QCOMPARE( k.id(), QStringLiteral( "welcome" ) );
}
{
InstanceKey k( QStringLiteral( "shellprocess" ), QStringLiteral( "zfssetup" ) );
QVERIFY( k.isValid() );
QVERIFY( k.isCustom() );
QCOMPARE( k.module(), QStringLiteral( "shellprocess" ) );
QCOMPARE( k.id(), QStringLiteral( "zfssetup" ) );
}
{
// This is a bad idea, names and ids with odd punctuation
InstanceKey k( QStringLiteral( " o__O " ), QString() );
QVERIFY( k.isValid() );
QVERIFY( !k.isCustom() );
QCOMPARE( k.module(), QStringLiteral( " o__O " ) );
}
{
// .. but @ is disallowed
InstanceKey k( QStringLiteral( "welcome@hi" ), QString() );
QVERIFY( !k.isValid() );
QVERIFY( !k.isCustom() );
QVERIFY( k.module().isEmpty() );
}
{
InstanceKey k = InstanceKey::fromString( "welcome" );
QVERIFY( k.isValid() );
QVERIFY( !k.isCustom() );
QCOMPARE( k.module(), QStringLiteral( "welcome" ) );
QCOMPARE( k.id(), QStringLiteral( "welcome" ) );
}
{
InstanceKey k = InstanceKey::fromString( "welcome@welcome" );
QVERIFY( k.isValid() );
QVERIFY( !k.isCustom() );
QCOMPARE( k.module(), QStringLiteral( "welcome" ) );
QCOMPARE( k.id(), QStringLiteral( "welcome" ) );
}
{
InstanceKey k = InstanceKey::fromString( "welcome@hi" );
QVERIFY( k.isValid() );
QVERIFY( k.isCustom() );
QCOMPARE( k.module(), QStringLiteral( "welcome" ) );
QCOMPARE( k.id(), QStringLiteral( "hi" ) );
}
{
InstanceKey k = InstanceKey::fromString( "welcome@hi@hi" );
QVERIFY( !k.isValid() );
QVERIFY( !k.isCustom() );
QVERIFY( k.module().isEmpty() );
QVERIFY( k.id().isEmpty() );
}
}
void
TestLibCalamares::testInstanceDescription()
{
using InstanceDescription = Calamares::InstanceDescription;
using InstanceKey = Calamares::ModuleSystem::InstanceKey;
// With invalid keys
//
//
{
InstanceDescription d;
QVERIFY( !d.isValid() );
QVERIFY( !d.isCustom() );
QCOMPARE( d.weight(), 0 );
QVERIFY( d.configFileName().isEmpty() );
QVERIFY( !d.explicitWeight() );
}
{
InstanceDescription d( InstanceKey {} ); // most-vexing, use brace-init instead
QVERIFY( !d.isValid() );
QVERIFY( !d.isCustom() );
QCOMPARE( d.weight(), 0 );
QVERIFY( d.configFileName().isEmpty() );
QVERIFY( !d.explicitWeight() );
}
// Private constructor
//
// This does set up the config file, to default values
{
InstanceDescription d( InstanceKey::fromString( "welcome" ) );
QVERIFY( d.isValid() );
QVERIFY( !d.isCustom() );
QCOMPARE( d.weight(), 1 ); // **now** the constraints kick in
QVERIFY( !d.configFileName().isEmpty() );
QCOMPARE( d.configFileName(), QStringLiteral( "welcome.conf" ) );
QVERIFY( !d.explicitWeight() );
}
{
InstanceDescription d( InstanceKey::fromString( "welcome@hi" ) );
QVERIFY( d.isValid() );
QVERIFY( d.isCustom() );
QCOMPARE( d.weight(), 1 ); // **now** the constraints kick in
QVERIFY( !d.configFileName().isEmpty() );
QCOMPARE( d.configFileName(), QStringLiteral( "welcome.conf" ) );
QVERIFY( !d.explicitWeight() );
}
// From settings, normal program flow
//
//
{
QVariantMap m;
InstanceDescription d = InstanceDescription::fromSettings( m );
QVERIFY( !d.isValid() );
}
{
QVariantMap m;
m.insert( "name", "welcome" );
InstanceDescription d = InstanceDescription::fromSettings( m );
QVERIFY( !d.isValid() );
QVERIFY( !d.explicitWeight() );
}
{
QVariantMap m;
m.insert( "module", "welcome" );
InstanceDescription d = InstanceDescription::fromSettings( m );
QVERIFY( d.isValid() );
QVERIFY( !d.isCustom() );
// Valid, but no weight set by settings
QCOMPARE( d.weight(), 1 );
QVERIFY( !d.explicitWeight() );
QCOMPARE( d.key().module(), QString( "welcome" ) );
QCOMPARE( d.key().id(), QString( "welcome" ) );
QCOMPARE( d.configFileName(), QString( "welcome.conf" ) );
}
{
QVariantMap m;
m.insert( "module", "welcome" );
m.insert( "weight", 1);
InstanceDescription d = InstanceDescription::fromSettings( m );
QVERIFY( d.isValid() );
QVERIFY( !d.isCustom() );
//Valid, set explicitly
QCOMPARE( d.weight(), 1 );
QVERIFY( d.explicitWeight() );
QCOMPARE( d.key().module(), QString( "welcome" ) );
QCOMPARE( d.key().id(), QString( "welcome" ) );
QCOMPARE( d.configFileName(), QString( "welcome.conf" ) );
}
{
QVariantMap m;
m.insert( "module", "welcome" );
m.insert( "id", "hi" );
m.insert( "weight", "17" ); // String, that's kind of bogus
InstanceDescription d = InstanceDescription::fromSettings( m );
QVERIFY( d.isValid() );
QVERIFY( d.isCustom() );
QCOMPARE( d.weight(), 17 );
QCOMPARE( d.key().module(), QString( "welcome" ) );
QCOMPARE( d.key().id(), QString( "hi" ) );
QCOMPARE( d.configFileName(), QString( "welcome.conf" ) );
QVERIFY( d.explicitWeight() );
}
{
QVariantMap m;
m.insert( "module", "welcome" );
m.insert( "id", "hi" );
m.insert( "weight", 134 );
m.insert( "config", "hi.conf" );
InstanceDescription d = InstanceDescription::fromSettings( m );
QVERIFY( d.isValid() );
QVERIFY( d.isCustom() );
QCOMPARE( d.weight(), 100 );
QCOMPARE( d.key().module(), QString( "welcome" ) );
QCOMPARE( d.key().id(), QString( "hi" ) );
QCOMPARE( d.configFileName(), QString( "hi.conf" ) );
QVERIFY( d.explicitWeight() );
}
}
void
TestLibCalamares::testSettings()
{
{
Calamares::Settings s( false );
QVERIFY( !s.debugMode() );
QVERIFY( !s.isValid() );
}
{
Calamares::Settings s( true );
QVERIFY( s.debugMode() );
QVERIFY( s.moduleInstances().isEmpty() );
QVERIFY( s.modulesSequence().isEmpty() );
QVERIFY( s.brandingComponentName().isEmpty() );
QVERIFY( !s.isValid() );
s.setConfiguration( R"(---
branding: default # needed for it to be considered valid
instances:
- module: welcome
id: hi
weight: 75
- module: welcome
id: yo
config: yolo.conf
sequence:
- show:
- welcome@hi
- welcome@yo
- dummycpp
- summary
- exec:
- welcome@hi
)",
QStringLiteral( "<testdata>" ) );
QVERIFY( s.debugMode() );
QCOMPARE( s.moduleInstances().count(), 4 ); // there are 4 module instances mentioned
QCOMPARE( s.modulesSequence().count(), 2 ); // 2 steps (show, exec)
QVERIFY( !s.brandingComponentName().isEmpty() );
QVERIFY( s.isValid() );
// Make a lambda where we can adjust what it looks for from the outside,
// by capturing a reference.
QString moduleKey = QString( "welcome" );
auto moduleFinder = [&moduleKey]( const Calamares::InstanceDescription& d ) {
return d.isValid() && d.key().module() == moduleKey;
};
const auto it0 = std::find_if( s.moduleInstances().constBegin(), s.moduleInstances().constEnd(), moduleFinder );
QVERIFY( it0 != s.moduleInstances().constEnd() );
moduleKey = QString( "derp" );
const auto it1 = std::find_if( s.moduleInstances().constBegin(), s.moduleInstances().constEnd(), moduleFinder );
QVERIFY( it1 == s.moduleInstances().constEnd() );
int validCount = 0;
int customCount = 0;
for ( const auto& d : s.moduleInstances() )
{
if ( d.isValid() )
{
validCount++;
}
if ( d.isCustom() )
{
customCount++;
}
QVERIFY( d.isCustom() ? d.isValid() : true ); // All custom entries are valid
if ( !d.isCustom() )
{
QCOMPARE( d.configFileName(), QString( "%1.conf" ).arg( d.key().module() ) );
}
else
{
// Specific cases from this config file
if ( d.key().id() == QString( "yo" ) )
{
QCOMPARE( d.configFileName(), QString( "yolo.conf" ) );
}
else
{
QCOMPARE( d.configFileName(), QString( "welcome.conf" ) ); // Not set in the settings data
}
}
}
QCOMPARE( customCount, 2 );
QCOMPARE( validCount, 4 ); // welcome@hi is listed twice, in *show* and *exec*
}
}
QTEST_GUILESS_MAIN( TestLibCalamares )

@ -1,7 +1,8 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
*
* SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -16,9 +17,6 @@
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
*
*/
#ifndef MODULESYSTEM_ACTIONS_H

@ -0,0 +1,137 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#include "Descriptor.h"
#include "utils/Logger.h"
#include "utils/Variant.h"
namespace Calamares
{
namespace ModuleSystem
{
const NamedEnumTable< Type >&
typeNames()
{
// *INDENT-OFF*
// clang-format off
static const NamedEnumTable< Type > table{
{ QStringLiteral( "job" ), Type::Job },
{ QStringLiteral( "view" ), Type::View },
{ QStringLiteral( "viewmodule" ), Type::View },
{ QStringLiteral( "jobmodule" ), Type::Job }
};
// *INDENT-ON*
// clang-format on
return table;
}
const NamedEnumTable< Interface >&
interfaceNames()
{
// *INDENT-OFF*
// clang-format off
static const NamedEnumTable< Interface > table {
{ QStringLiteral("process"), Interface::Process },
{ QStringLiteral("qtplugin"), Interface::QtPlugin },
{ QStringLiteral("python"), Interface::Python },
{ QStringLiteral("pythonqt"), Interface::PythonQt }
};
// *INDENT-ON*
// clang-format on
return table;
}
Descriptor::Descriptor() {}
Descriptor
Descriptor::fromDescriptorData( const QVariantMap& moduleDesc )
{
Descriptor d;
{
bool typeOk = false;
QString typeValue = moduleDesc.value( "type" ).toString();
Type t = typeNames().find( typeValue, typeOk );
if ( !typeOk )
{
cWarning() << "Module descriptor contains invalid *type*" << typeValue;
}
bool interfaceOk = false;
QString interfaceValue = moduleDesc.value( "interface" ).toString();
Interface i = interfaceNames().find( interfaceValue, interfaceOk );
if ( !interfaceOk )
{
cWarning() << "Module descriptor contains invalid *interface*" << interfaceValue;
}
d.m_name = moduleDesc.value( "name" ).toString();
if ( typeOk && interfaceOk && !d.m_name.isEmpty() )
{
d.m_type = t;
d.m_interface = i;
d.m_isValid = true;
}
}
if ( !d.m_isValid )
{
return d;
}
d.m_isEmergeny = CalamaresUtils::getBool( moduleDesc, "emergency", false );
d.m_hasConfig = !CalamaresUtils::getBool( moduleDesc, "noconfig", false ); // Inverted logic during load
d.m_requiredModules = CalamaresUtils::getStringList( moduleDesc, "requiredModules" );
QStringList consumedKeys { "type", "interface", "name", "emergency", "noconfig", "requiredModules" };
switch ( d.interface() )
{
case Interface::QtPlugin:
d.m_script = CalamaresUtils::getString( moduleDesc, "load" );
consumedKeys << "load";
break;
case Interface::Python:
case Interface::PythonQt:
d.m_script = CalamaresUtils::getString( moduleDesc, "script" );
consumedKeys << "script";
break;
case Interface::Process:
d.m_script = CalamaresUtils::getString( moduleDesc, "command" );
d.m_processTimeout = CalamaresUtils::getInteger( moduleDesc, "timeout", 30 );
d.m_processChroot = CalamaresUtils::getBool( moduleDesc, "chroot", false );
consumedKeys << "command"
<< "timeout"
<< "chroot";
if ( d.m_processTimeout < 0 )
{
d.m_processTimeout = 0;
}
break;
}
QStringList superfluousKeys;
for ( auto kv = moduleDesc.keyBegin(); kv != moduleDesc.keyEnd(); ++kv )
{
if ( !consumedKeys.contains( *kv ) )
{
superfluousKeys << *kv;
}
}
if ( !superfluousKeys.isEmpty() )
{
cWarning() << "Module descriptor contains extra keys:" << Logger::DebugList( superfluousKeys );
d.m_isValid = false;
}
return d;
}
} // namespace ModuleSystem
} // namespace Calamares

@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
*
* SPDX-FileCopyrightText: 2020 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -15,25 +16,136 @@
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
*
*/
#ifndef MODULESYSTEM_DESCRIPTOR_H
#define MODULESYSTEM_DESCRIPTOR_H
#include "utils/NamedEnum.h"
#include <QVariantMap>
namespace Calamares
{
namespace ModuleSystem
{
/**
* @brief The Type enum represents the intended functionality of the module
* Every module is either a job module or a view module.
* A job module is a single Calamares job.
* A view module has a UI (one or more view pages) and zero-to-many jobs.
*/
enum class Type
{
Job,
View
};
const NamedEnumTable< Type >& typeNames();
/**
* @brief The Interface enum represents the interface through which the module
* talks to Calamares.
* Not all Type-Interface associations are valid.
*/
enum class Interface
{
QtPlugin, // Jobs or Views
Python, // Jobs only
Process, // Deprecated interface
PythonQt // Views only, available as enum even if PythonQt isn't used
};
const NamedEnumTable< Interface >& interfaceNames();
/* While this isn't a useful *using* right now, the intention is
* to create a more strongly-typed Module Descriptor that carries
* only the necessary information and no variants.
*/
using Descriptor = QVariantMap;
class Descriptor
{
public:
///@brief an invalid, and empty, descriptor
Descriptor();
/** @brief Fills a descriptor from the loaded (YAML) data.
*
*/
static Descriptor fromDescriptorData( const QVariantMap& moduleDesc );
bool isValid() const { return m_isValid; }
QString name() const { return m_name; }
Type type() const { return m_type; }
Interface interface() const { return m_interface; }
bool isEmergency() const { return m_isEmergeny; }
bool hasConfig() const { return m_hasConfig; }
/// @brief The directory where the module.desc lives
QString directory() const { return m_directory; }
void setDirectory( const QString& d ) { m_directory = d; }
const QStringList& requiredModules() const { return m_requiredModules; }
/** @section C++ Modules
*
* The C++ modules are the most general, and are loaded as
* a shared library after which a suitable factory creates
* objects from them.
*/
/// @brief Short path to the shared-library; no extension.
QString load() const { return m_interface == Interface::QtPlugin ? m_script : QString(); }
/** @section Process Job modules
*
* Process Jobs are somewhat deprecated in favor of shellprocess
* and contextualprocess jobs, since those handle multiple configuration
* much more gracefully.
*
* Process Jobs execute one command.
*/
/// @brief The command to execute; passed to the shell
QString command() const { return m_interface == Interface::Process ? m_script : QString(); }
/// @brief Timeout in seconds
int timeout() const { return m_processTimeout; }
/// @brief Run command in chroot?
bool chroot() const { return m_processChroot; }
/** @section Python Job modules
*
* Python job modules have one specific script to load and run.
*/
QString script() const
{
return ( m_interface == Interface::Python || m_interface == Interface::PythonQt ) ? m_script : QString();
}
private:
QString m_name;
QString m_directory;
QStringList m_requiredModules;
Type m_type;
Interface m_interface;
bool m_isValid = false;
bool m_isEmergeny = false;
bool m_hasConfig = true;
/** @brief The name of the thing to load
*
* - A C++ module loads a shared library (via key *load*),
* - A Python module loads a Python script (via key *script*),
* - A process runs a specific command (via key *command*)
*
* This name-of-the-thing is stored here, regardless of which
* interface is being used.
*/
QString m_script;
int m_processTimeout = 30;
bool m_processChroot = false;
};
} // namespace ModuleSystem
} // namespace Calamares

@ -1,7 +1,8 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
*
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -16,9 +17,6 @@
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
*
*/
#include "InstanceKey.h"

@ -1,7 +1,8 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
*
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -16,14 +17,12 @@
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
*
*/
#ifndef MODULESYSTEM_INSTANCEKEY_H
#define MODULESYSTEM_INSTANCEKEY_H
#include <QDebug>
#include <QList>
#include <QPair>
#include <QString>
@ -98,6 +97,8 @@ private:
}
};
using InstanceKeyList = QList< InstanceKey >;
QDebug& operator<<( QDebug& s, const Calamares::ModuleSystem::InstanceKey& i );
} // namespace ModuleSystem

@ -1,7 +1,8 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
*
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2017-2018 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -16,9 +17,6 @@
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
*
*/
#include "Module.h"
@ -51,10 +49,10 @@ Module::~Module() {}
void
Module::initFrom( const Calamares::ModuleSystem::Descriptor& moduleDescriptor, const QString& id )
{
m_key = ModuleSystem::InstanceKey( moduleDescriptor.value( "name" ).toString(), id );
if ( moduleDescriptor.contains( EMERGENCY ) )
m_key = ModuleSystem::InstanceKey( moduleDescriptor.name(), id );
if ( moduleDescriptor.isEmergency() )
{
m_maybe_emergency = moduleDescriptor[ EMERGENCY ].toBool();
m_maybe_emergency = true;
}
}
@ -135,54 +133,20 @@ Module::loadConfigurationFile( const QString& configFileName ) //throws YAML::E
}
static const NamedEnumTable< Module::Type >&
typeNames()
{
using Type = Module::Type;
// *INDENT-OFF*
// clang-format off
static const NamedEnumTable< Type > table{
{ QStringLiteral( "job" ), Type::Job },
{ QStringLiteral( "view" ), Type::View },
{ QStringLiteral( "viewmodule" ), Type::View },
{ QStringLiteral( "jobmodule" ), Type::Job }
};
// *INDENT-ON*
// clang-format on
return table;
}
QString
Module::typeString() const
{
bool ok = false;
QString v = typeNames().find( type(), ok );
QString v = Calamares::ModuleSystem::typeNames().find( type(), ok );
return ok ? v : QString();
}
static const NamedEnumTable< Module::Interface >&
interfaceNames()
{
using Interface = Module::Interface;
// *INDENT-OFF*
// clang-format off
static const NamedEnumTable< Interface > table {
{ QStringLiteral("process"), Interface::Process },
{ QStringLiteral("qtplugin"), Interface::QtPlugin },
{ QStringLiteral("python"), Interface::Python },
{ QStringLiteral("pythonqt"), Interface::PythonQt }
};
// *INDENT-ON*
// clang-format on
return table;
}
QString
Module::interfaceString() const
{
bool ok = false;
QString v = interfaceNames().find( interface(), ok );
QString v = Calamares::ModuleSystem::interfaceNames().find( interface(), ok );
return ok ? v : QString();
}

@ -1,7 +1,8 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
*
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2017 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -16,9 +17,6 @@
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
*
*/
#ifndef CALAMARES_MODULE_H
@ -53,30 +51,8 @@ Module* moduleFromDescriptor( const ModuleSystem::Descriptor& moduleDescriptor,
class DLLEXPORT Module
{
public:
/**
* @brief The Type enum represents the intended functionality of the module
* Every module is either a job module or a view module.
* A job module is a single Calamares job.
* A view module has a UI (one or more view pages) and zero-to-many jobs.
*/
enum class Type
{
Job,
View
};
/**
* @brief The Interface enum represents the interface through which the module
* talks to Calamares.
* Not all Type-Interface associations are valid.
*/
enum class Interface
{
QtPlugin, // Jobs or Views
Python, // Jobs only
Process, // Deprecated interface
PythonQt // Views only, available as enum even if PythonQt isn't used
};
using Type = ModuleSystem::Type;
using Interface = ModuleSystem::Interface;
virtual ~Module();
@ -176,9 +152,9 @@ protected:
explicit Module();
/// @brief For subclasses to read their part of the descriptor
virtual void initFrom( const QVariantMap& moduleDescriptor ) = 0;
virtual void initFrom( const ModuleSystem::Descriptor& moduleDescriptor ) = 0;
/// @brief Generic part of descriptor reading (and instance id)
void initFrom( const QVariantMap& moduleDescriptor, const QString& id );
void initFrom( const ModuleSystem::Descriptor& moduleDescriptor, const QString& id );
QVariantMap m_configurationMap;

@ -1,22 +0,0 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* SPDX-FileCopyrightText: 2017 Adriaan de Groot <groot@kde.org>
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Calamares is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
*
*/
#include "Requirement.h"

@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
*
* SPDX-FileCopyrightText: 2017 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -15,9 +16,6 @@
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
*
*/
#ifndef CALAMARES_REQUIREMENT_H
#define CALAMARES_REQUIREMENT_H

@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
*
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -15,9 +16,6 @@
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
*
*/
#include "RequirementsChecker.h"

@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
*
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -15,9 +16,6 @@
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
*
*/
#ifndef CALAMARES_REQUIREMENTSCHECKER_H
#define CALAMARES_REQUIREMENTSCHECKER_H

@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* SPDX-FileCopyrightText: 2019-2020 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -15,9 +16,6 @@
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
*
*/
#include "RequirementsModel.h"

@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
*
* SPDX-FileCopyrightText: 2019-2020 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -15,9 +16,6 @@
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
*
*/
#ifndef CALAMARES_REQUIREMENTSMODEL_H

@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
*
* SPDX-FileCopyrightText: 2019 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -15,11 +16,9 @@
* You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
* License-Filename: LICENSE
*
*/
#include "modulesystem/Descriptor.h"
#include "modulesystem/InstanceKey.h"
#include <QtTest/QtTest>
@ -42,6 +41,8 @@ private Q_SLOTS:
void testBadSimpleCases();
void testBadFromStringCases();
void testBasicDescriptor();
};
void
@ -138,6 +139,47 @@ ModuleSystemTests::testBadFromStringCases()
assert_is_invalid( k0 );
}
void
ModuleSystemTests::testBasicDescriptor()
{
{
QVariantMap m;
auto d = Calamares::ModuleSystem::Descriptor::fromDescriptorData( m );
QVERIFY( !d.isValid() );
QVERIFY( d.name().isEmpty() );
}
{
QVariantMap m;
m.insert( "name", QVariant() );
auto d = Calamares::ModuleSystem::Descriptor::fromDescriptorData( m );
QVERIFY( !d.isValid() );
QVERIFY( d.name().isEmpty() );
}
{
QVariantMap m;
m.insert( "name", 17 );
auto d = Calamares::ModuleSystem::Descriptor::fromDescriptorData( m );
QVERIFY( !d.isValid() );
QVERIFY( !d.name().isEmpty() );
QCOMPARE( d.name(), QStringLiteral( "17" ) ); // Strange but true
}
{
QVariantMap m;
m.insert( "name", "welcome" );
m.insert( "type", "viewmodule" );
m.insert( "interface", "qtplugin" );
auto d = Calamares::ModuleSystem::Descriptor::fromDescriptorData( m );
// QVERIFY( !d.isValid() );
QCOMPARE( d.name(), QStringLiteral( "welcome" ) );
QCOMPARE( d.type(), Calamares::ModuleSystem::Type::View );
QCOMPARE( d.interface(), Calamares::ModuleSystem::Interface::QtPlugin );
}
}
QTEST_GUILESS_MAIN( ModuleSystemTests )

@ -105,6 +105,16 @@ struct NamedEnumTable
// ok is still false
return string_t();
}
/** @brief Find a value @p s in the table and return its name.
*
* Returns emptry string if the value is not found.
*/
string_t find( enum_t s ) const
{
bool ok = false;
return find( s, ok );
}
};
/** @brief Smashes an enum value to its underlying type. */

@ -84,13 +84,12 @@ CppJobModule::jobs() const
void
CppJobModule::initFrom( const QVariantMap& moduleDescriptor )
CppJobModule::initFrom( const ModuleSystem::Descriptor& moduleDescriptor )
{
QDir directory( location() );
QString load;
if ( !moduleDescriptor.value( "load" ).toString().isEmpty() )
QString load = moduleDescriptor.load();
if ( !load.isEmpty() )
{
load = moduleDescriptor.value( "load" ).toString();
load = directory.absoluteFilePath( load );
}
// If a load path is not specified, we look for a plugin to load in the directory.

@ -39,7 +39,7 @@ public:
JobList jobs() const override;
protected:
void initFrom( const QVariantMap& moduleDescriptor ) override;
void initFrom( const ModuleSystem::Descriptor& moduleDescriptor ) override;
private:
explicit CppJobModule();

@ -52,23 +52,22 @@ moduleFromDescriptor( const Calamares::ModuleSystem::Descriptor& moduleDescripto
const QString& configFileName,
const QString& moduleDirectory )
{
std::unique_ptr< Module > m;
using Type = Calamares::ModuleSystem::Type;
using Interface = Calamares::ModuleSystem::Interface;
QString typeString = moduleDescriptor.value( "type" ).toString();
QString intfString = moduleDescriptor.value( "interface" ).toString();
std::unique_ptr< Module > m;
if ( typeString.isEmpty() || intfString.isEmpty() )
{
if ( !moduleDescriptor.isValid() ) {
cError() << "Bad module descriptor format" << instanceId;
return nullptr;
}
if ( ( typeString == "view" ) || ( typeString == "viewmodule" ) )
if ( moduleDescriptor.type() == Type::View )
{
if ( intfString == "qtplugin" )
if ( moduleDescriptor.interface() == Interface::QtPlugin )
{
m.reset( new ViewModule() );
}
else if ( intfString == "pythonqt" )
else if ( moduleDescriptor.interface() == Interface::PythonQt )
{
#ifdef WITH_PYTHONQT
m.reset( new PythonQtViewModule() );
@ -78,20 +77,20 @@ moduleFromDescriptor( const Calamares::ModuleSystem::Descriptor& moduleDescripto
}
else
{
cError() << "Bad interface" << intfString << "for module type" << typeString;
cError() << "Bad interface" << Calamares::ModuleSystem::interfaceNames().find( moduleDescriptor.interface() ) << "for module type" << Calamares::ModuleSystem::typeNames().find( moduleDescriptor.type() );
}
}
else if ( typeString == "job" )
else if ( moduleDescriptor.type() == Type::Job )
{
if ( intfString == "qtplugin" )
if ( moduleDescriptor.interface() == Interface::QtPlugin )
{
m.reset( new CppJobModule() );
}
else if ( intfString == "process" )
else if ( moduleDescriptor.interface() == Interface::Process )
{
m.reset( new ProcessJobModule() );
}
else if ( intfString == "python" )
else if ( moduleDescriptor.interface() == Interface::Python )
{
#ifdef WITH_PYTHON
m.reset( new PythonJobModule() );
@ -101,17 +100,17 @@ moduleFromDescriptor( const Calamares::ModuleSystem::Descriptor& moduleDescripto
}
else
{
cError() << "Bad interface" << intfString << "for module type" << typeString;
cError() << "Bad interface" << Calamares::ModuleSystem::interfaceNames().find( moduleDescriptor.interface() ) << "for module type" << Calamares::ModuleSystem::typeNames().find( moduleDescriptor.type() );
}
}
else
{
cError() << "Bad module type" << typeString;
cError() << "Bad module type" << Calamares::ModuleSystem::typeNames().find( moduleDescriptor.type() );
}
if ( !m )
{
cError() << "Bad module type (" << typeString << ") or interface string (" << intfString << ") for module "
cError() << "Bad module type (" << Calamares::ModuleSystem::typeNames().find( moduleDescriptor.type() ) << ") or interface string (" << Calamares::ModuleSystem::interfaceNames().find( moduleDescriptor.interface() ) << ") for module "
<< instanceId;
return nullptr;
}

@ -1,7 +1,8 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2018, Adriaan de Groot <groot@kde.org>
* SPDX-FileCopyrightText: 2014-2015 Teo Mrnjavac <teo@kde.org>
* SPDX-FileCopyrightText: 2018 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -113,9 +114,9 @@ ModuleManager::doInit()
if ( ok && !moduleName.isEmpty() && ( moduleName == currentDir.dirName() )
&& !m_availableDescriptorsByModuleName.contains( moduleName ) )
{
m_availableDescriptorsByModuleName.insert( moduleName, moduleDescriptorMap );
m_moduleDirectoriesByModuleName.insert( moduleName,
descriptorFileInfo.absoluteDir().absolutePath() );
auto descriptor = Calamares::ModuleSystem::Descriptor::fromDescriptorData( moduleDescriptorMap );
descriptor.setDirectory(descriptorFileInfo.absoluteDir().absolutePath() );
m_availableDescriptorsByModuleName.insert( moduleName, descriptor );
}
}
else
@ -131,8 +132,7 @@ ModuleManager::doInit()
}
// At this point m_availableDescriptorsByModuleName is filled with
// the modules that were found in the search paths.
cDebug() << "Found" << m_availableDescriptorsByModuleName.count() << "modules"
<< m_moduleDirectoriesByModuleName.count() << "names";
cDebug() << "Found" << m_availableDescriptorsByModuleName.count() << "modules";
emit initDone();
}
@ -151,66 +151,43 @@ ModuleManager::moduleDescriptor( const QString& name )
}
Module*
ModuleManager::moduleInstance( const QString& instanceKey )
ModuleManager::moduleInstance( const ModuleSystem::InstanceKey& instanceKey )
{
return m_loadedModulesByInstanceKey.value( ModuleSystem::InstanceKey::fromString( instanceKey ) );
return m_loadedModulesByInstanceKey.value( instanceKey );
}
/**
* @brief Search a list of instance descriptions for one matching @p module and @p id
*
* @return -1 on failure, otherwise index of the instance that matches.
*/
static int
findCustomInstance( const Settings::InstanceDescriptionList& customInstances, const ModuleSystem::InstanceKey& m )
{
for ( int i = 0; i < customInstances.count(); ++i )
{
const auto& thisInstance = customInstances[ i ];
if ( thisInstance.module == m.module() && thisInstance.id == m.id() )
{
return i;
}
}
return -1;
}
/** @brief Returns the config file name for the fiven @p instanceKey
/** @brief Returns the config file name for the given @p instanceKey
*
* Custom instances have custom config files, non-custom ones
* have a <modulename>.conf file. Returns an empty QString on
* errors.
*/
static QString
getConfigFileName( const Settings::InstanceDescriptionList& customInstances,
getConfigFileName( const Settings::InstanceDescriptionList& descriptorList,
const ModuleSystem::InstanceKey& instanceKey,
const ModuleSystem::Descriptor& descriptor )
const ModuleSystem::Descriptor& thisModule )
{
if ( instanceKey.isCustom() )
if ( !thisModule.hasConfig() )
{
int found = findCustomInstance( customInstances, instanceKey );
if ( found < 0 )
{
// This should already have been checked and failed the module already
return QString();
}
return customInstances[ found ].config;
// Explicitly set to no-configuration. This doesn't apply
// to custom instances (above) since the only reason to
// **have** a custom instance is to specify a different
// config file for more than one module.
return QString();
}
else
for ( const auto& descriptor : descriptorList )
{
if ( descriptor.value( "noconfig", false ).toBool() )
if ( descriptor.key() == instanceKey )
{
// Explicitly set to no-configuration. This doesn't apply
// to custom instances (above) since the only reason to
// **have** a custom instance is to specify a different
// config file for more than one module.
return QString();
return descriptor.configFileName();
}
return QString( "%1.conf" ).arg( instanceKey.module() );
}
// This should already have been checked and failed the module already
return QString();
}
void
@ -220,7 +197,7 @@ ModuleManager::loadModules()
{
cWarning() << "Some installed modules have unmet dependencies.";
}
Settings::InstanceDescriptionList customInstances = Settings::instance()->customModuleInstances();
Settings::InstanceDescriptionList customInstances = Settings::instance()->moduleInstances();
QStringList failedModules;
const auto modulesSequence = Settings::instance()->modulesSequence();
@ -228,29 +205,18 @@ ModuleManager::loadModules()
{
ModuleSystem::Action currentAction = modulePhase.first;
foreach ( const QString& moduleEntry, modulePhase.second )
for ( const auto& instanceKey : modulePhase.second )
{
auto instanceKey = ModuleSystem::InstanceKey::fromString( moduleEntry );
if ( !instanceKey.isValid() )
{
cError() << "Wrong module entry format for module" << moduleEntry;
failedModules.append( moduleEntry );
cError() << "Wrong module entry format for module" << instanceKey;
failedModules.append( instanceKey.toString() );
continue;
}
if ( instanceKey.isCustom() )
{
int found = findCustomInstance( customInstances, instanceKey );
if ( found < 0 )
{
cError() << "Custom instance" << moduleEntry << "not found in custom instances section.";
failedModules.append( moduleEntry );
continue;
}
}
ModuleSystem::Descriptor descriptor
= m_availableDescriptorsByModuleName.value( instanceKey.module(), ModuleSystem::Descriptor() );
if ( descriptor.isEmpty() )
if ( !descriptor.isValid() )
{
cError() << "Module" << instanceKey.toString() << "not found in module search paths."
<< Logger::DebugList( m_paths );
@ -291,7 +257,7 @@ ModuleManager::loadModules()
= Calamares::moduleFromDescriptor( descriptor,
instanceKey.id(),
configFileName,
m_moduleDirectoriesByModuleName.value( instanceKey.module() ) );
descriptor.directory() );
if ( !thisModule )
{
cError() << "Module" << instanceKey.toString() << "cannot be created from descriptor"
@ -320,7 +286,7 @@ ModuleManager::loadModules()
ViewManager::instance()->addViewStep( evs );
}
evs->appendJobModuleInstanceKey( instanceKey.toString() );
evs->appendJobModuleInstanceKey( instanceKey );
}
}
}
@ -336,7 +302,7 @@ ModuleManager::loadModules()
}
bool
ModuleManager::addModule( Module *module )
ModuleManager::addModule( Module* module )
{
if ( !module )
{
@ -344,7 +310,7 @@ ModuleManager::addModule( Module *module )
}
if ( !module->instanceKey().isValid() )
{
cWarning() << "Module" << module->location() << Logger::Pointer(module) << "has invalid instance key.";
cWarning() << "Module" << module->location() << Logger::Pointer( module ) << "has invalid instance key.";
return false;
}
if ( !checkModuleDependencies( *module ) )
@ -383,13 +349,15 @@ ModuleManager::checkRequirements()
RequirementsChecker* rq = new RequirementsChecker( modules, m_requirementsModel, this );
connect( rq, &RequirementsChecker::done, rq, &RequirementsChecker::deleteLater );
connect( rq, &RequirementsChecker::done, this, [=](){ this->requirementsComplete( m_requirementsModel->satisfiedMandatory() ); } );
connect( rq, &RequirementsChecker::done, this, [=]() {
this->requirementsComplete( m_requirementsModel->satisfiedMandatory() );
} );
QTimer::singleShot( 0, rq, &RequirementsChecker::run );
}
static QStringList
missingRequiredModules( const QStringList& required, const QMap< QString, QVariantMap >& available )
missingRequiredModules( const QStringList& required, const QMap< QString, ModuleSystem::Descriptor >& available )
{
QStringList l;
for ( const QString& depName : required )
@ -417,12 +385,12 @@ ModuleManager::checkDependencies()
for ( auto it = m_availableDescriptorsByModuleName.begin(); it != m_availableDescriptorsByModuleName.end();
++it )
{
QStringList unmet = missingRequiredModules( it->value( "requiredModules" ).toStringList(),
QStringList unmet = missingRequiredModules( it->requiredModules(),
m_availableDescriptorsByModuleName );
if ( unmet.count() > 0 )
{
QString moduleName = it->value( "name" ).toString();
QString moduleName = it->name();
somethingWasRemovedBecauseOfUnmetDependencies = true;
m_availableDescriptorsByModuleName.erase( it );
numberRemoved++;
@ -446,7 +414,7 @@ ModuleManager::checkModuleDependencies( const Module& m )
bool allRequirementsFound = true;
QStringList requiredModules
= m_availableDescriptorsByModuleName[ m.name() ].value( "requiredModules" ).toStringList();
= m_availableDescriptorsByModuleName[ m.name() ].requiredModules();
for ( const QString& required : requiredModules )
{

@ -76,7 +76,7 @@ public:
* @param instanceKey the instance key for a module instance.
* @return a pointer to an object of a subtype of Module.
*/
Module* moduleInstance( const QString& instanceKey );
Module* moduleInstance( const ModuleSystem::InstanceKey& instanceKey );
/**
* @brief loadModules does all of the module loading operation.
@ -162,7 +162,6 @@ private:
bool checkModuleDependencies( const Module& );
QMap< QString, ModuleSystem::Descriptor > m_availableDescriptorsByModuleName;
QMap< QString, QString > m_moduleDirectoriesByModuleName;
QMap< ModuleSystem::InstanceKey, Module* > m_loadedModulesByInstanceKey;
const QStringList m_paths;
RequirementsModel* m_requirementsModel;

@ -61,32 +61,14 @@ ProcessJobModule::jobs() const
void
ProcessJobModule::initFrom( const QVariantMap& moduleDescriptor )
ProcessJobModule::initFrom( const ModuleSystem::Descriptor& moduleDescriptor )
{
QDir directory( location() );
m_workingPath = directory.absolutePath();
if ( !moduleDescriptor.value( "command" ).toString().isEmpty() )
{
m_command = moduleDescriptor.value( "command" ).toString();
}
m_secondsTimeout = std::chrono::seconds( 30 );
if ( moduleDescriptor.contains( "timeout" ) && !moduleDescriptor.value( "timeout" ).isNull() )
{
int sec = moduleDescriptor.value( "timeout" ).toInt();
if ( sec < 0 )
{
sec = 0;
}
m_secondsTimeout = std::chrono::seconds( sec );
}
m_runInChroot = false;
if ( moduleDescriptor.contains( "chroot" ) && !moduleDescriptor.value( "chroot" ).isNull() )
{
m_runInChroot = moduleDescriptor.value( "chroot" ).toBool();
}
m_command = moduleDescriptor.command();
m_secondsTimeout = std::chrono::seconds( moduleDescriptor.timeout() );
m_runInChroot = moduleDescriptor.chroot();
}

@ -38,7 +38,7 @@ public:
JobList jobs() const override;
protected:
void initFrom( const QVariantMap& moduleDescriptor ) override;
void initFrom( const ModuleSystem::Descriptor& moduleDescriptor ) override;
private:
explicit ProcessJobModule();

@ -62,15 +62,11 @@ PythonJobModule::jobs() const
void
PythonJobModule::initFrom( const QVariantMap& moduleDescriptor )
PythonJobModule::initFrom( const ModuleSystem::Descriptor& moduleDescriptor )
{
QDir directory( location() );
m_workingPath = directory.absolutePath();
if ( !moduleDescriptor.value( "script" ).toString().isEmpty() )
{
m_scriptFileName = moduleDescriptor.value( "script" ).toString();
}
m_scriptFileName = moduleDescriptor.script();
}

@ -35,7 +35,7 @@ public:
JobList jobs() const override;
protected:
void initFrom( const QVariantMap& moduleDescriptor ) override;
void initFrom( const ModuleSystem::Descriptor& moduleDescriptor ) override;
private:
explicit PythonJobModule();

@ -89,13 +89,12 @@ ViewModule::jobs() const
void
ViewModule::initFrom( const QVariantMap& moduleDescriptor )
ViewModule::initFrom( const ModuleSystem::Descriptor& moduleDescriptor )
{
QDir directory( location() );
QString load;
if ( !moduleDescriptor.value( "load" ).toString().isEmpty() )
QString load = moduleDescriptor.load();
if ( !load.isEmpty() )
{
load = moduleDescriptor.value( "load" ).toString();
load = directory.absoluteFilePath( load );
}
// If a load path is not specified, we look for a plugin to load in the directory.

@ -42,7 +42,7 @@ public:
RequirementsList checkRequirements() override;
protected:
void initFrom( const QVariantMap& moduleDescriptor ) override;
void initFrom( const ModuleSystem::Descriptor& moduleDescriptor ) override;
private:
explicit ViewModule();

@ -147,7 +147,7 @@ ExecutionViewStep::onActivate()
m_slideshow->changeSlideShowState( Slideshow::Start );
JobQueue* queue = JobQueue::instance();
foreach ( const QString& instanceKey, m_jobInstanceKeys )
for( const auto& instanceKey : m_jobInstanceKeys )
{
Calamares::Module* module = Calamares::ModuleManager::instance()->moduleInstance( instanceKey );
if ( module )
@ -176,7 +176,7 @@ ExecutionViewStep::jobs() const
void
ExecutionViewStep::appendJobModuleInstanceKey( const QString& instanceKey )
ExecutionViewStep::appendJobModuleInstanceKey( const ModuleSystem::InstanceKey& instanceKey )
{
m_jobInstanceKeys.append( instanceKey );
}

@ -21,6 +21,7 @@
#define EXECUTIONVIEWSTEP_H
#include "ViewStep.h"
#include "modulesystem/InstanceKey.h"
#include <QStringList>
@ -57,7 +58,7 @@ public:
JobList jobs() const override;
void appendJobModuleInstanceKey( const QString& instanceKey );
void appendJobModuleInstanceKey( const ModuleSystem::InstanceKey& instanceKey );
private:
QWidget* m_widget;
@ -65,7 +66,7 @@ private:
QLabel* m_label;
Slideshow* m_slideshow;
QStringList m_jobInstanceKeys;
QList< ModuleSystem::InstanceKey > m_jobInstanceKeys;
void updateFromJobQueue( qreal percent, const QString& message );
};

@ -2,5 +2,4 @@
type: "job"
name: "displaymanager"
interface: "python"
requires: []
script: "main.py"

@ -2,6 +2,5 @@
type: "job"
name: "hwclock"
interface: "python"
requires: []
script: "main.py"
noconfig: true

@ -2,6 +2,5 @@
type: "job"
name: "networkcfg"
interface: "python"
requires: []
script: "main.py"
noconfig: true

@ -41,12 +41,6 @@ PlasmaLnfJob::prettyName() const
return tr( "Plasma Look-and-Feel Job" );
}
QString
PlasmaLnfJob::prettyDescription() const
{
return prettyName();
}
QString PlasmaLnfJob::prettyStatusMessage() const
{
return prettyName();

@ -33,7 +33,6 @@ public:
virtual ~PlasmaLnfJob() override;
QString prettyName() const override;
QString prettyDescription() const override;
QString prettyStatusMessage() const override;
Calamares::JobResult exec() override;

@ -2,5 +2,4 @@
type: "job"
name: "services-systemd"
interface: "python"
requires: []
script: "main.py"

@ -46,12 +46,6 @@ TrackingInstallJob::prettyName() const
return tr( "Installation feedback" );
}
QString
TrackingInstallJob::prettyDescription() const
{
return prettyName();
}
QString
TrackingInstallJob::prettyStatusMessage() const
{
@ -86,12 +80,6 @@ TrackingMachineUpdateManagerJob::prettyName() const
return tr( "Machine feedback" );
}
QString
TrackingMachineUpdateManagerJob::prettyDescription() const
{
return prettyName();
}
QString
TrackingMachineUpdateManagerJob::prettyStatusMessage() const
{
@ -143,12 +131,6 @@ TrackingKUserFeedbackJob::prettyName() const
return tr( "KDE user feedback" );
}
QString
TrackingKUserFeedbackJob::prettyDescription() const
{
return prettyName();
}
QString
TrackingKUserFeedbackJob::prettyStatusMessage() const
{

@ -54,7 +54,6 @@ public:
~TrackingInstallJob() override;
QString prettyName() const override;
QString prettyDescription() const override;
QString prettyStatusMessage() const override;
Calamares::JobResult exec() override;
@ -75,7 +74,6 @@ public:
~TrackingMachineUpdateManagerJob() override;
QString prettyName() const override;
QString prettyDescription() const override;
QString prettyStatusMessage() const override;
Calamares::JobResult exec() override;
};
@ -93,7 +91,6 @@ public:
~TrackingKUserFeedbackJob() override;
QString prettyName() const override;
QString prettyDescription() const override;
QString prettyStatusMessage() const override;
Calamares::JobResult exec() override;

@ -4,5 +4,4 @@ type: "job"
name: "unpackfs"
interface: "python"
script: "main.py"
requiredModules:
- mount
requiredModules: [ mount ]

Loading…
Cancel
Save