Merge branch 'development' into 3.2.x-stable

This commit is contained in:
Philip Müller 2019-03-12 08:59:02 +01:00
commit 693ea79704
89 changed files with 1558 additions and 879 deletions

31
CHANGES
View File

@ -3,6 +3,36 @@ contributors are listed. Note that Calamares does not have a historical
changelog -- this log starts with version 3.2.0. The release notes on the changelog -- this log starts with version 3.2.0. The release notes on the
website will have to do for older versions. website will have to do for older versions.
# 3.2.5 (unreleased) #
This release contains contributions from (alphabetically by first name):
- Arnaud Ferraris
- Dan Simmons
- Gabriel Craciunescu
## Core ##
* View modules (in C++) can now perform their own requirements-checking
to see if installation makes sense. This expands upon the existing
requirements checks in the welcome module (RAM, disk space, ..).
The checks have been made asynchronous, so that responsiveness during
requirements-checking is improved and the user has better feedback.
## Modules ##
* *Bootloader* module: a serious bug introduced in 3.2.4 which prevents
succesful boot after installation on EFI machines, has been repaired.
(Thanks to Gabriel)
* *Partition* module: it is now possible to build without libparted. Since
KPMCore may not need this library anymore, it is a dependency that will
be dropped as soon as it is feasible. Add `-DCMAKE_DISABLE_FIND_PACKAGE_LIBPARTED=ON`
to the CMake flags to do so.
* Python modules: several modules have had translations added. This is
usually only visible when the module runs as part of the *exec* step,
when the module's *pretty name* is displayed. In addition, error
messages are now translated.
# 3.2.4 (2019-02-12) # # 3.2.4 (2019-02-12) #
This release contains contributions from (alphabetically by first name): This release contains contributions from (alphabetically by first name):
@ -66,6 +96,7 @@ This release contains contributions from (alphabetically by first name):
the installation media to the target stystem. This can be used, for instance, the installation media to the target stystem. This can be used, for instance,
for block-level-identical installations. for block-level-identical installations.
# 3.2.3 (2019-01-09) # # 3.2.3 (2019-01-09) #
This release contains contributions from (alphabetically by first name): This release contains contributions from (alphabetically by first name):

View File

@ -75,8 +75,8 @@ set( CALAMARES_DESCRIPTION_SUMMARY
set( CALAMARES_VERSION_MAJOR 3 ) set( CALAMARES_VERSION_MAJOR 3 )
set( CALAMARES_VERSION_MINOR 2 ) set( CALAMARES_VERSION_MINOR 2 )
set( CALAMARES_VERSION_PATCH 4 ) set( CALAMARES_VERSION_PATCH 5 )
set( CALAMARES_VERSION_RC 0 ) set( CALAMARES_VERSION_RC 1 )
### Transifex (languages) info ### Transifex (languages) info
@ -240,7 +240,7 @@ include( CMakeColors )
### DEPENDENCIES ### DEPENDENCIES
# #
find_package( Qt5 ${QT_VERSION} CONFIG REQUIRED Core Gui Widgets LinguistTools Svg Quick QuickWidgets ) find_package( Qt5 ${QT_VERSION} CONFIG REQUIRED Concurrent Core Gui Widgets LinguistTools Svg Quick QuickWidgets )
find_package( YAMLCPP ${YAMLCPP_VERSION} REQUIRED ) find_package( YAMLCPP ${YAMLCPP_VERSION} REQUIRED )
if( INSTALL_POLKIT ) if( INSTALL_POLKIT )
find_package( PolkitQt5-1 REQUIRED ) find_package( PolkitQt5-1 REQUIRED )

View File

@ -348,6 +348,7 @@ void
CalamaresApplication::initViewSteps() CalamaresApplication::initViewSteps()
{ {
cDebug() << "STARTUP: loadModules for all modules done"; cDebug() << "STARTUP: loadModules for all modules done";
m_moduleManager->checkRequirements();
if ( Calamares::Branding::instance()->windowMaximize() ) if ( Calamares::Branding::instance()->windowMaximize() )
{ {
m_mainwindow->setWindowFlag( Qt::FramelessWindowHint ); m_mainwindow->setWindowFlag( Qt::FramelessWindowHint );
@ -355,6 +356,7 @@ CalamaresApplication::initViewSteps()
} }
else else
m_mainwindow->show(); m_mainwindow->show();
ProgressTreeModel* m = new ProgressTreeModel( nullptr ); ProgressTreeModel* m = new ProgressTreeModel( nullptr );
ProgressTreeView::instance()->setModel( m ); ProgressTreeView::instance()->setModel( m );
cDebug() << "STARTUP: Window now visible and ProgressTreeView populated"; cDebug() << "STARTUP: Window now visible and ProgressTreeView populated";

View File

@ -2,7 +2,7 @@
* *
* Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org> * Copyright 2010-2011, Christian Muehlhaeuser <muesli@tomahawk-player.org>
* Copyright 2014, Teo Mrnjavac <teo@kde.org> * Copyright 2014, Teo Mrnjavac <teo@kde.org>
* Copyright 2017-2018, Adriaan de Groot <groot@kde.org> * Copyright 2017-2019, Adriaan de Groot <groot@kde.org>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -68,9 +68,9 @@ logLevel()
} }
static void static void
log( const char* msg, unsigned int debugLevel, bool toDisk = true ) log( const char* msg, unsigned int debugLevel )
{ {
if ( toDisk || debugLevel < s_threshold ) if ( true )
{ {
QMutexLocker lock( &s_mutex ); QMutexLocker lock( &s_mutex );

View File

@ -51,7 +51,7 @@ struct NamedEnumTable
* *
* static const NamedEnumTable<Colors> c{ {"red", Colors::Red } }; * static const NamedEnumTable<Colors> c{ {"red", Colors::Red } };
*/ */
NamedEnumTable( const std::initializer_list< pair_t >& v ) : table( v ) { /* static_assert( v.size() > 0 ); */ }; NamedEnumTable( const std::initializer_list< pair_t >& v ) : table( v ) { /* static_assert( v.size() > 0 ); */ }
/** @brief Find a name @p s in the table. /** @brief Find a name @p s in the table.
* *

View File

@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* Copyright 2017, Adriaan de Groot <groot@kde.org> * Copyright 2017, Adriaan de Groot <groot@kde.org>
* Copyright 2019, Collabora Ltd <arnaud.ferraris@collabora.com>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -24,10 +25,16 @@
namespace CalamaresUtils namespace CalamaresUtils
{ {
/** User defined literals, 1_KiB is 1 KibiByte (= 2^10 bytes) */
constexpr qint64 operator ""_KiB( unsigned long long m )
{
return qint64(m) * 1024;
}
/** User defined literals, 1_MiB is 1 MibiByte (= 2^20 bytes) */ /** User defined literals, 1_MiB is 1 MibiByte (= 2^20 bytes) */
constexpr qint64 operator ""_MiB( unsigned long long m ) constexpr qint64 operator ""_MiB( unsigned long long m )
{ {
return qint64(m) * 1024 * 1024; return operator ""_KiB(m) * 1024;
} }
/** User defined literals, 1_GiB is 1 GibiByte (= 2^30 bytes) */ /** User defined literals, 1_GiB is 1 GibiByte (= 2^30 bytes) */
@ -36,6 +43,11 @@ constexpr qint64 operator ""_GiB( unsigned long long m )
return operator ""_MiB(m) * 1024; return operator ""_MiB(m) * 1024;
} }
constexpr qint64 KiBtoBytes( unsigned long long m )
{
return operator ""_KiB( m );
}
constexpr qint64 MiBtoBytes( unsigned long long m ) constexpr qint64 MiBtoBytes( unsigned long long m )
{ {
return operator ""_MiB( m ); return operator ""_MiB( m );
@ -46,7 +58,12 @@ constexpr qint64 GiBtoBytes( unsigned long long m )
return operator ""_GiB( m ); return operator ""_GiB( m );
} }
constexpr qint64 MiBToBytes( double m ) constexpr qint64 KiBtoBytes( double m )
{
return qint64(m * 1024);
}
constexpr qint64 MiBtoBytes( double m )
{ {
return qint64(m * 1024 * 1024); return qint64(m * 1024 * 1024);
} }

View File

@ -5,6 +5,8 @@ set( calamaresui_SOURCES
modulesystem/Module.cpp modulesystem/Module.cpp
modulesystem/ModuleManager.cpp modulesystem/ModuleManager.cpp
modulesystem/ProcessJobModule.cpp modulesystem/ProcessJobModule.cpp
modulesystem/Requirement.cpp
modulesystem/RequirementsChecker.cpp
modulesystem/ViewModule.cpp modulesystem/ViewModule.cpp
utils/CalamaresUtilsGui.cpp utils/CalamaresUtilsGui.cpp

View File

@ -281,4 +281,10 @@ Module::initFrom( const QVariantMap& moduleDescriptor )
m_maybe_emergency = moduleDescriptor[ EMERGENCY ].toBool(); m_maybe_emergency = moduleDescriptor[ EMERGENCY ].toBool();
} }
RequirementsList
Module::checkRequirements()
{
return RequirementsList();
}
} //ns } //ns

View File

@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org> * Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, Adriaan de Groot <groot@kde.org>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -19,6 +20,7 @@
#ifndef CALAMARES_MODULE_H #ifndef CALAMARES_MODULE_H
#define CALAMARES_MODULE_H #define CALAMARES_MODULE_H
#include "Requirement.h"
#include "UiDllMacro.h" #include "UiDllMacro.h"
#include <Typedefs.h> #include <Typedefs.h>
@ -178,6 +180,11 @@ public:
*/ */
QVariantMap configurationMap(); QVariantMap configurationMap();
/**
* @brief Check the requirements of this module.
*/
virtual RequirementsList checkRequirements();
protected: protected:
explicit Module(); explicit Module();
virtual void initFrom( const QVariantMap& moduleDescriptor ); virtual void initFrom( const QVariantMap& moduleDescriptor );

View File

@ -21,26 +21,23 @@
#include "ExecutionViewStep.h" #include "ExecutionViewStep.h"
#include "Module.h" #include "Module.h"
#include "utils/Logger.h" #include "RequirementsChecker.h"
#include "utils/YamlUtils.h"
#include "Settings.h" #include "Settings.h"
#include "ViewManager.h" #include "ViewManager.h"
#include "utils/Logger.h"
#include "utils/YamlUtils.h"
#include <yaml-cpp/yaml.h> #include <yaml-cpp/yaml.h>
#include <QApplication> #include <QApplication>
#include <QDir> #include <QDir>
#include <QTimer> #include <QTimer>
#define MODULE_CONFIG_FILENAME "module.desc"
namespace Calamares namespace Calamares
{ {
ModuleManager* ModuleManager::s_instance = nullptr; ModuleManager* ModuleManager::s_instance = nullptr;
ModuleManager* ModuleManager*
ModuleManager::instance() ModuleManager::instance()
{ {
@ -94,7 +91,7 @@ ModuleManager::doInit()
bool success = currentDir.cd( subdir ); bool success = currentDir.cd( subdir );
if ( success ) if ( success )
{ {
QFileInfo descriptorFileInfo( currentDir.absoluteFilePath( MODULE_CONFIG_FILENAME ) ); QFileInfo descriptorFileInfo( currentDir.absoluteFilePath( QLatin1Literal( "module.desc") ) );
if ( ! ( descriptorFileInfo.exists() && descriptorFileInfo.isReadable() ) ) if ( ! ( descriptorFileInfo.exists() && descriptorFileInfo.isReadable() ) )
{ {
cDebug() << Q_FUNC_INFO << "unreadable file: " cDebug() << Q_FUNC_INFO << "unreadable file: "
@ -307,6 +304,26 @@ ModuleManager::loadModules()
} ); } );
} }
void
ModuleManager::checkRequirements()
{
cDebug() << "Checking module requirements ..";
QVector< Module* > modules( m_loadedModulesByInstanceKey.count() );
int count = 0;
for (const auto& module : m_loadedModulesByInstanceKey )
{
modules[count++] = module;
}
RequirementsChecker *rq = new RequirementsChecker( modules, this );
connect( rq, &RequirementsChecker::requirementsResult, this, &ModuleManager::requirementsResult );
connect( rq, &RequirementsChecker::requirementsComplete, this, &ModuleManager::requirementsComplete );
connect( rq, &RequirementsChecker::requirementsProgress, this, &ModuleManager::requirementsProgress );
connect( rq, &RequirementsChecker::done, rq, &RequirementsChecker::deleteLater );
QTimer::singleShot( 0, rq, &RequirementsChecker::run );
}
QStringList QStringList
ModuleManager::checkDependencies() ModuleManager::checkDependencies()
@ -334,8 +351,6 @@ ModuleManager::checkDependencies()
break; break;
} }
} }
if ( somethingWasRemovedBecauseOfUnmetDependencies )
break;
} }
if ( !somethingWasRemovedBecauseOfUnmetDependencies ) if ( !somethingWasRemovedBecauseOfUnmetDependencies )
break; break;
@ -369,4 +384,4 @@ ModuleManager::checkDependencies( const Module& m )
return allRequirementsFound; return allRequirementsFound;
} }
} } // namespace

View File

@ -20,6 +20,7 @@
#ifndef MODULELOADER_H #ifndef MODULELOADER_H
#define MODULELOADER_H #define MODULELOADER_H
#include "Requirement.h"
#include "Typedefs.h" #include "Typedefs.h"
#include <QObject> #include <QObject>
@ -30,6 +31,7 @@ namespace Calamares
{ {
class Module; class Module;
struct RequirementEntry; // from Requirement.h
/** /**
* @brief The ModuleManager class is a singleton which manages Calamares modules. * @brief The ModuleManager class is a singleton which manages Calamares modules.
@ -81,10 +83,20 @@ public:
*/ */
void loadModules(); void loadModules();
/**
* @brief Starts asynchronous requirements checking for each module.
* When this is done, the signal modulesChecked is emitted.
*/
void checkRequirements();
signals: signals:
void initDone(); void initDone();
void modulesLoaded(); /// All of the modules were loaded successfully void modulesLoaded(); /// All of the modules were loaded successfully
void modulesFailed( QStringList ); /// .. or not void modulesFailed( QStringList ); /// .. or not
// Below, see RequirementsChecker documentation
void requirementsComplete( bool );
void requirementsResult( RequirementsList );
void requirementsProgress( const QString& );
private slots: private slots:
void doInit(); void doInit();

View File

@ -1,6 +1,5 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <http://github.com/calamares> ===
* *
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, Adriaan de Groot <groot@kde.org> * Copyright 2017, Adriaan de Groot <groot@kde.org>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
@ -16,23 +15,5 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with Calamares. If not, see <http://www.gnu.org/licenses/>. * along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "Requirement.h"
#ifndef CHECKITEMWIDGET_H
#define CHECKITEMWIDGET_H
#include <QLabel>
class CheckItemWidget : public QWidget
{
Q_OBJECT
public:
explicit CheckItemWidget( bool checked, bool required,
QWidget* parent = nullptr );
void setText( const QString& text );
private:
QLabel* m_textLabel;
QLabel* m_iconLabel;
};
#endif // CHECKITEMWIDGET_H

View File

@ -0,0 +1,67 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 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/>.
*/
#ifndef CALAMARES_REQUIREMENT_H
#define CALAMARES_REQUIREMENT_H
#include <QList>
#include <QMetaType>
#include <QString>
#include <functional>
namespace Calamares
{
/**
* An indication of a requirement, which is checked in preparation
* for system installation. An entry has a name and some explanation functions
* (functions, because they need to respond to translations).
*
* A requirement can be *satisfied* or not.
* A requirement can be optional (i.e. a "good to have") or mandatory.
*
* Requirements which are not satisfied, and also mandatory, will prevent the
* installation from proceeding.
*/
struct RequirementEntry
{
using TextFunction = std::function< QString() >;
/// @brief name of this requirement; not shown to user and used as ID
QString name;
/// @brief Detailed description of this requirement, for use in user-visible lists
TextFunction enumerationText;
/// @brief User-visible string to show that the requirement is not met, short form
TextFunction negatedText;
bool satisfied;
bool mandatory;
/// @brief Convenience to check if this entry should be shown in details dialog
bool hasDetails() const { return !enumerationText().isEmpty(); }
};
using RequirementsList = QList< RequirementEntry >;
} // namespace Calamares
Q_DECLARE_METATYPE(Calamares::RequirementEntry)
#endif

View File

@ -0,0 +1,154 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2019, 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/>.
*/
#include "RequirementsChecker.h"
#include "Module.h"
#include "Requirement.h"
#include "utils/Logger.h"
#include <algorithm>
#include <QtConcurrent/QtConcurrent>
#include <QFuture>
#include <QFutureWatcher>
#include <QTimer>
namespace Calamares
{
static void
registerMetatypes()
{
static bool done = false;
if ( !done )
{
qRegisterMetaType< RequirementEntry >( "RequirementEntry" );
// It's sensitive to the names of types in parameters; in particular
// althrough QList<RequirementEntry> is the same as RequirementsList,
// because we *name* the type as RequirementsList in the parameters,
// we need to register that (as well). Here, be safe and register
// both names.
qRegisterMetaType< QList< RequirementEntry > >( "QList<RequirementEntry>" );
qRegisterMetaType< RequirementsList >( "RequirementsList" );
done = true;
}
}
static void
check( Module * const &m, RequirementsChecker *c )
{
RequirementsList l = m->checkRequirements();
if ( l.count() > 0 )
c->addCheckedRequirements( l );
c->requirementsProgress( QObject::tr( "Requirements checking for module <i>%1</i> is complete." ).arg( m->name() ) );
}
RequirementsChecker::RequirementsChecker( QVector< Module* > modules, QObject* parent )
: QObject( parent )
, m_modules( std::move( modules ) )
, m_progressTimer( nullptr )
, m_progressTimeouts( 0 )
{
m_watchers.reserve( m_modules.count() );
m_collectedRequirements.reserve( m_modules.count() );
registerMetatypes();
}
RequirementsChecker::~RequirementsChecker()
{
}
void
RequirementsChecker::run()
{
m_progressTimer = new QTimer( this );
connect( m_progressTimer, &QTimer::timeout, this, &RequirementsChecker::reportProgress );
m_progressTimer->start( 1200 ); // msec
for (const auto& module : m_modules )
{
Watcher *watcher = new Watcher( this );
watcher->setFuture( QtConcurrent::run( check, module, this ) );
m_watchers.append( watcher );
connect( watcher, &Watcher::finished, this, &RequirementsChecker::finished );
}
QTimer::singleShot( 0, this, &RequirementsChecker::finished );
}
void
RequirementsChecker::finished()
{
if ( std::all_of( m_watchers.cbegin(), m_watchers.cend(), []( const Watcher *w ) { return w && w->isFinished(); } ) )
{
cDebug() << "All requirements have been checked.";
if ( m_progressTimer )
m_progressTimer->stop();
bool acceptable = true;
int count = 0;
for ( const auto& r : m_collectedRequirements )
{
if ( r.mandatory && !r.satisfied )
{
cDebug() << " .. requirement" << count << r.name << "is not satisfied.";
acceptable = false;
}
++count;
}
emit requirementsComplete( acceptable );
QTimer::singleShot(0, this, &RequirementsChecker::done );
}
}
void
RequirementsChecker::addCheckedRequirements( RequirementsList l )
{
static QMutex addMutex;
{
QMutexLocker lock( &addMutex );
m_collectedRequirements.append( l );
}
cDebug() << "Added" << l.count() << "requirement results";
emit requirementsResult( l );
}
void
RequirementsChecker::reportProgress()
{
m_progressTimeouts++;
auto remaining = std::count_if( m_watchers.cbegin(), m_watchers.cend(), []( const Watcher *w ) { return w && !w->isFinished(); } );
if ( remaining > 0 )
{
QString waiting = tr( "Waiting for %n module(s).", "", remaining );
QString elapsed = tr( "(%n second(s))", "", m_progressTimeouts * m_progressTimer->interval() / 1000 );
emit requirementsProgress( waiting + QString( " " ) + elapsed );
}
else
emit requirementsProgress( tr( "System-requirements checking is complete." ) );
}
}

View File

@ -0,0 +1,87 @@
/* === This file is part of Calamares - <http://github.com/calamares> ===
*
* Copyright 2019, 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/>.
*/
#ifndef CALAMARES_REQUIREMENTSCHECKER_H
#define CALAMARES_REQUIREMENTSCHECKER_H
#include "Requirement.h"
#include <QFutureWatcher>
#include <QObject>
#include <QTimer>
#include <QVector>
namespace Calamares
{
class Module;
/** @brief A manager-class that checks all the module requirements
*
* Asynchronously checks the requirements for each module, and
* emits progress signals as appropriate.
*/
class RequirementsChecker : public QObject
{
Q_OBJECT
public:
RequirementsChecker( QVector< Module* > modules, QObject* parent = nullptr );
virtual ~RequirementsChecker() override;
public slots:
/// @brief Start checking all the requirements
void run();
/// @brief Called when requirements are reported by a module
void addCheckedRequirements( RequirementsList );
/// @brief Called when all requirements have been checked
void finished();
/// @brief Called periodically while requirements are being checked
void reportProgress();
signals:
/// @brief Human-readable progress message
void requirementsProgress( const QString& );
/// @brief Requirements from a single module
void requirementsResult( RequirementsList );
/** @brief When all requirements are collected
*
* The argument indicates if all mandatory requirements are satisfied.
*/
void requirementsComplete( bool );
/// @brief Emitted after requirementsComplete
void done();
private:
QVector< Module* > m_modules;
using Watcher = QFutureWatcher< void >;
QVector< Watcher* > m_watchers;
RequirementsList m_collectedRequirements;
QTimer *m_progressTimer;
unsigned m_progressTimeouts;
} ;
}
#endif

View File

@ -132,4 +132,10 @@ ViewModule::~ViewModule()
delete m_loader; delete m_loader;
} }
RequirementsList
ViewModule::checkRequirements()
{
return m_viewStep->checkRequirements();
}
} // namespace Calamares } // namespace Calamares

View File

@ -39,6 +39,8 @@ public:
void loadSelf() override; void loadSelf() override;
JobList jobs() const override; JobList jobs() const override;
RequirementsList checkRequirements() override;
protected: protected:
void initFrom( const QVariantMap& moduleDescriptor ) override; void initFrom( const QVariantMap& moduleDescriptor ) override;

View File

@ -52,6 +52,14 @@ void
ViewStep::onLeave() ViewStep::onLeave()
{} {}
void
ViewStep::next()
{}
void
ViewStep::back()
{}
void void
ViewStep::setModuleInstanceKey( const QString& instanceKey ) ViewStep::setModuleInstanceKey( const QString& instanceKey )
@ -66,4 +74,10 @@ ViewStep::setConfigurationMap( const QVariantMap& configurationMap )
Q_UNUSED( configurationMap ); Q_UNUSED( configurationMap );
} }
RequirementsList ViewStep::checkRequirements()
{
return RequirementsList();
}
} }

View File

@ -22,6 +22,7 @@
#include <QObject> #include <QObject>
#include "modulesystem/Requirement.h"
#include "../UiDllMacro.h" #include "../UiDllMacro.h"
#include "Typedefs.h" #include "Typedefs.h"
@ -68,13 +69,39 @@ public:
//TODO: we might want to make this a QSharedPointer //TODO: we might want to make this a QSharedPointer
virtual QWidget* widget() = 0; virtual QWidget* widget() = 0;
virtual void next() = 0; /**
virtual void back() = 0; * @brief Multi-page support, go next
*
* Multi-page view steps need to manage the content visible in the widget
* themselves. This method is called when the user clicks the *next*
* button, and should switch to the next of the multiple-pages. It needs
* to be consistent with both isNextEnabled() and isAtEnd().
*
* In particular: when isAtEnd() returns false, next() is called when
* the user clicks the button and a new page should be shown by this
* view step. When isAtEnd() returns true, clicking the button will
* switch to the next view step in sequence, rather than a next page
* in the current view step.
*/
virtual void next();
/// @brief Multi-page support, go back
virtual void back();
/// @brief Can the user click *next* with currently-filled-in data?
virtual bool isNextEnabled() const = 0; virtual bool isNextEnabled() const = 0;
/// @brief Can the user click *previous* with currently-filled-in data?
virtual bool isBackEnabled() const = 0; virtual bool isBackEnabled() const = 0;
/**
* @brief Multi-page support, switch to previous view step?
*
* For a multi-page view step, this indicates that the first (beginning)
* page is showing. Clicking *previous* when at the beginning of a view
* step, switches to the previous step, not the previous page of the
* current view step.
*/
virtual bool isAtBeginning() const = 0; virtual bool isAtBeginning() const = 0;
/// @brief Multi-page support, switch to next view step?
virtual bool isAtEnd() const = 0; virtual bool isAtEnd() const = 0;
/** /**
@ -91,6 +118,12 @@ public:
*/ */
virtual void onLeave(); virtual void onLeave();
/**
* @brief Jobs needed to run this viewstep
*
* When a ViewStep is listed in the exec section, its jobs are executed instead.
* This function returns that list of jobs; an empty list is ok.
*/
virtual JobList jobs() const = 0; virtual JobList jobs() const = 0;
void setModuleInstanceKey( const QString& instanceKey ); void setModuleInstanceKey( const QString& instanceKey );
@ -101,9 +134,18 @@ public:
virtual void setConfigurationMap( const QVariantMap& configurationMap ); virtual void setConfigurationMap( const QVariantMap& configurationMap );
/**
* @brief Can this module proceed, on this machine?
*
* This is called asynchronously at startup, and returns a list of
* the requirements that the module has checked, and their status.
* See Calamares::RequirementEntry for details.
*/
virtual RequirementsList checkRequirements();
signals: signals:
/// @brief Tells the viewmanager to enable the *next* button according to @p status
void nextStatusChanged( bool status ); void nextStatusChanged( bool status );
void done();
/* Emitted when the viewstep thinks it needs more space than is currently /* Emitted when the viewstep thinks it needs more space than is currently
* available for display. @p enlarge is the requested additional space, * available for display. @p enlarge is the requested additional space,

View File

@ -8,27 +8,28 @@ Each Calamares module lives in its own directory.
All modules are installed in `$DESTDIR/lib/calamares/modules`. All modules are installed in `$DESTDIR/lib/calamares/modules`.
# Module types There are two **types** of Calamares module:
There are two types of Calamares module:
* viewmodule, for user-visible modules. These may be in C++, or PythonQt. * viewmodule, for user-visible modules. These may be in C++, or PythonQt.
* jobmodule, for not-user-visible modules. These may be done in C++, * jobmodule, for not-user-visible modules. These may be done in C++,
Python, or as external processes. Python, or as external processes.
# Module interfaces A viewmodule exposes a UI to the user. The PythonQt-based modules
are considered experimental (and as of march 2019 may be on the
way out again as never-used-much and PythonQt is not packaged
on Debian anymore).
There are three (four) interfaces for Calamares modules: There are three (four) **interfaces** for Calamares modules:
* qtplugin, * qtplugin (viewmodules, jobmodules),
* python (jobmodules only), * python (jobmodules only),
* pythonqt (optional), * pythonqt (viewmodules, jobmodules, optional),
* process (jobmodules only). * process (jobmodules only).
# Module directory ## Module directory
Each Calamares module lives in its own directory. The contents Each Calamares module lives in its own directory. The contents
of the directory depend on the interface and type of the module. of the directory depend on the interface and type of the module.
## Module descriptor ### Module descriptor
A Calamares module must have a *module descriptor file*, named A Calamares module must have a *module descriptor file*, named
`module.desc`. For C++ (qtplugin) modules using CMake as a build- `module.desc`. For C++ (qtplugin) modules using CMake as a build-
@ -49,25 +50,66 @@ Module descriptors **must** have the following keys:
- *interface* (see below for the different interfaces; generally we - *interface* (see below for the different interfaces; generally we
refer to the kinds of modules by their interface) refer to the kinds of modules by their interface)
Module descriptors for Python and PythonQt modules **must** have the following key:
- *script* (the name of the Python script to load, nearly always `main.py`)
Module descriptors **may** have the following keys: Module descriptors **may** have the following keys:
- *required* **unimplemented** (a list of modules which are required for this module - *requiredModules* (a list of modules which are required for this module
to operate properly) to operate properly)
- *emergency* (a boolean value, set to true to mark the module - *emergency* (a boolean value, set to true to mark the module
as an emergency module) as an emergency module)
## Module-specific configuration ### Required Modules
A module may list zero (if it has no requirements) or more modules
by name. As modules are loaded from the global sequence in `settings.conf`,
each module is checked that all of the modules it requires are
already loaded before it. This ensures that if a module needs
another one to fill in globalstorage keys, that happens before
it needs those keys.
### Emergency Modules
Only C++ modules and job modules may be emergency modules. If, during an
*exec* step in the sequence, a module fails, installation as a whole fails
and the install is aborted. If there are emergency modules in the **same**
exec block, those will be executed before the installation is aborted.
Non-emergency modules are not executed.
If an emergency-module fails while processing emergency-modules for
another failed module, that failure is ignored and emergency-module
processing continues.
Use the EMERGENCY keyword in the CMake description of a C++ module
to generate a suitable `module.desc`.
A module that is marked as an emergency module in its module.desc
must **also** set the *emergency* key to *true* in its configuration file
(see below). If it does not, the module is not considered to be an emergency
module after all (this is so that you can have modules that have several
instances, only some of which are actually needed for emergencies).
### Module-specific configuration
A Calamares module **may** read a module configuration file, A Calamares module **may** read a module configuration file,
named `<modulename>.conf`. If such a file is present in the named `<modulename>.conf`. If such a file is present in the
module's directory, it is shipped as a *default* configuration file. module's directory, it can be shipped as a *default* configuration file.
This only happens if the CMake-time option `INSTALL_CONFIG` is on.
The sample configuration files may work and may be suitable for
your distribution, but no guarantee is given about their stability
beyond syntactic correctness.
The module configuration file, if it exists, is a YAML 1.2 document The module configuration file, if it exists, is a YAML 1.2 document
which contains a YAML map of anything. which contains a YAML map of anything.
All default module configuration files are installed in All sample module configuration files are installed in
`$DESTDIR/share/calamares/modules` but can be overridden by `$DESTDIR/share/calamares/modules` but can be overridden by
files with the same name placed manually (or by the packager) files with the same name placed manually (or by the packager)
in `/etc/calamares/modules`. in `/etc/calamares/modules`.
## C++ modules ## C++ modules
Currently the recommended way to write a module which exposes one or more Currently the recommended way to write a module which exposes one or more
@ -79,6 +121,8 @@ To add a Qt plugin module, put it in a subdirectory and make sure it has
a `CMakeLists.txt` with a `calamares_add_plugin` call. It will be picked a `CMakeLists.txt` with a `calamares_add_plugin` call. It will be picked
up automatically by our CMake magic. The `module.desc` file is optional. up automatically by our CMake magic. The `module.desc` file is optional.
## Python modules ## Python modules
Modules may use one of the python interfaces, which may be present Modules may use one of the python interfaces, which may be present
@ -90,7 +134,7 @@ or the experimental pythonqt job- and viewmodule interfaces.
To add a Python or process jobmodule, put it in a subdirectory and make sure To add a Python or process jobmodule, put it in a subdirectory and make sure
it has a `module.desc`. It will be picked up automatically by our CMake magic. it has a `module.desc`. It will be picked up automatically by our CMake magic.
For all kinds of Python jobs, the key *script* must be set to the name of For all kinds of Python jobs, the key *script* must be set to the name of
the main python file for the job. This is almost universally "main.py". the main python file for the job. This is almost universally `main.py`.
`CMakeLists.txt` is *not* used for Python and process jobmodules. `CMakeLists.txt` is *not* used for Python and process jobmodules.
@ -113,6 +157,17 @@ function `run()` as entry point. The function `run()` must return `None` if
everything went well, or a tuple `(str,str)` with an error message and everything went well, or a tuple `(str,str)` with an error message and
description if something went wrong. description if something went wrong.
### Python API
**TODO:** this needs documentation
## PythonQt modules
The PythonQt modules are considered experimental and may be removed again
due to low uptake. Their documentation is also almost completely lacking.
### PythonQt Jobmodule ### PythonQt Jobmodule
A PythonQt jobmodule implements the experimental Job interface by defining A PythonQt jobmodule implements the experimental Job interface by defining
@ -123,31 +178,18 @@ a subclass of something.
A PythonQt viewmodule implements the experimental View interface by defining A PythonQt viewmodule implements the experimental View interface by defining
a subclass of something. a subclass of something.
### Python API
**TODO:** this needs documentation
## Process jobmodules ## Process jobmodules
A process jobmodule runs a (single) command. The interface is "process", A process jobmodule runs a (single) command. The interface is *process*,
while the module type must be "job" or "jobmodule". while the module type must be *job* or *jobmodule*.
The key *command* should have a string as value, which is passed to the The module-descriptor key *command* should have a string as value, which is
shell -- remember to quote it properly. passed to the shell -- remember to quote it properly. It is generally
recommended to use a *shellprocess* job module instead (less configuration,
## Emergency Modules easier to have multiple instances).
Only C++ modules and job modules may be emergency modules. If, during an
*exec* step in the sequence, a module fails, installation as a whole fails
and the install is aborted. If there are emergency modules in the **same**
exec block, those will be executed before the installation is aborted.
Non-emergency modules are not executed.
If an emergency-module fails while processing emergency-modules for
another failed module, that failure is ignored and emergency-module
processing continues.
Use the EMERGENCY keyword in the CMake description of a C++ module
to generate a suitable `module.desc`.
A module that is marked as an emergency module in its module.desc
must **also** set the *emergency* key to *true* in its configuration file.
If it does not, the module is not considered to be an emergency module
after all (this is so that you can have modules that have several
instances, only some of which are actually needed for emergencies.

View File

@ -1,5 +1,8 @@
--- ---
type: "job" type: "job"
name: "bootloader"
interface: "python" interface: "python"
name: "bootloader"
script: "main.py" script: "main.py"
# The partition module sets up the EFI firmware type
# global key, which is used to decide how to install.
requiredModules: [ "partition" ]

View File

@ -768,11 +768,11 @@ def run():
displaymanagers.remove(dm) displaymanagers.remove(dm)
if not dm_impl: if not dm_impl:
return ( libcalamares.utils.warning(
_("No display managers selected for the displaymanager module."), "No display managers selected for the displaymanager module. "
_("The list is empty after checking for installed display managers.") "The list is empty after checking for installed display managers."
) )
return None
# Pick up remaining settings # Pick up remaining settings
if "defaultDesktopEnvironment" in libcalamares.job.configuration: if "defaultDesktopEnvironment" in libcalamares.job.configuration:

View File

@ -67,18 +67,6 @@ FinishedViewStep::widget()
} }
void
FinishedViewStep::next()
{
emit done();
}
void
FinishedViewStep::back()
{}
bool bool
FinishedViewStep::isNextEnabled() const FinishedViewStep::isNextEnabled() const
{ {

View File

@ -40,9 +40,6 @@ public:
QWidget* widget() override; QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override; bool isNextEnabled() const override;
bool isBackEnabled() const override; bool isBackEnabled() const override;

View File

@ -80,7 +80,7 @@ ResizeFSJob::RelativeSize::apply( qint64 totalSectors, qint64 sectorSize )
case unit_t::None: case unit_t::None:
return -1; return -1;
case unit_t::Absolute: case unit_t::Absolute:
return CalamaresUtils::MiBtoBytes( value() ) / sectorSize; return CalamaresUtils::MiBtoBytes( static_cast<unsigned long long>( value() ) ) / sectorSize;
case unit_t::Percent: case unit_t::Percent:
if ( value() == 100 ) if ( value() == 100 )
return totalSectors; // Common-case, avoid futzing around return totalSectors; // Common-case, avoid futzing around

View File

@ -62,12 +62,6 @@ def modify_grub_default(partitions, root_mount_point, distributor):
cryptdevice_params = [] cryptdevice_params = []
# GRUB needs to decrypt the partition that /boot is on, which may be / or /boot
boot_mountpoint = "/"
for partition in partitions:
if partition["mountPoint"] == "/boot":
boot_mountpoint = "/boot"
if have_dracut: if have_dracut:
for partition in partitions: for partition in partitions:
has_luks = "luksMapperName" in partition has_luks = "luksMapperName" in partition
@ -78,7 +72,7 @@ def modify_grub_default(partitions, root_mount_point, distributor):
swap_outer_uuid = partition["luksUuid"] swap_outer_uuid = partition["luksUuid"]
swap_outer_mappername = partition["luksMapperName"] swap_outer_mappername = partition["luksMapperName"]
if (partition["mountPoint"] == boot_mountpoint and has_luks): if (partition["mountPoint"] == "/" and has_luks):
cryptdevice_params = [ cryptdevice_params = [
"rd.luks.uuid={!s}".format(partition["luksUuid"]) "rd.luks.uuid={!s}".format(partition["luksUuid"])
] ]
@ -88,7 +82,7 @@ def modify_grub_default(partitions, root_mount_point, distributor):
if partition["fs"] == "linuxswap" and not has_luks: if partition["fs"] == "linuxswap" and not has_luks:
swap_uuid = partition["uuid"] swap_uuid = partition["uuid"]
if (partition["mountPoint"] == boot_mountpoint and has_luks): if (partition["mountPoint"] == "/" and has_luks):
cryptdevice_params = [ cryptdevice_params = [
"cryptdevice=UUID={!s}:{!s}".format( "cryptdevice=UUID={!s}:{!s}".format(
partition["luksUuid"], partition["luksMapperName"] partition["luksUuid"], partition["luksMapperName"]

View File

@ -53,18 +53,6 @@ InteractiveTerminalViewStep::widget()
} }
void
InteractiveTerminalViewStep::next()
{
emit done();
}
void
InteractiveTerminalViewStep::back()
{}
bool bool
InteractiveTerminalViewStep::isNextEnabled() const InteractiveTerminalViewStep::isNextEnabled() const
{ {

View File

@ -41,9 +41,6 @@ public:
QWidget* widget() override; QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override; bool isNextEnabled() const override;
bool isBackEnabled() const override; bool isBackEnabled() const override;

View File

@ -65,19 +65,6 @@ KeyboardViewStep::widget()
} }
void
KeyboardViewStep::next()
{
//TODO: actually save those settings somewhere
emit done();
}
void
KeyboardViewStep::back()
{}
bool bool
KeyboardViewStep::isNextEnabled() const KeyboardViewStep::isNextEnabled() const
{ {

View File

@ -42,9 +42,6 @@ public:
QWidget* widget() override; QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override; bool isNextEnabled() const override;
bool isBackEnabled() const override; bool isBackEnabled() const override;

View File

@ -59,18 +59,6 @@ LicenseViewStep::widget()
} }
void
LicenseViewStep::next()
{
emit done();
}
void
LicenseViewStep::back()
{}
bool bool
LicenseViewStep::isNextEnabled() const LicenseViewStep::isNextEnabled() const
{ {

View File

@ -42,9 +42,6 @@ public:
QWidget* widget() override; QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override; bool isNextEnabled() const override;
bool isBackEnabled() const override; bool isBackEnabled() const override;

View File

@ -31,7 +31,7 @@ public:
explicit LocaleConfiguration(); explicit LocaleConfiguration();
/// @brief Create a locale with everything set to the given @p localeName /// @brief Create a locale with everything set to the given @p localeName
explicit LocaleConfiguration( const QString& localeName /* "en_US.UTF-8" */ ) explicit LocaleConfiguration( const QString& localeName /* "en_US.UTF-8" */ )
: LocaleConfiguration( localeName, localeName ) { }; : LocaleConfiguration( localeName, localeName ) { }
/// @brief Create a locale with language and formats separate /// @brief Create a locale with language and formats separate
explicit LocaleConfiguration( const QString& localeName, const QString& formatsName ); explicit LocaleConfiguration( const QString& localeName, const QString& formatsName );

View File

@ -190,18 +190,6 @@ LocaleViewStep::widget()
} }
void
LocaleViewStep::next()
{
emit done();
}
void
LocaleViewStep::back()
{}
bool bool
LocaleViewStep::isNextEnabled() const LocaleViewStep::isNextEnabled() const
{ {

View File

@ -45,9 +45,6 @@ public:
QWidget* widget() override; QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override; bool isNextEnabled() const override;
bool isBackEnabled() const override; bool isBackEnabled() const override;

View File

@ -6,6 +6,7 @@
# Copyright 2014, Kevin Kofler <kevin.kofler@chello.at> # Copyright 2014, Kevin Kofler <kevin.kofler@chello.at>
# Copyright 2016, Philip Müller <philm@manjaro.org> # Copyright 2016, Philip Müller <philm@manjaro.org>
# Copyright 2017, Alf Gaida <agaida@siduction.org> # Copyright 2017, Alf Gaida <agaida@siduction.org>
# Copyright 2019, Adriaan de Groot <groot@kde.org>
# #
# Calamares is free software: you can redistribute it and/or modify # Calamares is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by

View File

@ -69,18 +69,6 @@ NetInstallViewStep::widget()
} }
void
NetInstallViewStep::next()
{
emit done();
}
void
NetInstallViewStep::back()
{}
bool bool
NetInstallViewStep::isNextEnabled() const NetInstallViewStep::isNextEnabled() const
{ {

View File

@ -42,9 +42,6 @@ public:
QWidget* widget() override; QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override; bool isNextEnabled() const override;
bool isBackEnabled() const override; bool isBackEnabled() const override;

View File

@ -2,6 +2,7 @@
* *
* Copyright 2015-2016, Teo Mrnjavac <teo@kde.org> * Copyright 2015-2016, Teo Mrnjavac <teo@kde.org>
* Copyright 2018, Adriaan de Groot <groot@kde.org> * Copyright 2018, Adriaan de Groot <groot@kde.org>
* Copyright 2019, Collabora Ltd <arnaud.ferraris@collabora.com>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -42,6 +43,25 @@
namespace PartUtils namespace PartUtils
{ {
static QString
convenienceName( const Partition* const candidate )
{
if ( !candidate->mountPoint().isEmpty() )
return candidate->mountPoint();
if ( !candidate->partitionPath().isEmpty() )
return candidate->partitionPath();
if ( !candidate->devicePath().isEmpty() )
return candidate->devicePath();
if ( !candidate->deviceNode().isEmpty() )
return candidate->devicePath();
QString p;
QTextStream s( &p );
s << (void *)candidate;
return p;
}
bool bool
canBeReplaced( Partition* candidate ) canBeReplaced( Partition* candidate )
{ {
@ -63,12 +83,12 @@ canBeReplaced( Partition* candidate )
<< QString( "(%1GB)" ).arg( requiredStorageB / 1024 / 1024 / 1024 ); << QString( "(%1GB)" ).arg( requiredStorageB / 1024 / 1024 / 1024 );
cDebug() << "Storage capacity B:" << availableStorageB cDebug() << "Storage capacity B:" << availableStorageB
<< QString( "(%1GB)" ).arg( availableStorageB / 1024 / 1024 / 1024 ) << QString( "(%1GB)" ).arg( availableStorageB / 1024 / 1024 / 1024 )
<< "for" << candidate->partitionPath() << " length:" << candidate->length(); << "for" << convenienceName( candidate ) << " length:" << candidate->length();
if ( ok && if ( ok &&
availableStorageB > requiredStorageB ) availableStorageB > requiredStorageB )
{ {
cDebug() << "Partition" << candidate->partitionPath() << "authorized for replace install."; cDebug() << "Partition" << convenienceName( candidate ) << "authorized for replace install.";
return true; return true;
} }
@ -85,7 +105,7 @@ canBeResized( Partition* candidate )
return false; return false;
} }
cDebug() << "Checking if" << candidate->partitionPath() << "can be resized."; cDebug() << "Checking if" << convenienceName( candidate ) << "can be resized.";
if ( !candidate->fileSystem().supportGrow() || if ( !candidate->fileSystem().supportGrow() ||
!candidate->fileSystem().supportShrink() ) !candidate->fileSystem().supportShrink() )
{ {
@ -139,13 +159,13 @@ canBeResized( Partition* candidate )
<< QString( "(%1GB)" ).arg( advisedStorageGB ); << QString( "(%1GB)" ).arg( advisedStorageGB );
cDebug() << "Available storage B:" << availableStorageB cDebug() << "Available storage B:" << availableStorageB
<< QString( "(%1GB)" ).arg( availableStorageB / 1024 / 1024 / 1024 ) << QString( "(%1GB)" ).arg( availableStorageB / 1024 / 1024 / 1024 )
<< "for" << candidate->partitionPath() << " length:" << candidate->length() << "for" << convenienceName( candidate ) << " length:" << candidate->length()
<< " sectorsUsed:" << candidate->sectorsUsed() << " fsType:" << candidate->fileSystem().name(); << " sectorsUsed:" << candidate->sectorsUsed() << " fsType:" << candidate->fileSystem().name();
if ( ok && if ( ok &&
availableStorageB > advisedStorageB ) availableStorageB > advisedStorageB )
{ {
cDebug() << "Partition" << candidate->partitionPath() << "authorized for resize + autopartition install."; cDebug() << "Partition" << convenienceName( candidate ) << "authorized for resize + autopartition install.";
return true; return true;
} }
@ -381,7 +401,7 @@ isEfiSystem()
bool bool
isEfiBootable( const Partition* candidate ) isEfiBootable( const Partition* candidate )
{ {
cDebug() << "Check EFI bootable" << candidate->partitionPath() << candidate->devicePath(); cDebug() << "Check EFI bootable" << convenienceName( candidate ) << candidate->devicePath();
cDebug() << " .. flags" << candidate->activeFlags(); cDebug() << " .. flags" << candidate->activeFlags();
auto flags = PartitionInfo::flags( candidate ); auto flags = PartitionInfo::flags( candidate );
@ -408,6 +428,149 @@ isEfiBootable( const Partition* candidate )
flags.testFlag( PartitionTable::FlagBoot ); flags.testFlag( PartitionTable::FlagBoot );
} }
QString
findFS( QString fsName, FileSystem::Type* fsType )
{
QStringList fsLanguage { QLatin1Literal( "C" ) }; // Required language list to turn off localization
if ( fsName.isEmpty() )
fsName = QStringLiteral( "ext4" );
FileSystem::Type tmpType = FileSystem::typeForName( fsName, fsLanguage );
if ( tmpType != FileSystem::Unknown )
{
cDebug() << "Found filesystem" << fsName;
if ( fsType )
*fsType = tmpType;
return fsName;
}
// Second pass: try case-insensitive
const auto fstypes = FileSystem::types();
for ( FileSystem::Type t : fstypes )
{
if ( 0 == QString::compare( fsName, FileSystem::nameForType( t, fsLanguage ), Qt::CaseInsensitive ) )
{
QString fsRealName = FileSystem::nameForType( t, fsLanguage );
cDebug() << "Filesystem name" << fsName << "translated to" << fsRealName;
if ( fsType )
*fsType = t;
return fsRealName;
}
}
cDebug() << "Filesystem" << fsName << "not found, using ext4";
fsName = QStringLiteral( "ext4" );
// fsType can be used to check whether fsName was a valid filesystem.
if (fsType)
*fsType = FileSystem::Unknown;
#ifdef DEBUG_FILESYSTEMS
// This bit is for distro's debugging their settings, and shows
// all the strings that KPMCore is matching against for FS type.
{
Logger::CDebug d;
using TR = Logger::DebugRow< int, QString >;
const auto fstypes = FileSystem::types();
d << "Available types (" << fstypes.count() << ')';
for ( FileSystem::Type t : fstypes )
d << TR( static_cast<int>( t ), FileSystem::nameForType( t, fsLanguage ) );
}
#endif
return fsName;
}
static qint64
sizeToBytes( double size, SizeUnit unit, qint64 totalSize )
{
qint64 bytes;
switch ( unit )
{
case SizeUnit::Percent:
bytes = qint64( static_cast<double>( totalSize ) * size / 100.0L );
break;
case SizeUnit::KiB:
bytes = CalamaresUtils::KiBtoBytes(size);
break;
case SizeUnit::MiB:
bytes = CalamaresUtils::MiBtoBytes(size);
break;
case SizeUnit::GiB:
bytes = CalamaresUtils::GiBtoBytes(size);
break;
default:
bytes = size;
break;
}
return bytes;
}
double
parseSizeString( const QString& sizeString, SizeUnit* unit )
{
double value;
bool ok;
QString valueString;
QString unitString;
QRegExp rx( "[KkMmGg%]" );
int pos = rx.indexIn( sizeString );
if (pos > 0)
{
valueString = sizeString.mid( 0, pos );
unitString = sizeString.mid( pos );
}
else
valueString = sizeString;
value = valueString.toDouble( &ok );
if ( !ok )
{
/*
* In case the conversion fails, a size of 100% allows a few cases to pass
* anyway (e.g. when it is the last partition of the layout)
*/
*unit = SizeUnit::Percent;
return 100.0L;
}
if ( unitString.length() > 0 )
{
if ( unitString.at(0) == '%' )
*unit = SizeUnit::Percent;
else if ( unitString.at(0).toUpper() == 'K' )
*unit = SizeUnit::KiB;
else if ( unitString.at(0).toUpper() == 'M' )
*unit = SizeUnit::MiB;
else if ( unitString.at(0).toUpper() == 'G' )
*unit = SizeUnit::GiB;
else
*unit = SizeUnit::Byte;
}
else
{
*unit = SizeUnit::Byte;
}
return value;
}
qint64
parseSizeString( const QString& sizeString, qint64 totalSize )
{
SizeUnit unit;
double value = parseSizeString( sizeString, &unit );
return sizeToBytes( value, unit, totalSize );
}
qint64
sizeToSectors( double size, SizeUnit unit, qint64 totalSectors, qint64 logicalSize )
{
qint64 bytes = sizeToBytes( size, unit, totalSectors * logicalSize );
return bytesToSectors( static_cast<unsigned long long>( bytes ), logicalSize );
}
} // nmamespace PartUtils } // nmamespace PartUtils
/* Implementation of methods for FstabEntry, from OsproberEntry.h */ /* Implementation of methods for FstabEntry, from OsproberEntry.h */

View File

@ -2,6 +2,7 @@
* *
* Copyright 2015-2016, Teo Mrnjavac <teo@kde.org> * Copyright 2015-2016, Teo Mrnjavac <teo@kde.org>
* Copyright 2018, Adriaan de Groot <groot@kde.org> * Copyright 2018, Adriaan de Groot <groot@kde.org>
* Copyright 2019, Collabora Ltd <arnaud.ferraris@collabora.com>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -21,7 +22,12 @@
#define PARTUTILS_H #define PARTUTILS_H
#include "OsproberEntry.h" #include "OsproberEntry.h"
#include "utils/Units.h"
// KPMcore
#include <kpmcore/fs/filesystem.h>
// Qt
#include <QString> #include <QString>
class PartitionCoreModule; class PartitionCoreModule;
@ -29,6 +35,16 @@ class Partition;
namespace PartUtils namespace PartUtils
{ {
using CalamaresUtils::MiBtoBytes;
enum SizeUnit
{
Percent = 0,
Byte,
KiB,
MiB,
GiB
};
/** /**
* @brief canBeReplaced checks whether the given Partition satisfies the criteria * @brief canBeReplaced checks whether the given Partition satisfies the criteria
@ -73,6 +89,56 @@ bool isEfiSystem();
* the partition table layout, this may mean different flags. * the partition table layout, this may mean different flags.
*/ */
bool isEfiBootable( const Partition* candidate ); bool isEfiBootable( const Partition* candidate );
/** @brief translate @p fsName into a recognized name and type
*
* Makes several attempts to translate the string into a
* name that KPMCore will recognize.
* The corresponding filesystem type is stored in @p fsType, and
* its value is FileSystem::Unknown if @p fsName is not recognized.
*/
QString findFS( QString fsName, FileSystem::Type* fsType );
/**
* @brief Parse a partition size string and return its value and unit used.
* @param sizeString the string to parse.
* @param unit pointer to a SizeUnit variable for storing the parsed unit.
* @return the size value, as parsed from the input string.
*/
double parseSizeString( const QString& sizeString, SizeUnit* unit );
/**
* @brief Parse a partition size string and return its value in bytes.
* @param sizeString the string to parse.
* @param totalSize the size of the selected drive (used when the size is expressed in %)
* @return the size value in bytes.
*/
qint64 parseSizeString( const QString& sizeString, qint64 totalSize );
/**
* @brief Convert a partition size to a sectors count.
* @param size the partition size.
* @param unit the partition size unit.
* @param totalSectors the total number of sectors of the selected drive.
* @param logicalSize the sector size, in bytes.
* @return the number of sectors to be used for the given partition size.
*/
qint64 sizeToSectors( double size, SizeUnit unit, qint64 totalSectors, qint64 logicalSize );
constexpr qint64 alignBytesToBlockSize( qint64 bytes, qint64 blocksize )
{
qint64 blocks = bytes / blocksize;
if ( blocks * blocksize != bytes )
++blocks;
return blocks * blocksize;
}
constexpr qint64 bytesToSectors( qint64 bytes, qint64 blocksize )
{
return alignBytesToBlockSize( alignBytesToBlockSize( bytes, blocksize), MiBtoBytes(1ULL) ) / blocksize;
}
} }
#endif // PARTUTILS_H #endif // PARTUTILS_H

View File

@ -2,6 +2,7 @@
* *
* Copyright 2014-2017, Teo Mrnjavac <teo@kde.org> * Copyright 2014-2017, Teo Mrnjavac <teo@kde.org>
* Copyright 2017-2018, Adriaan de Groot <groot@kde.org> * Copyright 2017-2018, Adriaan de Groot <groot@kde.org>
* Copyright 2019, Collabora Ltd <arnaud.ferraris@collabora.com>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -28,6 +29,7 @@
#include "utils/Units.h" #include "utils/Units.h"
#include "utils/NamedEnum.h" #include "utils/NamedEnum.h"
#include "GlobalStorage.h"
#include "JobQueue.h" #include "JobQueue.h"
#include "utils/Logger.h" #include "utils/Logger.h"
@ -38,8 +40,6 @@
namespace PartitionActions namespace PartitionActions
{ {
using CalamaresUtils::GiBtoBytes;
using CalamaresUtils::MiBtoBytes;
using CalamaresUtils::operator""_GiB; using CalamaresUtils::operator""_GiB;
using CalamaresUtils::operator""_MiB; using CalamaresUtils::operator""_MiB;
@ -82,25 +82,10 @@ swapSuggestion( const qint64 availableSpaceB, Choices::SwapChoice swap )
return suggestedSwapSizeB; return suggestedSwapSizeB;
} }
constexpr qint64
alignBytesToBlockSize( qint64 bytes, qint64 blocksize )
{
qint64 blocks = bytes / blocksize;
if ( blocks * blocksize != bytes )
++blocks;
return blocks * blocksize;
}
qint64
bytesToSectors( qint64 bytes, qint64 blocksize )
{
return alignBytesToBlockSize( alignBytesToBlockSize( bytes, blocksize), MiBtoBytes(1) ) / blocksize;
}
void void
doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionOptions o ) doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionOptions o )
{ {
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
QString defaultFsType = o.defaultFsType; QString defaultFsType = o.defaultFsType;
if ( FileSystem::typeForName( defaultFsType ) == FileSystem::Unknown ) if ( FileSystem::typeForName( defaultFsType ) == FileSystem::Unknown )
defaultFsType = "ext4"; defaultFsType = "ext4";
@ -109,19 +94,27 @@ doAutopartition( PartitionCoreModule* core, Device* dev, Choices::AutoPartitionO
// Partition sizes are expressed in MiB, should be multiples of // Partition sizes are expressed in MiB, should be multiples of
// the logical sector size (usually 512B). EFI starts with 2MiB // the logical sector size (usually 512B). EFI starts with 2MiB
// empty and a 300MiB EFI boot partition, while BIOS starts at // empty and a EFI boot partition, while BIOS starts at
// the 1MiB boundary (usually sector 2048). // the 1MiB boundary (usually sector 2048).
int uefisys_part_sizeB = isEfi ? 300_MiB : 0_MiB;
int empty_space_sizeB = isEfi ? 2_MiB : 1_MiB; int empty_space_sizeB = isEfi ? 2_MiB : 1_MiB;
int uefisys_part_sizeB = 0_MiB;
if ( isEfi )
{
if ( gs->contains( "efiSystemPartitionSize" ) )
uefisys_part_sizeB = PartUtils::parseSizeString( gs->value( "efiSystemPartitionSize" ).toString(), dev->capacity() );
else
uefisys_part_sizeB = 300_MiB;
}
// Since sectors count from 0, if the space is 2048 sectors in size, // Since sectors count from 0, if the space is 2048 sectors in size,
// the first free sector has number 2048 (and there are 2048 sectors // the first free sector has number 2048 (and there are 2048 sectors
// before that one, numbered 0..2047). // before that one, numbered 0..2047).
qint64 firstFreeSector = bytesToSectors( empty_space_sizeB, dev->logicalSize() ); qint64 firstFreeSector = PartUtils::bytesToSectors( empty_space_sizeB, dev->logicalSize() );
if ( isEfi ) if ( isEfi )
{ {
qint64 efiSectorCount = bytesToSectors( uefisys_part_sizeB, dev->logicalSize() ); qint64 efiSectorCount = PartUtils::bytesToSectors( uefisys_part_sizeB, dev->logicalSize() );
Q_ASSERT( efiSectorCount > 0 ); Q_ASSERT( efiSectorCount > 0 );
// Since sectors count from 0, and this partition is created starting // Since sectors count from 0, and this partition is created starting

View File

@ -75,8 +75,6 @@ namespace Choices
} // namespace Choices } // namespace Choices
qint64 bytesToSectors( qint64 bytes, qint64 blocksize );
/** /**
* @brief doAutopartition sets up an autopartitioning operation on the given Device. * @brief doAutopartition sets up an autopartitioning operation on the given Device.
* @param core a pointer to the PartitionCoreModule instance. * @param core a pointer to the PartitionCoreModule instance.

View File

@ -154,6 +154,9 @@ public:
void setPartitionFlags( Device* device, Partition* partition, PartitionTable::Flags flags ); void setPartitionFlags( Device* device, Partition* partition, PartitionTable::Flags flags );
/// @brief Retrieve the path where the bootloader will be installed
QString bootLoaderInstallPath() const { return m_bootLoaderInstallPath; }
/// @brief Set the path where the bootloader will be installed
void setBootLoaderInstallPath( const QString& path ); void setBootLoaderInstallPath( const QString& path );
void initLayout(); void initLayout();

View File

@ -18,27 +18,50 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>. * along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "GlobalStorage.h"
#include "JobQueue.h"
#include "core/PartitionLayout.h" #include "core/PartitionLayout.h"
#include "core/KPMHelpers.h" #include "core/KPMHelpers.h"
#include "core/PartitionActions.h" #include "core/PartitionActions.h"
#include "core/PartitionInfo.h" #include "core/PartitionInfo.h"
#include "core/PartUtils.h"
#include <kpmcore/core/device.h> #include <kpmcore/core/device.h>
#include <kpmcore/core/partition.h> #include <kpmcore/core/partition.h>
#include <kpmcore/fs/filesystem.h> #include <kpmcore/fs/filesystem.h>
static FileSystem::Type
getDefaultFileSystemType()
{
Calamares::GlobalStorage* gs = Calamares::JobQueue::instance()->globalStorage();
FileSystem::Type defaultFS = FileSystem::Ext4;
if ( gs->contains( "defaultFileSystemType" ) )
{
PartUtils::findFS( gs->value( "defaultFileSystemType" ).toString(), &defaultFS);
if ( defaultFS == FileSystem::Unknown )
defaultFS = FileSystem::Ext4;
}
return defaultFS;
}
PartitionLayout::PartitionLayout() PartitionLayout::PartitionLayout()
{ {
m_defaultFsType = getDefaultFileSystemType();
} }
PartitionLayout::PartitionLayout( PartitionLayout::PartitionEntry entry ) PartitionLayout::PartitionLayout( PartitionLayout::PartitionEntry entry )
{ {
partLayout.append( entry ); m_defaultFsType = getDefaultFileSystemType();
m_partLayout.append( entry );
} }
PartitionLayout::PartitionLayout( const PartitionLayout& layout ) PartitionLayout::PartitionLayout( const PartitionLayout& layout )
: partLayout( layout.partLayout ) : m_partLayout( layout.m_partLayout )
, m_defaultFsType( layout.m_defaultFsType )
{ {
} }
@ -49,64 +72,14 @@ PartitionLayout::~PartitionLayout()
void void
PartitionLayout::addEntry( PartitionLayout::PartitionEntry entry ) PartitionLayout::addEntry( PartitionLayout::PartitionEntry entry )
{ {
partLayout.append( entry ); m_partLayout.append( entry );
}
static double
parseSizeString( const QString& sizeString, PartitionLayout::SizeUnit* unit )
{
double value;
bool ok;
QString valueString;
QString unitString;
QRegExp rx( "[KkMmGg%]" );
int pos = rx.indexIn( sizeString );
if (pos > 0)
{
valueString = sizeString.mid( 0, pos );
unitString = sizeString.mid( pos );
}
else
valueString = sizeString;
value = valueString.toDouble( &ok );
if ( !ok )
{
/*
* In case the conversion fails, a size of 100% allows a few cases to pass
* anyway (e.g. when it is the last partition of the layout)
*/
*unit = PartitionLayout::SizeUnit::Percent;
return 100;
}
if ( unitString.length() > 0 )
{
if ( unitString.at(0) == '%' )
*unit = PartitionLayout::SizeUnit::Percent;
else if ( unitString.at(0).toUpper() == 'K' )
*unit = PartitionLayout::SizeUnit::KiB;
else if ( unitString.at(0).toUpper() == 'M' )
*unit = PartitionLayout::SizeUnit::MiB;
else if ( unitString.at(0).toUpper() == 'G' )
*unit = PartitionLayout::SizeUnit::GiB;
else
*unit = PartitionLayout::SizeUnit::Byte;
}
else
{
*unit = PartitionLayout::SizeUnit::Byte;
}
return value;
} }
PartitionLayout::PartitionEntry::PartitionEntry(const QString& size, const QString& min) PartitionLayout::PartitionEntry::PartitionEntry(const QString& size, const QString& min)
{ {
partSize = parseSizeString( size , &partSizeUnit ); partSize = PartUtils::parseSizeString( size , &partSizeUnit );
if ( !min.isEmpty() ) if ( !min.isEmpty() )
partMinSize = parseSizeString( min , &partMinSizeUnit ); partMinSize = PartUtils::parseSizeString( min , &partMinSizeUnit );
} }
void void
@ -115,9 +88,9 @@ PartitionLayout::addEntry( const QString& mountPoint, const QString& size, const
PartitionLayout::PartitionEntry entry( size, min ); PartitionLayout::PartitionEntry entry( size, min );
entry.partMountPoint = mountPoint; entry.partMountPoint = mountPoint;
entry.partFileSystem = FileSystem::Ext4; entry.partFileSystem = m_defaultFsType;
partLayout.append( entry ); m_partLayout.append( entry );
} }
void void
@ -127,38 +100,11 @@ PartitionLayout::addEntry( const QString& label, const QString& mountPoint, cons
entry.partLabel = label; entry.partLabel = label;
entry.partMountPoint = mountPoint; entry.partMountPoint = mountPoint;
entry.partFileSystem = FileSystem::typeForName( fs ); PartUtils::findFS( fs, &entry.partFileSystem );
if ( entry.partFileSystem == FileSystem::Unknown )
entry.partFileSystem = m_defaultFsType;
partLayout.append( entry ); m_partLayout.append( entry );
}
static qint64
sizeToSectors( double size, PartitionLayout::SizeUnit unit, qint64 totalSize, qint64 logicalSize )
{
qint64 sectors;
double tmp;
if ( unit == PartitionLayout::SizeUnit::Percent )
{
tmp = static_cast<double>( totalSize ) * size / 100;
sectors = static_cast<qint64>( tmp );
}
else
{
tmp = size;
if ( unit >= PartitionLayout::SizeUnit::KiB )
tmp *= 1024;
if ( unit >= PartitionLayout::SizeUnit::MiB )
tmp *= 1024;
if ( unit >= PartitionLayout::SizeUnit::GiB )
tmp *= 1024;
sectors = PartitionActions::bytesToSectors( static_cast<unsigned long long>( tmp ),
logicalSize
);
}
return sectors;
} }
QList< Partition* > QList< Partition* >
@ -175,13 +121,13 @@ PartitionLayout::execute( Device *dev, qint64 firstSector,
// TODO: Refine partition sizes to make sure there is room for every partition // TODO: Refine partition sizes to make sure there is room for every partition
// Use a default (200-500M ?) minimum size for partition without minSize // Use a default (200-500M ?) minimum size for partition without minSize
foreach( const PartitionLayout::PartitionEntry& part, partLayout ) foreach( const PartitionLayout::PartitionEntry& part, m_partLayout )
{ {
Partition *currentPartition = nullptr; Partition *currentPartition = nullptr;
// Calculate partition size // Calculate partition size
size = sizeToSectors( part.partSize, part.partSizeUnit, totalSize, dev->logicalSize() ); size = PartUtils::sizeToSectors( part.partSize, part.partSizeUnit, totalSize, dev->logicalSize() );
minSize = sizeToSectors( part.partMinSize, part.partMinSizeUnit, totalSize, dev->logicalSize() ); minSize = PartUtils::sizeToSectors( part.partMinSize, part.partMinSizeUnit, totalSize, dev->logicalSize() );
if ( size < minSize ) if ( size < minSize )
size = minSize; size = minSize;
if ( size > availableSize ) if ( size > availableSize )
@ -194,7 +140,7 @@ PartitionLayout::execute( Device *dev, qint64 firstSector,
parent, parent,
*dev, *dev,
role, role,
static_cast<FileSystem::Type>(part.partFileSystem), part.partFileSystem,
firstSector, firstSector,
end, end,
PartitionTable::FlagNone PartitionTable::FlagNone
@ -206,7 +152,7 @@ PartitionLayout::execute( Device *dev, qint64 firstSector,
parent, parent,
*dev, *dev,
role, role,
static_cast<FileSystem::Type>(part.partFileSystem), part.partFileSystem,
firstSector, firstSector,
end, end,
luksPassphrase, luksPassphrase,

View File

@ -20,10 +20,13 @@
#ifndef PARTITIONLAYOUT_H #ifndef PARTITIONLAYOUT_H
#define PARTITIONLAYOUT_H #define PARTITIONLAYOUT_H
#include "core/PartUtils.h"
#include "Typedefs.h" #include "Typedefs.h"
// KPMcore // KPMcore
#include <kpmcore/core/partitiontable.h> #include <kpmcore/core/partitiontable.h>
#include <kpmcore/fs/filesystem.h>
// Qt // Qt
#include <QList> #include <QList>
@ -35,24 +38,15 @@ class PartitionLayout
{ {
public: public:
enum SizeUnit
{
Percent = 0,
Byte,
KiB,
MiB,
GiB
};
struct PartitionEntry struct PartitionEntry
{ {
QString partLabel; QString partLabel;
QString partMountPoint; QString partMountPoint;
int partFileSystem = 0; FileSystem::Type partFileSystem = FileSystem::Unknown;
double partSize = 0.0L; double partSize = 0.0L;
SizeUnit partSizeUnit = Percent; PartUtils::SizeUnit partSizeUnit = PartUtils::SizeUnit::Percent;
double partMinSize = 0.0L; double partMinSize = 0.0L;
SizeUnit partMinSizeUnit = Percent; PartUtils::SizeUnit partMinSizeUnit = PartUtils::SizeUnit::Percent;
/// @brief All-zeroes PartitionEntry /// @brief All-zeroes PartitionEntry
PartitionEntry() {}; PartitionEntry() {};
@ -76,7 +70,8 @@ public:
QList< Partition* > execute( Device *dev, qint64 firstSector, qint64 lastSector, QString luksPassphrase, PartitionNode* parent, const PartitionRole& role ); QList< Partition* > execute( Device *dev, qint64 firstSector, qint64 lastSector, QString luksPassphrase, PartitionNode* parent, const PartitionRole& role );
private: private:
QList< PartitionEntry > partLayout; FileSystem::Type m_defaultFsType;
QList< PartitionEntry > m_partLayout;
}; };
#endif /* PARTITIONLAYOUT_H */ #endif /* PARTITIONLAYOUT_H */

View File

@ -882,11 +882,6 @@ ChoicePage::updateDeviceStatePreview()
PartitionModel* model = new PartitionModel( m_beforePartitionBarsView ); PartitionModel* model = new PartitionModel( m_beforePartitionBarsView );
model->init( deviceBefore, m_core->osproberEntries() ); model->init( deviceBefore, m_core->osproberEntries() );
// The QObject parents tree is meaningful for memory management here,
// see qDeleteAll above.
deviceBefore->setParent( model ); // Can't reparent across threads
model->setParent( m_beforePartitionBarsView );
m_beforePartitionBarsView->setModel( model ); m_beforePartitionBarsView->setModel( model );
m_beforePartitionLabelsView->setModel( model ); m_beforePartitionLabelsView->setModel( model );
@ -1248,7 +1243,7 @@ ChoicePage::setupActions()
} }
if ( PartUtils::canBeReplaced( *it ) ) if ( PartUtils::canBeReplaced( *it ) )
{ {
cDebug() << ".. contains replacable" << it; cDebug() << ".. contains replaceable" << it;
atLeastOneCanBeReplaced = true; atLeastOneCanBeReplaced = true;
} }
if ( (*it)->isMounted() ) if ( (*it)->isMounted() )

View File

@ -34,7 +34,7 @@ CreateVolumeGroupDialog::CreateVolumeGroupDialog( QString& vgName,
, m_selectedPVs( selectedPVs ) , m_selectedPVs( selectedPVs )
, m_peSize( pSize ) , m_peSize( pSize )
{ {
setWindowTitle( "Create Volume Group" ); setWindowTitle( tr( "Create Volume Group" ) );
peSize()->setValue( pSize ); peSize()->setValue( pSize );

View File

@ -44,7 +44,7 @@ void
standardMountPoints(QComboBox& combo) standardMountPoints(QComboBox& combo)
{ {
combo.clear(); combo.clear();
combo.addItem( combo.tr( "(no mount point)" ) ); combo.addItem( QObject::tr( "(no mount point)" ) );
combo.addItems( standardMountPoints() ); combo.addItems( standardMountPoints() );
} }

View File

@ -56,7 +56,6 @@
#include <kpmcore/ops/removevolumegroupoperation.h> #include <kpmcore/ops/removevolumegroupoperation.h>
// Qt // Qt
#include <QDebug>
#include <QHeaderView> #include <QHeaderView>
#include <QItemSelectionModel> #include <QItemSelectionModel>
#include <QMessageBox> #include <QMessageBox>
@ -90,22 +89,9 @@ PartitionPage::PartitionPage( PartitionCoreModule* core, QWidget* parent )
updateFromCurrentDevice(); updateFromCurrentDevice();
connect( m_ui->deviceComboBox, &QComboBox::currentTextChanged, connect( m_ui->deviceComboBox, &QComboBox::currentTextChanged, this, &PartitionPage::updateFromCurrentDevice );
[ this ]( const QString& /* text */ ) connect( m_ui->bootLoaderComboBox, QOverload<int>::of(&QComboBox::activated), this, &PartitionPage::updateSelectedBootLoaderIndex );
{ connect( m_ui->bootLoaderComboBox, &QComboBox::currentTextChanged, this, &PartitionPage::updateBootLoaderInstallPath );
updateFromCurrentDevice();
} );
connect( m_ui->bootLoaderComboBox, static_cast<void(QComboBox::*)(const QString &)>(&QComboBox::activated),
[ this ]( const QString& /* text */ )
{
m_lastSelectedBootLoaderIndex = m_ui->bootLoaderComboBox->currentIndex();
} );
connect( m_ui->bootLoaderComboBox, &QComboBox::currentTextChanged,
[ this ]( const QString& /* text */ )
{
updateBootLoaderInstallPath();
} );
connect( m_core, &PartitionCoreModule::isDirtyChanged, m_ui->revertButton, &QWidget::setEnabled ); connect( m_core, &PartitionCoreModule::isDirtyChanged, m_ui->revertButton, &QWidget::setEnabled );
@ -376,18 +362,18 @@ PartitionPage::onCreateClicked()
if ( !checkCanCreate( model->device() ) ) if ( !checkCanCreate( model->device() ) )
return; return;
QPointer< CreatePartitionDialog > dlg = new CreatePartitionDialog( model->device(), CreatePartitionDialog dlg(
model->device(),
partition->parent(), partition->parent(),
nullptr, nullptr,
getCurrentUsedMountpoints(), getCurrentUsedMountpoints(),
this ); this );
dlg->initFromFreeSpace( partition ); dlg.initFromFreeSpace( partition );
if ( dlg->exec() == QDialog::Accepted ) if ( dlg.exec() == QDialog::Accepted )
{ {
Partition* newPart = dlg->createPartition(); Partition* newPart = dlg.createPartition();
m_core->createPartition( model->device(), newPart, dlg->newFlags() ); m_core->createPartition( model->device(), newPart, dlg.newFlags() );
} }
delete dlg;
} }
void void
@ -508,10 +494,17 @@ PartitionPage::updateBootLoaderInstallPath()
QVariant var = m_ui->bootLoaderComboBox->currentData( BootLoaderModel::BootLoaderPathRole ); QVariant var = m_ui->bootLoaderComboBox->currentData( BootLoaderModel::BootLoaderPathRole );
if ( !var.isValid() ) if ( !var.isValid() )
return; return;
qDebug() << "PartitionPage::updateBootLoaderInstallPath" << var.toString(); cDebug() << "PartitionPage::updateBootLoaderInstallPath" << var.toString();
m_core->setBootLoaderInstallPath( var.toString() ); m_core->setBootLoaderInstallPath( var.toString() );
} }
void
PartitionPage::updateSelectedBootLoaderIndex()
{
m_lastSelectedBootLoaderIndex = m_ui->bootLoaderComboBox->currentIndex();
cDebug() << "Selected bootloader index" << m_lastSelectedBootLoaderIndex;
}
void void
PartitionPage::updateFromCurrentDevice() PartitionPage::updateFromCurrentDevice()
{ {
@ -581,7 +574,7 @@ void
PartitionPage::onPartitionModelReset() PartitionPage::onPartitionModelReset()
{ {
m_ui->partitionTreeView->expandAll(); m_ui->partitionTreeView->expandAll();
// updateButtons(); updateButtons();
updateBootLoaderIndex(); updateBootLoaderIndex();
} }

View File

@ -50,6 +50,14 @@ public:
int selectedDeviceIndex(); int selectedDeviceIndex();
void selectDeviceByIndex( int index ); void selectDeviceByIndex( int index );
private slots:
/// @brief Update everything when the base device changes
void updateFromCurrentDevice();
/// @brief Update when the selected device for boot loader changes
void updateBootLoaderInstallPath();
/// @brief Explicitly selected boot loader path
void updateSelectedBootLoaderIndex();
private: private:
QScopedPointer< Ui_PartitionPage > m_ui; QScopedPointer< Ui_PartitionPage > m_ui;
PartitionCoreModule* m_core; PartitionCoreModule* m_core;
@ -67,8 +75,6 @@ private:
void updatePartitionToCreate( Device*, Partition* ); void updatePartitionToCreate( Device*, Partition* );
void editExistingPartition( Device*, Partition* ); void editExistingPartition( Device*, Partition* );
void updateBootLoaderInstallPath();
void updateFromCurrentDevice();
void updateBootLoaderIndex(); void updateBootLoaderIndex();
/** /**

View File

@ -3,7 +3,7 @@
* Copyright 2014, Aurélien Gâteau <agateau@kde.org> * Copyright 2014, Aurélien Gâteau <agateau@kde.org>
* Copyright 2014-2017, Teo Mrnjavac <teo@kde.org> * Copyright 2014-2017, Teo Mrnjavac <teo@kde.org>
* Copyright 2018, Adriaan de Groot <groot@kde.org> * Copyright 2018, Adriaan de Groot <groot@kde.org>
* Copyright 2019, Collabora Ltd * Copyright 2019, Collabora Ltd <arnaud.ferraris@collabora.com>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -60,6 +60,8 @@
#include <QtConcurrent/QtConcurrent> #include <QtConcurrent/QtConcurrent>
#include <QFutureWatcher> #include <QFutureWatcher>
#include <unistd.h> // For sleep(3)
PartitionViewStep::PartitionViewStep( QObject* parent ) PartitionViewStep::PartitionViewStep( QObject* parent )
: Calamares::ViewStep( parent ) : Calamares::ViewStep( parent )
, m_core( nullptr ) , m_core( nullptr )
@ -292,25 +294,8 @@ PartitionViewStep::next()
if ( m_core->isDirty() ) if ( m_core->isDirty() )
m_manualPartitionPage->onRevertClicked(); m_manualPartitionPage->onRevertClicked();
} }
else if ( m_choicePage->currentChoice() == ChoicePage::Erase )
{
emit done();
return;
}
else if ( m_choicePage->currentChoice() == ChoicePage::Alongside )
{
emit done();
return;
}
else if ( m_choicePage->currentChoice() == ChoicePage::Replace )
{
emit done();
return;
}
cDebug() << "Choice applied: " << m_choicePage->currentChoice(); cDebug() << "Choice applied: " << m_choicePage->currentChoice();
return;
} }
emit done();
} }
@ -397,7 +382,7 @@ PartitionViewStep::onLeave()
if ( PartUtils::isEfiSystem() ) if ( PartUtils::isEfiSystem() )
{ {
QString espMountPoint = Calamares::JobQueue::instance()->globalStorage()-> QString espMountPoint = Calamares::JobQueue::instance()->globalStorage()->
value( "efiSystemPartition").toString(); value( "efiSystemPartition" ).toString();
Partition* esp = m_core->findPartitionByMountPoint( espMountPoint ); Partition* esp = m_core->findPartitionByMountPoint( espMountPoint );
QString message; QString message;
@ -493,55 +478,6 @@ nameToChoice( QString name, bool& ok )
return names.find( name, ok ); return names.find( name, ok );
} }
/** @brief translate @p defaultFS into a recognized name
*
* Makes several attempts to translate the string into a
* name that KPMCore will recognize.
*/
static QString
findFS( QString defaultFS )
{
QStringList fsLanguage { QLatin1Literal( "C" ) }; // Required language list to turn off localization
if ( defaultFS.isEmpty() )
{
cWarning() << "Partition-module setting *defaultFileSystemType* is missing, using ext4";
defaultFS = QStringLiteral( "ext4" );
}
if ( FileSystem::typeForName( defaultFS, fsLanguage ) != FileSystem::Unknown )
{
cDebug() << "Partition-module setting *defaultFileSystemType*" << defaultFS;
return defaultFS;
}
// Second pass: try case-insensitive
const auto fstypes = FileSystem::types();
for ( FileSystem::Type t : fstypes )
{
if ( 0 == QString::compare( defaultFS, FileSystem::nameForType( t, fsLanguage ), Qt::CaseInsensitive ) )
{
defaultFS = FileSystem::nameForType( t, fsLanguage );
cWarning() << "Partition-module setting *defaultFileSystemType* changed" << defaultFS;
return defaultFS;
}
}
cWarning() << "Partition-module setting *defaultFileSystemType* is bad (" << defaultFS << ") using ext4.";
defaultFS = QStringLiteral( "ext4" );
#ifdef DEBUG_FILESYSTEMS
// This bit is for distro's debugging their settings, and shows
// all the strings that KPMCore is matching against for FS type.
{
Logger::CDebug d;
using TR = Logger::DebugRow< int, QString >;
const auto fstypes = FileSystem::types();
d << "Available types (" << fstypes.count() << ')';
for ( FileSystem::Type t : fstypes )
d << TR( static_cast<int>( t ), FileSystem::nameForType( t, fsLanguage ) );
}
#endif
return defaultFS;
}
void void
PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap ) PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
{ {
@ -553,6 +489,17 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
efiSP = QStringLiteral( "/boot/efi" ); efiSP = QStringLiteral( "/boot/efi" );
gs->insert( "efiSystemPartition", efiSP ); gs->insert( "efiSystemPartition", efiSP );
// Set up firmwareType global storage entry. This is used, e.g. by the bootloader module.
QString firmwareType( PartUtils::isEfiSystem() ? QStringLiteral( "efi" ) : QStringLiteral( "bios" ) );
cDebug() << "Setting firmwareType to" << firmwareType;
gs->insert( "firmwareType", firmwareType );
// Read and parse key efiSystemPartitionSize
if ( configurationMap.contains( "efiSystemPartitionSize" ) )
{
gs->insert( "efiSystemPartitionSize", CalamaresUtils::getString( configurationMap, "efiSystemPartitionSize" ) );
}
// SWAP SETTINGS // SWAP SETTINGS
// //
// This is a bit convoluted because there's legacy settings to handle as well // This is a bit convoluted because there's legacy settings to handle as well
@ -630,23 +577,38 @@ PartitionViewStep::setConfigurationMap( const QVariantMap& configurationMap )
gs->insert( "alwaysShowPartitionLabels", CalamaresUtils::getBool( configurationMap, "alwaysShowPartitionLabels", true ) ); gs->insert( "alwaysShowPartitionLabels", CalamaresUtils::getBool( configurationMap, "alwaysShowPartitionLabels", true ) );
gs->insert( "enableLuksAutomatedPartitioning", CalamaresUtils::getBool( configurationMap, "enableLuksAutomatedPartitioning", true ) ); gs->insert( "enableLuksAutomatedPartitioning", CalamaresUtils::getBool( configurationMap, "enableLuksAutomatedPartitioning", true ) );
gs->insert( "allowManualPartitioning", CalamaresUtils::getBool( configurationMap, "allowManualPartitioning", true ) ); gs->insert( "allowManualPartitioning", CalamaresUtils::getBool( configurationMap, "allowManualPartitioning", true ) );
gs->insert( "defaultFileSystemType", findFS( CalamaresUtils::getString( configurationMap, "defaultFileSystemType" ) ) );
// The defaultFileSystemType setting needs a bit more processing,
// as we want to cover various cases (such as different cases)
QString fsName = CalamaresUtils::getString( configurationMap, "defaultFileSystemType" );
FileSystem::Type fsType;
if ( fsName.isEmpty() )
cWarning() << "Partition-module setting *defaultFileSystemType* is missing, will use ext4";
QString fsRealName = PartUtils::findFS( fsName, &fsType );
if ( fsRealName == fsName )
cDebug() << "Partition-module setting *defaultFileSystemType*" << fsRealName;
else if ( fsType != FileSystem::Unknown )
cWarning() << "Partition-module setting *defaultFileSystemType* changed" << fsRealName;
else
cWarning() << "Partition-module setting *defaultFileSystemType* is bad (" << fsRealName << ") using ext4.";
gs->insert( "defaultFileSystemType", fsRealName );
// Now that we have the config, we load the PartitionCoreModule in the background // Now that we have the config, we load the PartitionCoreModule in the background
// because it could take a while. Then when it's done, we can set up the widgets // because it could take a while. Then when it's done, we can set up the widgets
// and remove the spinner. // and remove the spinner.
QFutureWatcher< void >* watcher = new QFutureWatcher< void >(); m_future = new QFutureWatcher< void >();
connect( watcher, &QFutureWatcher< void >::finished, connect( m_future, &QFutureWatcher< void >::finished,
this, [ this, watcher, choices ] this, [ this ]
{ {
continueLoading(); continueLoading();
watcher->deleteLater(); this->m_future->deleteLater();
this->m_future = nullptr;
} ); } );
QFuture< void > future = QFuture< void > future =
QtConcurrent::run( this, &PartitionViewStep::initPartitionCoreModule ); QtConcurrent::run( this, &PartitionViewStep::initPartitionCoreModule );
watcher->setFuture( future ); m_future->setFuture( future );
if ( configurationMap.contains( "partitionLayout" ) ) if ( configurationMap.contains( "partitionLayout" ) )
{ {
@ -665,5 +627,24 @@ PartitionViewStep::jobs() const
return m_core->jobs(); return m_core->jobs();
} }
Calamares::RequirementsList
PartitionViewStep::checkRequirements()
{
if ( m_future )
m_future->waitForFinished();
Calamares::RequirementsList l;
l.append(
{
QLatin1Literal( "partitions" ),
[]{ return tr( "has at least one disk device available." ); },
[]{ return tr( "There are no partitons to install on." ); },
m_core->deviceModel()->rowCount() > 0, // satisfied
true // required
} );
return l;
}
CALAMARES_PLUGIN_FACTORY_DEFINITION( PartitionViewStepFactory, registerPlugin<PartitionViewStep>(); ) CALAMARES_PLUGIN_FACTORY_DEFINITION( PartitionViewStepFactory, registerPlugin<PartitionViewStep>(); )

View File

@ -36,6 +36,8 @@ class PartitionPage;
class PartitionCoreModule; class PartitionCoreModule;
class QStackedWidget; class QStackedWidget;
template<typename T> class QFutureWatcher;
/** /**
* The starting point of the module. Instantiates PartitionCoreModule, * The starting point of the module. Instantiates PartitionCoreModule,
* ChoicePage and PartitionPage, then connects them. * ChoicePage and PartitionPage, then connects them.
@ -69,6 +71,8 @@ public:
QList< Calamares::job_ptr > jobs() const override; QList< Calamares::job_ptr > jobs() const override;
Calamares::RequirementsList checkRequirements() override;
private: private:
void initPartitionCoreModule(); void initPartitionCoreModule();
void continueLoading(); void continueLoading();
@ -79,6 +83,7 @@ private:
PartitionPage* m_manualPartitionPage; PartitionPage* m_manualPartitionPage;
QWidget* m_waitingWidget; QWidget* m_waitingWidget;
QFutureWatcher<void>* m_future;
QSet< PartitionActions::Choices::SwapChoice > m_swapChoices; QSet< PartitionActions::Choices::SwapChoice > m_swapChoices;
}; };

View File

@ -35,7 +35,7 @@ ResizeVolumeGroupDialog::ResizeVolumeGroupDialog( LvmDevice *device,
: VolumeGroupBaseDialog( device->name(), device->physicalVolumes(), parent ) : VolumeGroupBaseDialog( device->name(), device->physicalVolumes(), parent )
, m_selectedPVs( selectedPVs ) , m_selectedPVs( selectedPVs )
{ {
setWindowTitle( "Resize Volume Group" ); setWindowTitle( tr( "Resize Volume Group" ) );
for ( int i = 0; i < pvList()->count(); i++ ) for ( int i = 0; i < pvList()->count(); i++ )
pvList()->item(i)->setCheckState( Qt::Checked ); pvList()->item(i)->setCheckState( Qt::Checked );

View File

@ -11,7 +11,7 @@
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>VolumeGroupDialog</string> <string>Create Volume Group</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="0" column="0">

View File

@ -27,6 +27,7 @@
class CreateVolumeGroupJob : public Calamares::Job class CreateVolumeGroupJob : public Calamares::Job
{ {
Q_OBJECT
public: public:
CreateVolumeGroupJob( QString& vgName, QVector< const Partition* > pvList, const qint32 peSize ); CreateVolumeGroupJob( QString& vgName, QVector< const Partition* > pvList, const qint32 peSize );

View File

@ -25,6 +25,7 @@ class LvmDevice;
class DeactivateVolumeGroupJob : public Calamares::Job class DeactivateVolumeGroupJob : public Calamares::Job
{ {
Q_OBJECT
public: public:
DeactivateVolumeGroupJob( LvmDevice* device ); DeactivateVolumeGroupJob( LvmDevice* device );

View File

@ -25,6 +25,7 @@ class LvmDevice;
class RemoveVolumeGroupJob : public Calamares::Job class RemoveVolumeGroupJob : public Calamares::Job
{ {
Q_OBJECT
public: public:
RemoveVolumeGroupJob( LvmDevice* device ); RemoveVolumeGroupJob( LvmDevice* device );

View File

@ -28,6 +28,7 @@ class Partition;
class ResizeVolumeGroupJob : public Calamares::Job class ResizeVolumeGroupJob : public Calamares::Job
{ {
Q_OBJECT
public: public:
ResizeVolumeGroupJob( LvmDevice* device, QVector< const Partition* >& partitionList ); ResizeVolumeGroupJob( LvmDevice* device, QVector< const Partition* >& partitionList );

View File

@ -3,6 +3,10 @@
# etc.) use just /boot. # etc.) use just /boot.
efiSystemPartition: "/boot/efi" efiSystemPartition: "/boot/efi"
# This optional setting specifies the size of the EFI system partition.
# If nothing is specified, the default size of 300MiB will be used.
# efiSystemPartitionSize: 300M
# In autogenerated partitioning, allow the user to select a swap size? # In autogenerated partitioning, allow the user to select a swap size?
# If there is exactly one choice, no UI is presented, and the user # If there is exactly one choice, no UI is presented, and the user
# cannot make a choice -- this setting is used. If there is more than # cannot make a choice -- this setting is used. If there is more than

View File

@ -350,7 +350,8 @@ PartitionJobTests::testResizePartition()
// Make the test data file smaller than the full size of the partition to // Make the test data file smaller than the full size of the partition to
// accomodate for the file system overhead // accomodate for the file system overhead
const QByteArray testData = generateTestData( CalamaresUtils::MiBtoBytes( qMin( oldSizeMB, newSizeMB ) ) * 3 / 4 ); const unsigned long long minSizeMB = qMin( oldSizeMB, newSizeMB );
const QByteArray testData = generateTestData( CalamaresUtils::MiBtoBytes( minSizeMB ) * 3 / 4 );
const QString testName = "test.data"; const QString testName = "test.data";
// Setup: create the test partition // Setup: create the test partition

View File

@ -76,18 +76,6 @@ PlasmaLnfViewStep::widget()
} }
void
PlasmaLnfViewStep::next()
{
emit done();
}
void
PlasmaLnfViewStep::back()
{}
bool bool
PlasmaLnfViewStep::isNextEnabled() const PlasmaLnfViewStep::isNextEnabled() const
{ {

View File

@ -41,9 +41,6 @@ public:
QWidget* widget() override; QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override; bool isNextEnabled() const override;
bool isBackEnabled() const override; bool isBackEnabled() const override;

View File

@ -6,6 +6,7 @@
# Copyright 2016, Artoo <artoo@manjaro.org> # Copyright 2016, Artoo <artoo@manjaro.org>
# Copyright 2017, Alf Gaida <agaida@siduction.org> # Copyright 2017, Alf Gaida <agaida@siduction.org>
# Copyright 2018, Gabriel Craciunescu <crazy@frugalware.org> # Copyright 2018, Gabriel Craciunescu <crazy@frugalware.org>
# Copyright 2019, Adriaan de Groot <groot@kde.org>
# #
# Calamares is free software: you can redistribute it and/or modify # Calamares is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -24,6 +25,16 @@ import libcalamares
from libcalamares.utils import debug, target_env_call from libcalamares.utils import debug, target_env_call
import gettext
_ = gettext.translation("calamares-python",
localedir=libcalamares.utils.gettext_path(),
languages=libcalamares.utils.gettext_languages(),
fallback=True).gettext
def pretty_name():
return _("Configure Plymouth theme")
class PlymouthController: class PlymouthController:

View File

@ -5,6 +5,7 @@
# #
# Copyright 2015, Teo Mrnjavac <teo@kde.org> # Copyright 2015, Teo Mrnjavac <teo@kde.org>
# Copyright 2017. Alf Gaida <agaida@siduction.org> # Copyright 2017. Alf Gaida <agaida@siduction.org>
# Copyright 2019, Adriaan de Groot <groot@kde.org>
# #
# Calamares is free software: you can redistribute it and/or modify # Calamares is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -22,6 +23,16 @@
import subprocess import subprocess
import libcalamares import libcalamares
import gettext
_ = gettext.translation("calamares-python",
localedir=libcalamares.utils.gettext_path(),
languages=libcalamares.utils.gettext_languages(),
fallback=True).gettext
def pretty_name():
return _("Remove live user from target system")
def run(): def run():
""" """

View File

@ -6,7 +6,7 @@
# Copyright 2016, Artoo <artoo@manjaro.org> # Copyright 2016, Artoo <artoo@manjaro.org>
# Copyright 2017, Philip Müller <philm@manjaro.org> # Copyright 2017, Philip Müller <philm@manjaro.org>
# Copyright 2018, Artoo <artoo@artixlinux.org> # Copyright 2018, Artoo <artoo@artixlinux.org>
# Copyright 2018, Adriaan de Groot <groot@kde.org> # Copyright 2018-2019, Adriaan de Groot <groot@kde.org>
# #
# Calamares is free software: you can redistribute it and/or modify # Calamares is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -27,6 +27,17 @@ from libcalamares.utils import target_env_call, warning
from os.path import exists, join from os.path import exists, join
import gettext
_ = gettext.translation("calamares-python",
localedir=libcalamares.utils.gettext_path(),
languages=libcalamares.utils.gettext_languages(),
fallback=True).gettext
def pretty_name():
return _("Configure OpenRC services")
class OpenrcController: class OpenrcController:
""" """
This is the openrc service controller. This is the openrc service controller.
@ -45,6 +56,22 @@ class OpenrcController:
self.initdDir = libcalamares.job.configuration['initdDir'] self.initdDir = libcalamares.job.configuration['initdDir']
self.runlevelsDir = libcalamares.job.configuration['runlevelsDir'] self.runlevelsDir = libcalamares.job.configuration['runlevelsDir']
def make_failure_description(self, state, name, runlevel):
"""
Returns a generic "could not <foo>" failure message, specialized
for the action @p state and the specific service @p name in @p runlevel.
"""
if state == "add":
description = _("Cannot add service {name!s} to run-level {level!s}.")
elif state == "del":
description = _("Cannot remove service {name!s} from run-level {level!s}.")
else:
description = _("Unknown service-action <code>{arg!s}</code> for service {name!s} in run-level {level!s}.")
return description.format(arg=state, name=name, level=runlevel)
def update(self, state): def update(self, state):
""" """
Call rc-update for each service listed Call rc-update for each service listed
@ -69,24 +96,31 @@ class OpenrcController:
if exists(runlevel_path): if exists(runlevel_path):
ec = target_env_call(["rc-update", state, name, runlevel]) ec = target_env_call(["rc-update", state, name, runlevel])
if ec != 0: if ec != 0:
warning("Cannot {} service {} to {}".format(state, name, runlevel))
warning("rc-update returned error code {!s}".format(ec))
if mandatory: if mandatory:
return ("Cannot {} service {} to {}".format(state, name, runlevel), title = _("Cannot modify service")
"rc-update {} call in chroot returned error code {}".format(state, ec) diagnostic = _("<code>rc-update {arg!s}</code> call in chroot returned error code {num!s}.").format(arg=state, num=ec)
return (title,
self.make_failure_description(state, name, runlevel) + " " + diagnostic
) )
else:
warning("Could not {} service {} in {}, error {!s}".format(state, name, runlevel, ec))
else:
if mandatory:
return ("Target runlevel {} does not exist for {}.".format(runlevel, name),
"No {} found.".format(runlevel_path))
else: else:
warning("Target runlevel {} does not exist for {}.".format(runlevel, name)) warning("Target runlevel {} does not exist for {}.".format(runlevel, name))
else:
if mandatory: if mandatory:
return ("Target service {} does not exist.".format(name), title = _("Target runlevel does not exist")
"No {} found.".format(service_path)) diagnostic = _("The path for runlevel {level!s} is <code>{path!s}</code>, which does not exist.").format(level=runlevel, path=runlevel_path)
return (title,
self.make_failure_description(state, name, runlevel) + " " + diagnostic
)
else: else:
warning("Target service {} does not exist in {}.".format(name, self.initdDir)) warning("Target service {} does not exist in {}.".format(name, self.initdDir))
if mandatory:
title = _("Target service does not exist")
diagnostic = _("The path for service {name!s} is <code>{path!s}</code>, which does not exist.").format(name=name, path=service_path)
return (title,
self.make_failure_description(state, name, runlevel) + " " + diagnostic
)
def run(self): def run(self):

View File

@ -6,7 +6,7 @@
# Copyright 2014, Philip Müller <philm@manjaro.org> # Copyright 2014, Philip Müller <philm@manjaro.org>
# Copyright 2014, Teo Mrnjavac <teo@kde.org> # Copyright 2014, Teo Mrnjavac <teo@kde.org>
# Copyright 2017, Alf Gaida <agaida@siduction.org> # Copyright 2017, Alf Gaida <agaida@siduction.org>
# Copyright 2018, Adriaan de Groot <groot@kde.org> # Copyright 2018-2019, Adriaan de Groot <groot@kde.org>
# #
# Calamares is free software: you can redistribute it and/or modify # Calamares is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -24,6 +24,17 @@
import libcalamares import libcalamares
import gettext
_ = gettext.translation("calamares-python",
localedir=libcalamares.utils.gettext_path(),
languages=libcalamares.utils.gettext_languages(),
fallback=True).gettext
def pretty_name():
return _("Configure systemd services")
def systemctl(targets, command, suffix): def systemctl(targets, command, suffix):
""" """
For each entry in @p targets, run "systemctl <command> <thing>", For each entry in @p targets, run "systemctl <command> <thing>",
@ -47,17 +58,32 @@ def systemctl(targets, command, suffix):
) )
if ec != 0: if ec != 0:
if mandatory:
return ("Cannot {} systemd {} {}".format(command, suffix, name),
"systemctl {} call in chroot returned error code {}".format(command, ec)
)
else:
libcalamares.utils.warning( libcalamares.utils.warning(
"Cannot {} systemd {} {}".format(command, suffix, name) "Cannot {} systemd {} {}".format(command, suffix, name)
) )
libcalamares.utils.warning( libcalamares.utils.warning(
"systemctl {} call in chroot returned error code {}".format(command, ec) "systemctl {} call in chroot returned error code {}".format(command, ec)
) )
if mandatory:
title = _("Cannot modify service")
diagnostic = _("<code>systemctl {arg!s}</code> call in chroot returned error code {num!s}.").format(arg=command, num=ec)
if command == "enable" and suffix == ".service":
description = _("Cannot enable systemd service <code>{name!s}</code>.")
elif command == "enable" and suffix == ".target":
description = _("Cannot enable systemd target <code>{name!s}</code>.")
elif command == "disable" and suffix == ".service":
description = _("Cannot enable systemd service <code>{name!s}</code>.")
elif command == "disable" and suffix == ".target":
description = _("Cannot disable systemd target <code>{name!s}</code>.")
elif command == "mask":
description = _("Cannot mask systemd unit <code>{name!s}</code>.")
else:
description = _("Unknown systemd commands <code>{command!s}</code> and <code>{suffix!s}</code> for unit {name!s}.")
return (title,
description.format(name=name, command=command, suffix=suffix) + " " + diagnostic
)
return None return None
@ -92,6 +118,4 @@ def run():
if r is not None: if r is not None:
return r return r
# This could have just been return r
return None return None

View File

@ -51,18 +51,6 @@ SummaryViewStep::widget()
} }
void
SummaryViewStep::next()
{
emit done();
}
void
SummaryViewStep::back()
{}
bool bool
SummaryViewStep::isNextEnabled() const SummaryViewStep::isNextEnabled() const
{ {

View File

@ -40,9 +40,6 @@ public:
QWidget* widget() override; QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override; bool isNextEnabled() const override;
bool isBackEnabled() const override; bool isBackEnabled() const override;

View File

@ -67,18 +67,6 @@ TrackingViewStep::widget()
} }
void
TrackingViewStep::next()
{
emit done();
}
void
TrackingViewStep::back()
{}
bool bool
TrackingViewStep::isNextEnabled() const TrackingViewStep::isNextEnabled() const
{ {

View File

@ -43,9 +43,6 @@ public:
QWidget* widget() override; QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override; bool isNextEnabled() const override;
bool isBackEnabled() const override; bool isBackEnabled() const override;

View File

@ -154,29 +154,29 @@ CreateUserJob::exec()
useradd << "-c" << m_fullName; useradd << "-c" << m_fullName;
useradd << m_userName; useradd << m_userName;
auto pres = CalamaresUtils::System::instance()->targetEnvCommand( useradd ); auto commandResult = CalamaresUtils::System::instance()->targetEnvCommand( useradd );
if ( pres.getExitCode() ) if ( commandResult.getExitCode() )
{ {
cError() << "useradd failed" << pres.getExitCode(); cError() << "useradd failed" << commandResult.getExitCode();
return pres.explainProcess( useradd, 10 /* bogus timeout */ ); return commandResult.explainProcess( useradd, 10 /* bogus timeout */ );
} }
pres = CalamaresUtils::System::instance()->targetEnvCommand( commandResult = CalamaresUtils::System::instance()->targetEnvCommand(
{ "usermod", "-aG", defaultGroups, m_userName } ); { "usermod", "-aG", defaultGroups, m_userName } );
if ( pres.getExitCode() ) if ( commandResult.getExitCode() )
{ {
cError() << "usermod failed" << pres.getExitCode(); cError() << "usermod failed" << commandResult.getExitCode();
return pres.explainProcess( "usermod", 10 ); return commandResult.explainProcess( "usermod", 10 );
} }
QString userGroup = QString( "%1:%2" ).arg( m_userName ).arg( m_userName ); QString userGroup = QString( "%1:%2" ).arg( m_userName ).arg( m_userName );
QString homeDir = QString( "/home/%1" ).arg( m_userName ); QString homeDir = QString( "/home/%1" ).arg( m_userName );
pres = CalamaresUtils::System::instance()->targetEnvCommand( commandResult = CalamaresUtils::System::instance()->targetEnvCommand(
{ "chown", "-R", userGroup, homeDir } ); { "chown", "-R", userGroup, homeDir } );
if ( pres.getExitCode() ) if ( commandResult.getExitCode() )
{ {
cError() << "chown failed" << pres.getExitCode(); cError() << "chown failed" << commandResult.getExitCode();
return pres.explainProcess( "chown", 10 ); return commandResult.explainProcess( "chown", 10 );
} }
return Calamares::JobResult::ok(); return Calamares::JobResult::ok();

View File

@ -61,18 +61,6 @@ UsersViewStep::widget()
} }
void
UsersViewStep::next()
{
emit done();
}
void
UsersViewStep::back()
{}
bool bool
UsersViewStep::isNextEnabled() const UsersViewStep::isNextEnabled() const
{ {

View File

@ -43,9 +43,6 @@ public:
QWidget* widget() override; QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override; bool isNextEnabled() const override;
bool isBackEnabled() const override; bool isBackEnabled() const override;

View File

@ -72,18 +72,6 @@ WebViewStep::widget()
} }
void
WebViewStep::next()
{
emit done();
}
void
WebViewStep::back()
{}
bool bool
WebViewStep::isNextEnabled() const WebViewStep::isNextEnabled() const
{ {

View File

@ -50,8 +50,6 @@ public:
QWidget* widget() override; QWidget* widget() override;
void next() override;
void back() override;
void onActivate() override; void onActivate() override;
bool isNextEnabled() const override; bool isNextEnabled() const override;

View File

@ -15,9 +15,10 @@ endif()
include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui ) include_directories( ${PROJECT_BINARY_DIR}/src/libcalamaresui )
set( CHECKER_SOURCES set( CHECKER_SOURCES
checker/CheckItemWidget.cpp checker/CheckerContainer.cpp
checker/CheckerWidget.cpp checker/ResultWidget.cpp
checker/RequirementsChecker.cpp checker/ResultsListWidget.cpp
checker/GeneralRequirements.cpp
${PARTMAN_SRC} ${PARTMAN_SRC}
) )

View File

@ -22,10 +22,12 @@
#include "ui_WelcomePage.h" #include "ui_WelcomePage.h"
#include "CalamaresVersion.h" #include "CalamaresVersion.h"
#include "checker/RequirementsChecker.h" #include "checker/CheckerContainer.h"
#include "utils/Logger.h" #include "utils/Logger.h"
#include "utils/CalamaresUtilsGui.h" #include "utils/CalamaresUtilsGui.h"
#include "utils/Retranslator.h" #include "utils/Retranslator.h"
#include "modulesystem/ModuleManager.h"
#include "ViewManager.h" #include "ViewManager.h"
#include <QApplication> #include <QApplication>
@ -39,11 +41,14 @@
#include "Branding.h" #include "Branding.h"
WelcomePage::WelcomePage( RequirementsChecker* requirementsChecker, QWidget* parent ) WelcomePage::WelcomePage( QWidget* parent )
: QWidget( parent ) : QWidget( parent )
, ui( new Ui::WelcomePage ) , ui( new Ui::WelcomePage )
, m_requirementsChecker( requirementsChecker ) , m_checkingWidget( new CheckerContainer( this ) )
{ {
connect( Calamares::ModuleManager::instance(), &Calamares::ModuleManager::requirementsResult, m_checkingWidget, &CheckerContainer::requirementsChecked );
connect( Calamares::ModuleManager::instance(), &Calamares::ModuleManager::requirementsComplete, m_checkingWidget, &CheckerContainer::requirementsComplete );
connect( Calamares::ModuleManager::instance(), &Calamares::ModuleManager::requirementsProgress, m_checkingWidget, &CheckerContainer::requirementsProgress );
ui->setupUi( this ); ui->setupUi( this );
ui->verticalLayout->insertSpacing( 1, CalamaresUtils::defaultFontHeight() * 2 ); ui->verticalLayout->insertSpacing( 1, CalamaresUtils::defaultFontHeight() * 2 );
@ -102,7 +107,7 @@ WelcomePage::WelcomePage( RequirementsChecker* requirementsChecker, QWidget* par
mb.exec(); mb.exec();
} ); } );
ui->verticalLayout->insertWidget( 3, m_requirementsChecker->widget() ); ui->verticalLayout->insertWidget( 3, m_checkingWidget);
} }
@ -277,3 +282,8 @@ WelcomePage::focusInEvent( QFocusEvent* e )
ui->languageWidget->setFocus(); ui->languageWidget->setFocus();
e->accept(); e->accept();
} }
bool WelcomePage::verdict() const
{
return m_checkingWidget->verdict();
}

View File

@ -26,26 +26,27 @@ namespace Ui
class WelcomePage; class WelcomePage;
} }
class RequirementsChecker; class CheckerContainer;
class WelcomePage : public QWidget class WelcomePage : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit WelcomePage( RequirementsChecker* requirementsChecker, explicit WelcomePage( QWidget* parent = nullptr );
QWidget* parent = nullptr );
void setUpLinks( bool showSupportUrl, void setUpLinks( bool showSupportUrl,
bool showKnownIssuesUrl, bool showKnownIssuesUrl,
bool showReleaseNotesUrl ); bool showReleaseNotesUrl );
bool verdict() const;
protected: protected:
void focusInEvent( QFocusEvent* e ) override; //choose the child widget to focus void focusInEvent( QFocusEvent* e ) override; //choose the child widget to focus
private: private:
void initLanguages(); void initLanguages();
Ui::WelcomePage* ui; Ui::WelcomePage* ui;
RequirementsChecker* m_requirementsChecker; CheckerContainer* m_checkingWidget;
}; };
#endif // WELCOMEPAGE_H #endif // WELCOMEPAGE_H

View File

@ -20,9 +20,10 @@
#include "WelcomeViewStep.h" #include "WelcomeViewStep.h"
#include "WelcomePage.h" #include "WelcomePage.h"
#include "checker/RequirementsChecker.h" #include "checker/GeneralRequirements.h"
#include "utils/Logger.h"
#include "modulesystem/ModuleManager.h"
#include "utils/Logger.h"
#include <QVariant> #include <QVariant>
@ -30,12 +31,10 @@ CALAMARES_PLUGIN_FACTORY_DEFINITION( WelcomeViewStepFactory, registerPlugin<Welc
WelcomeViewStep::WelcomeViewStep( QObject* parent ) WelcomeViewStep::WelcomeViewStep( QObject* parent )
: Calamares::ViewStep( parent ) : Calamares::ViewStep( parent )
, m_requirementsChecker( new RequirementsChecker( this ) ) , m_requirementsChecker( new GeneralRequirements( this ) )
{ {
emit nextStatusChanged( true ); connect( Calamares::ModuleManager::instance(), &Calamares::ModuleManager::requirementsComplete, this, &WelcomeViewStep::nextStatusChanged );
m_widget = new WelcomePage( m_requirementsChecker ); m_widget = new WelcomePage();
connect( m_requirementsChecker, &RequirementsChecker::verdictChanged,
this, &WelcomeViewStep::nextStatusChanged );
} }
@ -60,22 +59,10 @@ WelcomeViewStep::widget()
} }
void
WelcomeViewStep::next()
{
emit done();
}
void
WelcomeViewStep::back()
{}
bool bool
WelcomeViewStep::isNextEnabled() const WelcomeViewStep::isNextEnabled() const
{ {
return m_requirementsChecker->verdict(); return m_widget->verdict();
} }
@ -100,10 +87,10 @@ WelcomeViewStep::isAtEnd() const
} }
QList< Calamares::job_ptr > Calamares::JobList
WelcomeViewStep::jobs() const WelcomeViewStep::jobs() const
{ {
return QList< Calamares::job_ptr >(); return Calamares::JobList();
} }
@ -135,3 +122,7 @@ WelcomeViewStep::setConfigurationMap( const QVariantMap& configurationMap )
"module configuration."; "module configuration.";
} }
Calamares::RequirementsList WelcomeViewStep::checkRequirements()
{
return m_requirementsChecker->checkRequirements();
}

View File

@ -21,6 +21,7 @@
#include <QObject> #include <QObject>
#include <modulesystem/Requirement.h>
#include <utils/PluginFactory.h> #include <utils/PluginFactory.h>
#include <viewpages/ViewStep.h> #include <viewpages/ViewStep.h>
@ -29,7 +30,7 @@
#include <QVariantMap> #include <QVariantMap>
class WelcomePage; class WelcomePage;
class RequirementsChecker; class GeneralRequirements;
class PLUGINDLLEXPORT WelcomeViewStep : public Calamares::ViewStep class PLUGINDLLEXPORT WelcomeViewStep : public Calamares::ViewStep
{ {
@ -43,23 +44,21 @@ public:
QWidget* widget() override; QWidget* widget() override;
void next() override;
void back() override;
bool isNextEnabled() const override; bool isNextEnabled() const override;
bool isBackEnabled() const override; bool isBackEnabled() const override;
bool isAtBeginning() const override; bool isAtBeginning() const override;
bool isAtEnd() const override; bool isAtEnd() const override;
QList< Calamares::job_ptr > jobs() const override; Calamares::JobList jobs() const override;
void setConfigurationMap( const QVariantMap& configurationMap ) override; void setConfigurationMap( const QVariantMap& configurationMap ) override;
Calamares::RequirementsList checkRequirements() override;
private: private:
WelcomePage* m_widget; WelcomePage* m_widget;
GeneralRequirements* m_requirementsChecker;
RequirementsChecker* m_requirementsChecker;
}; };
CALAMARES_PLUGIN_FACTORY_DECLARATION( WelcomeViewStepFactory ) CALAMARES_PLUGIN_FACTORY_DECLARATION( WelcomeViewStepFactory )

View File

@ -0,0 +1,83 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2017, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, 2019, Adriaan de Groot <groot@kde.org>
* Copyright 2017, Gabriel Craciunescu <crazy@frugalware.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/>.
*/
/* Based on code extracted from RequirementsChecker.cpp */
#include "CheckerContainer.h"
#include "ResultsListWidget.h"
#include "utils/CalamaresUtilsGui.h"
#include "utils/Logger.h"
#include "utils/Retranslator.h"
#include "widgets/WaitingWidget.h"
CheckerContainer::CheckerContainer( QWidget* parent )
: QWidget( parent )
, m_waitingWidget( new WaitingWidget( QString(), this ) )
, m_checkerWidget( nullptr )
, m_verdict( false )
{
QBoxLayout* mainLayout = new QHBoxLayout;
setLayout( mainLayout );
CalamaresUtils::unmarginLayout( mainLayout );
mainLayout->addWidget( m_waitingWidget );
CALAMARES_RETRANSLATE(
if ( m_waitingWidget )
m_waitingWidget->setText( tr( "Gathering system information..." ) );
)
}
CheckerContainer::~CheckerContainer()
{
delete m_waitingWidget;
delete m_checkerWidget;
}
void CheckerContainer::requirementsComplete( bool ok )
{
layout()->removeWidget( m_waitingWidget );
m_waitingWidget->deleteLater();
m_waitingWidget = nullptr; // Don't delete in destructor
m_checkerWidget = new ResultsListWidget( this );
m_checkerWidget->init( m_requirements );
layout()->addWidget( m_checkerWidget );
m_verdict = ok;
}
void CheckerContainer::requirementsChecked(const Calamares::RequirementsList& l)
{
m_requirements.append( l );
}
void CheckerContainer::requirementsProgress(const QString& message)
{
if ( m_waitingWidget )
m_waitingWidget->setText( message );
}
bool CheckerContainer::verdict() const
{
return m_verdict;
}

View File

@ -0,0 +1,64 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2017, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, Adriaan de Groot <groot@kde.org>
* Copyright 2017, Gabriel Craciunescu <crazy@frugalware.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/>.
*/
/* Based on code extracted from RequirementsChecker.cpp */
#ifndef CHECKERCONTAINER_H
#define CHECKERCONTAINER_H
#include <QWidget>
#include "modulesystem/Requirement.h"
class ResultsListWidget;
class WaitingWidget;
/**
* A widget that collects requirements results; until the results are
* all in, displays a spinner / waiting widget. Then it switches to
* a (list) diplay of the results, plus some explanation of the
* overall state of the entire list of results.
*/
class CheckerContainer : public QWidget
{
Q_OBJECT
public:
explicit CheckerContainer( QWidget* parent = nullptr );
virtual ~CheckerContainer();
bool verdict() const;
public slots:
void requirementsChecked( const Calamares::RequirementsList& );
/** @brief All the requirements are complete, switch to list view */
void requirementsComplete( bool );
void requirementsProgress( const QString& message );
protected:
WaitingWidget *m_waitingWidget;
ResultsListWidget *m_checkerWidget;
Calamares::RequirementsList m_requirements;
bool m_verdict;
} ;
#endif

View File

@ -18,11 +18,12 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>. * along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "RequirementsChecker.h" #include "GeneralRequirements.h"
#include "CheckerWidget.h" #include "CheckerContainer.h"
#include "partman_devices.h" #include "partman_devices.h"
#include "modulesystem/Requirement.h"
#include "widgets/WaitingWidget.h" #include "widgets/WaitingWidget.h"
#include "utils/CalamaresUtilsGui.h" #include "utils/CalamaresUtilsGui.h"
#include "utils/Logger.h" #include "utils/Logger.h"
@ -30,6 +31,7 @@
#include "utils/CalamaresUtilsSystem.h" #include "utils/CalamaresUtilsSystem.h"
#include "utils/Units.h" #include "utils/Units.h"
#include "JobQueue.h" #include "JobQueue.h"
#include "GlobalStorage.h" #include "GlobalStorage.h"
@ -51,29 +53,17 @@
#include <unistd.h> //geteuid #include <unistd.h> //geteuid
RequirementsChecker::RequirementsChecker( QObject* parent ) GeneralRequirements::GeneralRequirements( QObject* parent )
: QObject( parent ) : QObject( parent )
, m_widget( new QWidget() )
, m_requiredStorageGB( -1 ) , m_requiredStorageGB( -1 )
, m_requiredRamGB( -1 ) , m_requiredRamGB( -1 )
, m_actualWidget( new CheckerWidget() )
, m_verdict( false )
{ {
QBoxLayout* mainLayout = new QHBoxLayout; }
m_widget->setLayout( mainLayout );
CalamaresUtils::unmarginLayout( mainLayout );
WaitingWidget* waitingWidget = new WaitingWidget( QString() ); Calamares::RequirementsList GeneralRequirements::checkRequirements()
mainLayout->addWidget( waitingWidget ); {
CALAMARES_RETRANSLATE( waitingWidget->setText( tr( "Gathering system information..." ) ); ) QSize availableSize = qApp->desktop()->availableGeometry().size();
QSize availableSize = qApp->desktop()->availableGeometry( m_widget ).size();
QTimer* timer = new QTimer;
timer->setSingleShot( true );
connect( timer, &QTimer::timeout,
[=]()
{
bool enoughStorage = false; bool enoughStorage = false;
bool enoughRam = false; bool enoughRam = false;
bool hasPower = false; bool hasPower = false;
@ -101,15 +91,14 @@ RequirementsChecker::RequirementsChecker( QObject* parent )
isRoot = checkIsRoot(); isRoot = checkIsRoot();
using TR = Logger::DebugRow<const char *, bool>; using TR = Logger::DebugRow<const char *, bool>;
cDebug() << "GeneralRequirements output:"
cDebug() << "RequirementsChecker output:"
<< TR("enoughStorage", enoughStorage) << TR("enoughStorage", enoughStorage)
<< TR("enoughRam", enoughRam) << TR("enoughRam", enoughRam)
<< TR("hasPower", hasPower) << TR("hasPower", hasPower)
<< TR("hasInternet", hasInternet) << TR("hasInternet", hasInternet)
<< TR("isRoot", isRoot); << TR("isRoot", isRoot);
QList< PrepareEntry > checkEntries; Calamares::RequirementsList checkEntries;
foreach ( const QString& entry, m_entriesToCheck ) foreach ( const QString& entry, m_entriesToCheck )
{ {
if ( entry == "storage" ) if ( entry == "storage" )
@ -165,52 +154,12 @@ RequirementsChecker::RequirementsChecker( QObject* parent )
false false
} ); } );
} }
return checkEntries;
m_actualWidget->init( checkEntries );
m_widget->layout()->removeWidget( waitingWidget );
waitingWidget->deleteLater();
m_actualWidget->setParent( m_widget );
m_widget->layout()->addWidget( m_actualWidget );
bool canGoNext = true;
foreach ( const PrepareEntry& entry, checkEntries )
{
if ( !entry.checked && entry.required )
{
canGoNext = false;
break;
}
}
m_verdict = canGoNext;
emit verdictChanged( m_verdict );
if ( canGoNext )
detectFirmwareType();
timer->deleteLater();
} );
timer->start( 0 );
emit verdictChanged( true );
}
RequirementsChecker::~RequirementsChecker()
{
if ( m_widget && m_widget->parent() == nullptr )
m_widget->deleteLater();
}
QWidget*
RequirementsChecker::widget() const
{
return m_widget;
} }
void void
RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap ) GeneralRequirements::setConfigurationMap( const QVariantMap& configurationMap )
{ {
bool incompleteConfiguration = false; bool incompleteConfiguration = false;
@ -222,7 +171,7 @@ RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
} }
else else
{ {
cWarning() << "RequirementsChecker entry 'check' is incomplete."; cWarning() << "GeneralRequirements entry 'check' is incomplete.";
incompleteConfiguration = true; incompleteConfiguration = true;
} }
@ -234,14 +183,25 @@ RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
} }
else else
{ {
cWarning() << "RequirementsChecker entry 'required' is incomplete."; cWarning() << "GeneralRequirements entry 'required' is incomplete.";
incompleteConfiguration = true; incompleteConfiguration = true;
} }
#ifdef WITHOUT_LIBPARTED
if ( m_entriesToCheck.contains( "storage" ) || m_entriesToRequire.contains( "storage" ) )
{
// Warn, but also drop the required bit because otherwise installation
// will be impossible (because the check always returns false).
cWarning() << "GeneralRequirements checks 'storage' but libparted is disabled.";
m_entriesToCheck.removeAll( "storage" );
m_entriesToRequire.removeAll( "storage" );
}
#endif
// Help out with consistency, but don't fix // Help out with consistency, but don't fix
for ( const auto& r : m_entriesToRequire ) for ( const auto& r : m_entriesToRequire )
if ( !m_entriesToCheck.contains( r ) ) if ( !m_entriesToCheck.contains( r ) )
cWarning() << "RequirementsChecker requires" << r << "but does not check it."; cWarning() << "GeneralRequirements requires" << r << "but does not check it.";
if ( configurationMap.contains( "requiredStorage" ) && if ( configurationMap.contains( "requiredStorage" ) &&
( configurationMap.value( "requiredStorage" ).type() == QVariant::Double || ( configurationMap.value( "requiredStorage" ).type() == QVariant::Double ||
@ -251,7 +211,7 @@ RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
m_requiredStorageGB = configurationMap.value( "requiredStorage" ).toDouble( &ok ); m_requiredStorageGB = configurationMap.value( "requiredStorage" ).toDouble( &ok );
if ( !ok ) if ( !ok )
{ {
cWarning() << "RequirementsChecker entry 'requiredStorage' is invalid."; cWarning() << "GeneralRequirements entry 'requiredStorage' is invalid.";
m_requiredStorageGB = 3.; m_requiredStorageGB = 3.;
} }
@ -259,7 +219,7 @@ RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
} }
else else
{ {
cWarning() << "RequirementsChecker entry 'requiredStorage' is missing."; cWarning() << "GeneralRequirements entry 'requiredStorage' is missing.";
m_requiredStorageGB = 3.; m_requiredStorageGB = 3.;
incompleteConfiguration = true; incompleteConfiguration = true;
} }
@ -272,14 +232,14 @@ RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
m_requiredRamGB = configurationMap.value( "requiredRam" ).toDouble( &ok ); m_requiredRamGB = configurationMap.value( "requiredRam" ).toDouble( &ok );
if ( !ok ) if ( !ok )
{ {
cWarning() << "RequirementsChecker entry 'requiredRam' is invalid."; cWarning() << "GeneralRequirements entry 'requiredRam' is invalid.";
m_requiredRamGB = 1.; m_requiredRamGB = 1.;
incompleteConfiguration = true; incompleteConfiguration = true;
} }
} }
else else
{ {
cWarning() << "RequirementsChecker entry 'requiredRam' is missing."; cWarning() << "GeneralRequirements entry 'requiredRam' is missing.";
m_requiredRamGB = 1.; m_requiredRamGB = 1.;
incompleteConfiguration = true; incompleteConfiguration = true;
} }
@ -291,7 +251,7 @@ RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
if ( m_checkHasInternetUrl.isEmpty() || if ( m_checkHasInternetUrl.isEmpty() ||
!QUrl( m_checkHasInternetUrl ).isValid() ) !QUrl( m_checkHasInternetUrl ).isValid() )
{ {
cWarning() << "RequirementsChecker entry 'internetCheckUrl' is invalid in welcome.conf" << m_checkHasInternetUrl cWarning() << "GeneralRequirements entry 'internetCheckUrl' is invalid in welcome.conf" << m_checkHasInternetUrl
<< "reverting to default (http://example.com)."; << "reverting to default (http://example.com).";
m_checkHasInternetUrl = "http://example.com"; m_checkHasInternetUrl = "http://example.com";
incompleteConfiguration = true; incompleteConfiguration = true;
@ -299,7 +259,7 @@ RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
} }
else else
{ {
cWarning() << "RequirementsChecker entry 'internetCheckUrl' is undefined in welcome.conf," cWarning() << "GeneralRequirements entry 'internetCheckUrl' is undefined in welcome.conf,"
"reverting to default (http://example.com)."; "reverting to default (http://example.com).";
m_checkHasInternetUrl = "http://example.com"; m_checkHasInternetUrl = "http://example.com";
@ -308,24 +268,17 @@ RequirementsChecker::setConfigurationMap( const QVariantMap& configurationMap )
if ( incompleteConfiguration ) if ( incompleteConfiguration )
{ {
cWarning() << "RequirementsChecker configuration map:" << Logger::DebugMap( configurationMap ); cWarning() << "GeneralRequirements configuration map:" << Logger::DebugMap( configurationMap );
} }
} }
bool bool
RequirementsChecker::verdict() const GeneralRequirements::checkEnoughStorage( qint64 requiredSpace )
{
return m_verdict;
}
bool
RequirementsChecker::checkEnoughStorage( qint64 requiredSpace )
{ {
#ifdef WITHOUT_LIBPARTED #ifdef WITHOUT_LIBPARTED
Q_UNUSED( requiredSpace ); Q_UNUSED( requiredSpace );
cWarning() << "RequirementsChecker is configured without libparted."; cWarning() << "GeneralRequirements is configured without libparted.";
return false; return false;
#else #else
return check_big_enough( requiredSpace ); return check_big_enough( requiredSpace );
@ -334,7 +287,7 @@ RequirementsChecker::checkEnoughStorage( qint64 requiredSpace )
bool bool
RequirementsChecker::checkEnoughRam( qint64 requiredRam ) GeneralRequirements::checkEnoughRam( qint64 requiredRam )
{ {
// Ignore the guesstimate-factor; we get an under-estimate // Ignore the guesstimate-factor; we get an under-estimate
// which is probably the usable RAM for programs. // which is probably the usable RAM for programs.
@ -344,7 +297,7 @@ RequirementsChecker::checkEnoughRam( qint64 requiredRam )
bool bool
RequirementsChecker::checkBatteryExists() GeneralRequirements::checkBatteryExists()
{ {
const QFileInfo basePath( "/sys/class/power_supply" ); const QFileInfo basePath( "/sys/class/power_supply" );
@ -370,7 +323,7 @@ RequirementsChecker::checkBatteryExists()
bool bool
RequirementsChecker::checkHasPower() GeneralRequirements::checkHasPower()
{ {
const QString UPOWER_SVC_NAME( "org.freedesktop.UPower" ); const QString UPOWER_SVC_NAME( "org.freedesktop.UPower" );
const QString UPOWER_INTF_NAME( "org.freedesktop.UPower" ); const QString UPOWER_INTF_NAME( "org.freedesktop.UPower" );
@ -401,10 +354,10 @@ RequirementsChecker::checkHasPower()
bool bool
RequirementsChecker::checkHasInternet() GeneralRequirements::checkHasInternet()
{ {
// default to true in the QNetworkAccessManager::UnknownAccessibility case // default to true in the QNetworkAccessManager::UnknownAccessibility case
QNetworkAccessManager qnam( this ); QNetworkAccessManager qnam;
bool hasInternet = qnam.networkAccessible() == QNetworkAccessManager::Accessible; bool hasInternet = qnam.networkAccessible() == QNetworkAccessManager::Accessible;
if ( !hasInternet && qnam.networkAccessible() == QNetworkAccessManager::UnknownAccessibility ) if ( !hasInternet && qnam.networkAccessible() == QNetworkAccessManager::UnknownAccessibility )
@ -425,15 +378,7 @@ RequirementsChecker::checkHasInternet()
bool bool
RequirementsChecker::checkIsRoot() GeneralRequirements::checkIsRoot()
{ {
return !geteuid(); return !geteuid();
} }
void
RequirementsChecker::detectFirmwareType()
{
QString fwType = QFile::exists( "/sys/firmware/efi/efivars" ) ? "efi" : "bios";
Calamares::JobQueue::instance()->globalStorage()->insert( "firmwareType", fwType );
}

View File

@ -17,52 +17,23 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>. * along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef REQUIREMENTSCHECKER_H #ifndef GENERALREQUIREMENTS_H
#define REQUIREMENTSCHECKER_H #define GENERALREQUIREMENTS_H
#include <QObject> #include <QObject>
#include <QStringList> #include <QStringList>
#include <functional> #include "modulesystem/Requirement.h"
class CheckerWidget; class GeneralRequirements : public QObject
class QWidget;
/**
* An indication of a requirement, which is checked in preparation
* for system installation. An entry has a name and some explanation,
* as well as three meaningful states:
* - checked = true, the requirement is met (green)
* - checked = false, the requirement is not met
* - required = false, warn about it (yellow), no failure
* - required = true, prohibit installation (red)
*/
struct PrepareEntry
{
QString name;
std::function< QString() > enumerationText; //Partial string, inserted in a
//list of requirements to satisfy.
std::function< QString() > negatedText; //Complete sentence about this requirement
//not having been met.
bool checked;
bool required;
};
class RequirementsChecker : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit RequirementsChecker( QObject* parent = nullptr ); explicit GeneralRequirements( QObject* parent = nullptr );
virtual ~RequirementsChecker();
QWidget* widget() const;
void setConfigurationMap( const QVariantMap& configurationMap ); void setConfigurationMap( const QVariantMap& configurationMap );
bool verdict() const; Calamares::RequirementsList checkRequirements();
signals:
void verdictChanged( bool );
private: private:
QStringList m_entriesToCheck; QStringList m_entriesToCheck;
@ -74,15 +45,10 @@ private:
bool checkHasPower(); bool checkHasPower();
bool checkHasInternet(); bool checkHasInternet();
bool checkIsRoot(); bool checkIsRoot();
void detectFirmwareType();
QWidget* m_widget;
qreal m_requiredStorageGB; qreal m_requiredStorageGB;
qreal m_requiredRamGB; qreal m_requiredRamGB;
QString m_checkHasInternetUrl; QString m_checkHasInternetUrl;
CheckerWidget* m_actualWidget;
bool m_verdict;
}; };
#endif // REQUIREMENTSCHECKER_H #endif // REQUIREMENTSCHECKER_H

View File

@ -1,7 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org> * Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, Adriaan de Groot <groot@kde.org> * Copyright 2017, 2019, Adriaan de Groot <groot@kde.org>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -17,7 +17,7 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>. * along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "CheckItemWidget.h" #include "ResultWidget.h"
#include "utils/CalamaresUtilsGui.h" #include "utils/CalamaresUtilsGui.h"
#include "utils/Logger.h" #include "utils/Logger.h"
@ -26,12 +26,13 @@
static inline void setCondition( QLabel* label, CalamaresUtils::ImageType t ) static inline void setCondition( QLabel* label, CalamaresUtils::ImageType t )
{ {
label->setPixmap( CalamaresUtils::defaultPixmap( t, label->setPixmap(
CalamaresUtils::defaultPixmap( t,
CalamaresUtils::Original, CalamaresUtils::Original,
QSize( label->height(), label->height() ) ) ); QSize( label->height(), label->height() ) ) );
} }
CheckItemWidget::CheckItemWidget( bool checked, ResultWidget::ResultWidget( bool satisfied,
bool required, bool required,
QWidget* parent ) QWidget* parent )
: QWidget( parent ) : QWidget( parent )
@ -46,11 +47,9 @@ CheckItemWidget::CheckItemWidget( bool checked,
mainLayout->addWidget( m_textLabel ); mainLayout->addWidget( m_textLabel );
m_textLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); m_textLabel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
if ( checked ) if ( satisfied )
// Condition is satisfied
setCondition( m_iconLabel, CalamaresUtils::StatusOk ); setCondition( m_iconLabel, CalamaresUtils::StatusOk );
else else if ( required )
if ( required )
setCondition( m_iconLabel, CalamaresUtils::StatusError ); setCondition( m_iconLabel, CalamaresUtils::StatusError );
else else
setCondition( m_iconLabel, CalamaresUtils::StatusWarning ); setCondition( m_iconLabel, CalamaresUtils::StatusWarning );
@ -58,7 +57,7 @@ CheckItemWidget::CheckItemWidget( bool checked,
void void
CheckItemWidget::setText( const QString& text ) ResultWidget::setText( const QString& text )
{ {
m_textLabel->setText( text ); m_textLabel->setText( text );
} }

View File

@ -0,0 +1,51 @@
/* === This file is part of Calamares - <https://github.com/calamares> ===
*
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, 2019, 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/>.
*/
#ifndef CHECKER_RESULTWIDGET_H
#define CHECKER_RESULTWIDGET_H
#include <QLabel>
/**
* @brief Displays the results of a single check.
*
* Widget to insert into a ResultListWidget to display an iconic status
* (warning or failure when the check is not satisfied) along with
* descriptive test.
*/
class ResultWidget : public QWidget
{
Q_OBJECT
public:
/**
* @brief Create widget with results of a check.
*
* Use setText() to set up the text of the widget.
*/
explicit ResultWidget( bool satisfied, bool required,
QWidget* parent = nullptr );
/// @brief Set the displayed description of the check.
void setText( const QString& text );
private:
QLabel* m_textLabel;
QLabel* m_iconLabel;
};
#endif // CHECKER_RESULTWIDGET_H

View File

@ -1,7 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org> * Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2017, Adriaan de Groot <groot@kde.org> * Copyright 2017, 2019, Adriaan de Groot <groot@kde.org>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -17,9 +17,9 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>. * along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "CheckerWidget.h" #include "ResultsListWidget.h"
#include "CheckItemWidget.h" #include "ResultWidget.h"
#include "Branding.h" #include "Branding.h"
#include "utils/CalamaresUtilsGui.h" #include "utils/CalamaresUtilsGui.h"
@ -33,7 +33,7 @@
#include <QLabel> #include <QLabel>
CheckerWidget::CheckerWidget( QWidget* parent ) ResultsListWidget::ResultsListWidget( QWidget* parent )
: QWidget( parent ) : QWidget( parent )
{ {
setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
@ -53,25 +53,23 @@ CheckerWidget::CheckerWidget( QWidget* parent )
void void
CheckerWidget::init( const QList< PrepareEntry >& checkEntries ) ResultsListWidget::init( const Calamares::RequirementsList& checkEntries )
{ {
bool allChecked = true; bool allChecked = true;
bool requirementsSatisfied = true; bool requirementsSatisfied = true;
for ( const PrepareEntry& entry : checkEntries ) for ( const auto& entry : checkEntries )
{ {
if ( !entry.checked ) if ( !entry.satisfied )
{ {
CheckItemWidget* ciw = new CheckItemWidget( entry.checked, entry.required ); ResultWidget* ciw = new ResultWidget( entry.satisfied, entry.mandatory );
CALAMARES_RETRANSLATE( ciw->setText( entry.negatedText() ); ) CALAMARES_RETRANSLATE( ciw->setText( entry.negatedText() ); )
m_entriesLayout->addWidget( ciw ); m_entriesLayout->addWidget( ciw );
ciw->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); ciw->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );
allChecked = false; allChecked = false;
if ( entry.required ) if ( entry.mandatory )
{
requirementsSatisfied = false; requirementsSatisfied = false;
}
ciw->setAutoFillBackground( true ); ciw->setAutoFillBackground( true );
QPalette pal( ciw->palette() ); QPalette pal( ciw->palette() );
pal.setColor( QPalette::Background, Qt::white ); pal.setColor( QPalette::Background, Qt::white );
@ -131,7 +129,7 @@ CheckerWidget::init( const QList< PrepareEntry >& checkEntries )
QLabel* imageLabel; QLabel* imageLabel;
if ( Calamares::Branding::instance()->welcomeExpandingLogo() ) if ( Calamares::Branding::instance()->welcomeExpandingLogo() )
{ {
FixedAspectRatioLabel *p = new FixedAspectRatioLabel; FixedAspectRatioLabel* p = new FixedAspectRatioLabel;
p->setPixmap( theImage ); p->setPixmap( theImage );
imageLabel = p; imageLabel = p;
} }
@ -155,14 +153,12 @@ CheckerWidget::init( const QList< PrepareEntry >& checkEntries )
) )
} }
else else
{
m_mainLayout->addStretch(); m_mainLayout->addStretch();
}
} }
void void
CheckerWidget::showDetailsDialog( const QList< PrepareEntry >& checkEntries ) ResultsListWidget::showDetailsDialog( const Calamares::RequirementsList& checkEntries )
{ {
QDialog* detailsDialog = new QDialog( this ); QDialog* detailsDialog = new QDialog( this );
QBoxLayout* mainLayout = new QVBoxLayout; QBoxLayout* mainLayout = new QVBoxLayout;
@ -177,12 +173,12 @@ CheckerWidget::showDetailsDialog( const QList< PrepareEntry >& checkEntries )
CalamaresUtils::unmarginLayout( entriesLayout ); CalamaresUtils::unmarginLayout( entriesLayout );
mainLayout->addLayout( entriesLayout ); mainLayout->addLayout( entriesLayout );
for ( const PrepareEntry& entry : checkEntries ) for ( const auto& entry : checkEntries )
{ {
if ( entry.enumerationText().isEmpty() ) if ( !entry.hasDetails() )
continue; continue;
CheckItemWidget* ciw = new CheckItemWidget( entry.checked, entry.required ); ResultWidget* ciw = new ResultWidget( entry.satisfied, entry.mandatory );
CALAMARES_RETRANSLATE( ciw->setText( entry.enumerationText() ); ) CALAMARES_RETRANSLATE( ciw->setText( entry.enumerationText() ); )
entriesLayout->addWidget( ciw ); entriesLayout->addWidget( ciw );
ciw->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred ); ciw->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Preferred );

View File

@ -1,6 +1,7 @@
/* === This file is part of Calamares - <https://github.com/calamares> === /* === This file is part of Calamares - <https://github.com/calamares> ===
* *
* Copyright 2014-2015, Teo Mrnjavac <teo@kde.org> * Copyright 2014-2015, Teo Mrnjavac <teo@kde.org>
* Copyright 2019, Adriaan de Groot <groot@kde.org>
* *
* Calamares is free software: you can redistribute it and/or modify * Calamares is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -16,28 +17,28 @@
* along with Calamares. If not, see <http://www.gnu.org/licenses/>. * along with Calamares. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef CHECKERWIDGET_H #ifndef CHECKER_RESULTSLISTWIDGET_H
#define CHECKERWIDGET_H #define CHECKER_RESULTSLISTWIDGET_H
#include "RequirementsChecker.h" #include "modulesystem/Requirement.h"
#include <QBoxLayout> #include <QBoxLayout>
#include <QWidget> #include <QWidget>
class CheckerWidget : public QWidget class ResultsListWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit CheckerWidget( QWidget* parent = nullptr ); explicit ResultsListWidget( QWidget* parent = nullptr );
void init( const QList< PrepareEntry >& checkEntries ); void init( const Calamares::RequirementsList& checkEntries );
private: private:
void showDetailsDialog( const QList< PrepareEntry >& checkEntries ); void showDetailsDialog( const Calamares::RequirementsList& checkEntries );
QBoxLayout* m_mainLayout; QBoxLayout* m_mainLayout;
QBoxLayout* m_entriesLayout; QBoxLayout* m_entriesLayout;
int m_paddingSize; int m_paddingSize;
}; };
#endif // CHECKERWIDGET_H #endif // CHECKER_RESULTSLISTWIDGET_H

View File

@ -1,18 +1,37 @@
# Configuration for the welcome module. The welcome page
# displays some information from the branding file.
# Which parts it displays can be configured through
# the show* variables.
#
# In addition to displaying the welcome page, this module
# can check requirements for installation.
--- ---
# Display settings for various buttons on the welcome page.
showSupportUrl: true showSupportUrl: true
showKnownIssuesUrl: true showKnownIssuesUrl: true
showReleaseNotesUrl: true showReleaseNotesUrl: true
# Requirements checking. These are general, generic, things
# that are checked. They may not match with the actual requirements
# imposed by other modules in the system.
requirements: requirements:
# Amount of available disk, in GB. Floating-point is allowed here.
# Note that this does not account for *usable* disk, so it is possible
# to pass this requirement, yet have no space to install to.
requiredStorage: 5.5 requiredStorage: 5.5
# Amount of available RAM, in GB. Floating-point is allowed here.
requiredRam: 1.0 requiredRam: 1.0
# To check for internet connectivity, Calamares does a HTTP GET
# on this URL; on success (e.g. HTTP code 200) internet is OK.
internetCheckUrl: http://google.com internetCheckUrl: http://google.com
# List conditions to check. Each listed condition will be # List conditions to check. Each listed condition will be
# probed in some way, and yields true or false according to # probed in some way, and yields true or false according to
# the host system satisfying the condition. # the host system satisfying the condition.
# #
# This sample file lists all the conditions that are know. # This sample file lists all the conditions that are known.
check: check:
- storage - storage
- ram - ram
@ -20,7 +39,7 @@ requirements:
- internet - internet
- root - root
- screen - screen
# List conditions that must be satisfied (from the list # List conditions that **must** be satisfied (from the list
# of conditions, above) for installation to proceed. # of conditions, above) for installation to proceed.
# If any of these conditions are not met, the user cannot # If any of these conditions are not met, the user cannot
# continue past the welcome page. # continue past the welcome page.