diff --git a/CMakeLists.txt b/CMakeLists.txt index 5eed360f8..1fa59421c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,13 +36,29 @@ cmake_minimum_required( VERSION 3.2 ) ### OPTIONS # -option( INSTALL_CONFIG "Install configuration files" ON ) +option( INSTALL_CONFIG "Install configuration files" OFF ) option( INSTALL_POLKIT "Install Polkit configuration" ON ) option( BUILD_TESTING "Build the testing tree." ON ) option( WITH_PYTHON "Enable Python modules API (requires Boost.Python)." ON ) option( WITH_PYTHONQT "Enable next generation Python modules API (experimental, requires PythonQt)." ON ) option( WITH_KF5Crash "Enable crash reporting with KCrash." ON ) +### USE_* +# +# By convention, when there are multiple modules that implement similar +# functionality, and it only makes sense to have **at most one** of them +# enabled at any time, those modules are named -. +# For example, services-systemd and services-openrc. +# +# Setting up SKIP_MODULES to ignore "the ones you don't want" can be +# annoying and error-prone (e.g. if a new module shows up). The USE_* +# modules provide a way to do automatic selection. To pick exactly +# one of the implementations from group , set USE_ to the +# name of the implementation. If USE_ is unset, or empty, then +# all the implementations are enabled (this just means they are +# **available** to `settings.conf`, not that they are used). +# +# Currently, no USE_ variables exist. ### Calamares application info # diff --git a/src/libcalamares/Settings.cpp b/src/libcalamares/Settings.cpp index 1fdfc6daa..8fd4eeac3 100644 --- a/src/libcalamares/Settings.cpp +++ b/src/libcalamares/Settings.cpp @@ -212,7 +212,7 @@ Settings::customModuleInstances() const } -QList< QPair< ModuleAction, QStringList > > +Settings::ModuleSequence Settings::modulesSequence() const { return m_modulesSequence; diff --git a/src/libcalamares/Settings.h b/src/libcalamares/Settings.h index 2330f5747..4da65f710 100644 --- a/src/libcalamares/Settings.h +++ b/src/libcalamares/Settings.h @@ -47,7 +47,8 @@ public: using InstanceDescriptionList = QList< InstanceDescription >; InstanceDescriptionList customModuleInstances() const; - QList< QPair< ModuleAction, QStringList > > modulesSequence() const; + using ModuleSequence = QList< QPair< ModuleAction, QStringList > >; + ModuleSequence modulesSequence() const; QString brandingComponentName() const; @@ -63,7 +64,7 @@ private: QStringList m_modulesSearchPaths; InstanceDescriptionList m_customModuleInstances; - QList< QPair< ModuleAction, QStringList > > m_modulesSequence; + ModuleSequence m_modulesSequence; QString m_brandingComponentName; diff --git a/src/libcalamaresui/modulesystem/Module.cpp b/src/libcalamaresui/modulesystem/Module.cpp index c820b98b3..ed1cb33ea 100644 --- a/src/libcalamaresui/modulesystem/Module.cpp +++ b/src/libcalamaresui/modulesystem/Module.cpp @@ -224,13 +224,6 @@ Module::instanceKey() const } -QStringList -Module::requiredModules() const -{ - return m_requiredModules; -} - - QString Module::location() const { @@ -286,7 +279,6 @@ void Module::initFrom( const QVariantMap& moduleDescriptor ) { m_name = moduleDescriptor.value( "name" ).toString(); - if ( moduleDescriptor.contains( EMERGENCY ) ) m_maybe_emergency = moduleDescriptor[ EMERGENCY ].toBool(); } diff --git a/src/libcalamaresui/modulesystem/Module.h b/src/libcalamaresui/modulesystem/Module.h index 18c2e4cbe..f89c9eedb 100644 --- a/src/libcalamaresui/modulesystem/Module.h +++ b/src/libcalamaresui/modulesystem/Module.h @@ -106,13 +106,6 @@ public: */ virtual QString instanceKey() const final; - /** - * @brief requiredModules a list of names of modules required by this one. - * @return the list of names. - * The module dependencies system is currently incomplete and unused. - */ - virtual QStringList requiredModules() const; - /** * @brief location returns the full path of this module's directory. * @return the path. @@ -198,7 +191,6 @@ private: void loadConfigurationFile( const QString& configFileName ); //throws YAML::Exception QString m_name; - QStringList m_requiredModules; QString m_directory; QString m_instanceId; diff --git a/src/libcalamaresui/modulesystem/ModuleManager.cpp b/src/libcalamaresui/modulesystem/ModuleManager.cpp index ed1e52f9f..86d97d2db 100644 --- a/src/libcalamaresui/modulesystem/ModuleManager.cpp +++ b/src/libcalamaresui/modulesystem/ModuleManager.cpp @@ -126,7 +126,6 @@ ModuleManager::doInit() } // At this point m_availableModules is filled with whatever was found in the // search paths. - checkDependencies(); emit initDone(); } @@ -176,11 +175,11 @@ ModuleManager::loadModules() { QTimer::singleShot( 0, this, [ this ]() { - QStringList failedModules; + QStringList failedModules = checkDependencies(); Settings::InstanceDescriptionList customInstances = Settings::instance()->customModuleInstances(); - const auto modulesSequence = Settings::instance()->modulesSequence(); + const auto modulesSequence = failedModules.isEmpty() ? Settings::instance()->modulesSequence() : Settings::ModuleSequence(); for ( const auto& modulePhase : modulesSequence ) { ModuleAction currentAction = modulePhase.first; @@ -262,6 +261,14 @@ ModuleManager::loadModules() failedModules.append( instanceKey ); continue; } + + if ( !checkDependencies( *thisModule ) ) + { + // Error message is already printed + failedModules.append( instanceKey ); + continue; + } + // If it's a ViewModule, it also appends the ViewStep to the ViewManager. thisModule->loadSelf(); m_loadedModulesByInstanceKey.insert( instanceKey, thisModule ); @@ -301,24 +308,29 @@ ModuleManager::loadModules() } -void +QStringList ModuleManager::checkDependencies() { + QStringList failed; + // This goes through the map of available modules, and deletes those whose // dependencies are not met, if any. - bool somethingWasRemovedBecauseOfUnmetDependencies = false; forever { + bool somethingWasRemovedBecauseOfUnmetDependencies = false; for ( auto it = m_availableDescriptorsByModuleName.begin(); it != m_availableDescriptorsByModuleName.end(); ++it ) { foreach ( const QString& depName, - ( *it ).value( "requiredModules" ).toStringList() ) + it->value( "requiredModules" ).toStringList() ) { if ( !m_availableDescriptorsByModuleName.contains( depName ) ) { + QString moduleName = it->value( "name" ).toString(); somethingWasRemovedBecauseOfUnmetDependencies = true; m_availableDescriptorsByModuleName.erase( it ); + failed << moduleName; + cWarning() << "Module" << moduleName << "has unknown requirement" << depName; break; } } @@ -328,7 +340,33 @@ ModuleManager::checkDependencies() if ( !somethingWasRemovedBecauseOfUnmetDependencies ) break; } + + return failed; } +bool +ModuleManager::checkDependencies( const Module& m ) +{ + bool allRequirementsFound = true; + QStringList requiredModules = m_availableDescriptorsByModuleName[ m.name() ].value( "requiredModules" ).toStringList(); + + for ( const QString& required : requiredModules ) + { + bool requirementFound = false; + for( const Module* v : m_loadedModulesByInstanceKey ) + if ( required == v->name() ) + { + requirementFound = true; + break; + } + if ( !requirementFound ) + { + cError() << "Module" << m.name() << "requires" << required << "before it in sequence."; + allRequirementsFound = false; + } + } + + return allRequirementsFound; +} } diff --git a/src/libcalamaresui/modulesystem/ModuleManager.h b/src/libcalamaresui/modulesystem/ModuleManager.h index eff09b321..a0edc2528 100644 --- a/src/libcalamaresui/modulesystem/ModuleManager.h +++ b/src/libcalamaresui/modulesystem/ModuleManager.h @@ -90,7 +90,25 @@ private slots: void doInit(); private: - void checkDependencies(); + /** + * Check in a general sense whether the dependencies between + * modules are valid. Returns a list of module names that + * do **not** have their requirements met. + * + * Returns an empty list on success. + * + * Also modifies m_availableDescriptorsByModuleName to remove + * all the entries that fail. + */ + QStringList checkDependencies(); + + /** + * Check for this specific module if its required modules have + * already been loaded (i.e. are in sequence before it). + * + * Returns true if the requirements are met. + */ + bool checkDependencies( const Module& ); QMap< QString, QVariantMap > m_availableDescriptorsByModuleName; QMap< QString, QString > m_moduleDirectoriesByModuleName; diff --git a/src/modules/CMakeLists.txt b/src/modules/CMakeLists.txt index 514d6b4f6..0a8d1db70 100644 --- a/src/modules/CMakeLists.txt +++ b/src/modules/CMakeLists.txt @@ -15,13 +15,34 @@ string( REPLACE " " ";" SKIP_LIST "${SKIP_MODULES}" ) file( GLOB SUBDIRECTORIES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*" ) list( SORT SUBDIRECTORIES ) +set( _use_categories "" ) +set( _found_categories "" ) + foreach( SUBDIRECTORY ${SUBDIRECTORIES} ) list( FIND SKIP_LIST ${SUBDIRECTORY} DO_SKIP ) + set( _skip_reason "user request" ) + # Handle the USE_ variables by looking for subdirectories + # with a - kind of name. + if( SUBDIRECTORY MATCHES "^[a-zA-Z0-9_]+-" ) + string( REGEX REPLACE "^[^-]+-" "" _implementation ${SUBDIRECTORY} ) + string( REGEX REPLACE "-.*" "" _category ${SUBDIRECTORY} ) + if( USE_${_category} ) + list( APPEND _use_categories ${_category} ) + if( "${_implementation}" STREQUAL "${USE_${_category}}" ) + list( APPEND _found_categories ${_category} ) + else() + list( APPEND SKIP_LIST ${SUBDIRECTORY} ) + set( _skip_reason "USE_${_category}=${USE_${_category}}" ) + set( DO_SKIP 1 ) + endif() + endif() + endif() + if( NOT DO_SKIP EQUAL -1 ) message( "${ColorReset}-- Skipping module ${BoldRed}${SUBDIRECTORY}${ColorReset}." ) message( "" ) - list( APPEND LIST_SKIPPED_MODULES "${SUBDIRECTORY} (user request)" ) + list( APPEND LIST_SKIPPED_MODULES "${SUBDIRECTORY} (${_skip_reason})" ) elseif( ( IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIRECTORY}" ) AND ( DO_SKIP EQUAL -1 ) ) set( SKIPPED_MODULES ) @@ -36,5 +57,12 @@ endforeach() # both before and after the feature summary. calamares_explain_skipped_modules( ${LIST_SKIPPED_MODULES} ) +foreach( _category ${_use_categories} ) + list( FIND _found_categories ${_category} _found ) + if ( _found EQUAL -1 ) + message( FATAL_ERROR "USE_${_category} is set to ${USE_${_category}} and no module matches." ) + endif() +endforeach() + include( CalamaresAddTranslations ) add_calamares_python_translations( ${CALAMARES_TRANSLATION_LANGUAGES} ) diff --git a/src/modules/bootloader/bootloader.conf b/src/modules/bootloader/bootloader.conf index ee8680f72..808a5e6d0 100644 --- a/src/modules/bootloader/bootloader.conf +++ b/src/modules/bootloader/bootloader.conf @@ -12,8 +12,10 @@ kernel: "/vmlinuz-linux" img: "/initramfs-linux.img" fallback: "/initramfs-linux-fallback.img" timeout: "10" + # Optionally set the menu entry name and kernel name to use in systemd-boot. # If not specified here, these settings will be taken from branding.desc. +# # bootloaderEntryName: "Generic GNU/Linux" # kernelLine: ", with Stable-Kernel" # fallbackKernelLine: ", with Stable-Kernel (fallback initramfs)" diff --git a/src/modules/displaymanager/displaymanager.conf b/src/modules/displaymanager/displaymanager.conf index 1c30ed637..8f8e9c704 100644 --- a/src/modules/displaymanager/displaymanager.conf +++ b/src/modules/displaymanager/displaymanager.conf @@ -1,3 +1,5 @@ +# Configure one or more display managers (e.g. SDDM) +# with a "best effort" approach. --- #The DM module attempts to set up all the DMs found in this list, in that precise order. #It also sets up autologin, if the feature is enabled in globalstorage. diff --git a/src/modules/dummycpp/dummycpp.conf b/src/modules/dummycpp/dummycpp.conf index c90b6f3b9..1f2e1daee 100644 --- a/src/modules/dummycpp/dummycpp.conf +++ b/src/modules/dummycpp/dummycpp.conf @@ -1,3 +1,6 @@ +# This is a dummy (example) module for C++ Jobs. +# +# The code is the documentation for the configuration file. --- syntax: "YAML map of anything" example: @@ -15,4 +18,4 @@ a_list_of_maps: - "another element" - name: "another item" contents: - - "not much" \ No newline at end of file + - "not much" diff --git a/src/modules/dummypython/dummypython.conf b/src/modules/dummypython/dummypython.conf index fc985089a..c700120e7 100644 --- a/src/modules/dummypython/dummypython.conf +++ b/src/modules/dummypython/dummypython.conf @@ -1,3 +1,6 @@ +# This is a dummy (example) module for a Python Job Module. +# +# The code is the documentation for the configuration file. --- syntax: "YAML map of anything" example: @@ -15,4 +18,4 @@ a_list_of_maps: - "another element" - name: "another item" contents: - - "not much" \ No newline at end of file + - "not much" diff --git a/src/modules/dummypythonqt/dummypythonqt.conf b/src/modules/dummypythonqt/dummypythonqt.conf index f60e778e1..5bc64abfa 100644 --- a/src/modules/dummypythonqt/dummypythonqt.conf +++ b/src/modules/dummypythonqt/dummypythonqt.conf @@ -1,3 +1,6 @@ +# This is a dummy (example) module for PythonQt. +# +# The code is the documentation for the configuration file. --- syntax: "YAML map of anything" example: diff --git a/src/modules/fstab/fstab.conf b/src/modules/fstab/fstab.conf index c3dbfc309..11adff2ed 100644 --- a/src/modules/fstab/fstab.conf +++ b/src/modules/fstab/fstab.conf @@ -1,13 +1,28 @@ +# Creates /etc/fstab and /etc/crypttab in the target system. +# Also creates mount points for all the filesystems. +# +# When creating fstab entries for a filesystem, this module +# uses the options for the filesystem type to write to the +# options field of the file. --- +# Mount options to use for all filesystems. If a specific filesystem +# is listed here, use those options, otherwise use the *default* +# options from this mapping. mountOptions: default: defaults,noatime btrfs: defaults,noatime,space_cache,autodefrag + +# If a filesystem is on an SSD, add the following options. If a specific +# filesystem is listed here, use those options, otherwise no additional +# options are set (i.e. there is no *default* like in *mountOptions*). ssdExtraMountOptions: ext4: discard jfs: discard xfs: discard swap: discard btrfs: discard,compress=lzo + +# Additional options added to each line in /etc/crypttab crypttabOptions: luks # For Debian and Debian-based distributions, change the above line to: # crypttabOptions: luks,keyscript=/bin/cat diff --git a/src/modules/grubcfg/grubcfg.conf b/src/modules/grubcfg/grubcfg.conf index 608c9b2b4..b354ec35a 100644 --- a/src/modules/grubcfg/grubcfg.conf +++ b/src/modules/grubcfg/grubcfg.conf @@ -1,10 +1,22 @@ +# Write lines to /etc/default/grub (in the target system) based +# on calculated values and the values set in the *defaults* key +# in this configuration file. +# +# Calculated values are: +# - GRUB_DISTRIBUTOR, branding module, *bootloaderEntryName* +# - GRUB_ENABLE_CRYPTODISK, based on the presence of filesystems +# that use LUKS +# - GRUB_CMDLINE_LINUX_DEFAULT, adding LUKS setup and plymouth +# support to the kernel. + --- # If set to true, always creates /etc/default/grub from scratch even if the file # already existed. If set to false, edits the existing file instead. overwrite: false + # Default entries to write to /etc/default/grub if it does not exist yet or if -# we are overwriting it. Note that in addition, GRUB_CMDLINE_LINUX_DEFAULT and -# GRUB_DISTRIBUTOR will always be written, with automatically detected values. +# we are overwriting it. +# defaults: GRUB_TIMEOUT: 5 GRUB_DEFAULT: "saved" diff --git a/src/modules/initcpio/initcpio.conf b/src/modules/initcpio/initcpio.conf index 21f5704cc..466a8785d 100644 --- a/src/modules/initcpio/initcpio.conf +++ b/src/modules/initcpio/initcpio.conf @@ -1,2 +1,3 @@ +# Run mkinitcpio(8) with the given preset value --- kernel: linux312 diff --git a/src/modules/luksopenswaphookcfg/luksopenswaphookcfg.conf b/src/modules/luksopenswaphookcfg/luksopenswaphookcfg.conf index 886867f8d..f5610cd7c 100644 --- a/src/modules/luksopenswaphookcfg/luksopenswaphookcfg.conf +++ b/src/modules/luksopenswaphookcfg/luksopenswaphookcfg.conf @@ -1,2 +1,4 @@ +# Writes an openswap configuration with LUKS settings to the given path --- +# Path of the configuration file to write (in the target system) configFilePath: /etc/openswap.conf diff --git a/src/modules/mount/mount.conf b/src/modules/mount/mount.conf index d8f8fb8cc..bb28eed66 100644 --- a/src/modules/mount/mount.conf +++ b/src/modules/mount/mount.conf @@ -1,4 +1,18 @@ +# Mount filesystems in the target (generally, before treating the +# target as a usable chroot / "live" system). Filesystems are +# automatically mounted from the partitioning module. Filesystems +# listed here are **extra**. The filesystems listed in *extraMounts* +# are mounted in all target systems. The filesystems listed in +# *extraMountsEfi* are mounted in the target system **only** if +# the host machine uses UEFI. --- +# Extra filesystems to mount. The key's value is a list of entries; each +# entry has four keys: +# - device The device node to mount +# - fs The filesystem type to use +# - mountPoint Where to mount the filesystem +# - options (optional) Extra options to pass to mount(8) +# extraMounts: - device: proc fs: proc diff --git a/src/modules/removeuser/removeuser.conf b/src/modules/removeuser/removeuser.conf index a59961ec5..dab4b2526 100644 --- a/src/modules/removeuser/removeuser.conf +++ b/src/modules/removeuser/removeuser.conf @@ -1,2 +1,6 @@ +# Removes a single user (with userdel) from the system. +# This is typically used in OEM setups or if the live user +# spills into the target system. --- +# Username in the target system to be removed. username: live diff --git a/src/modules/services/services.conf b/src/modules/services/services.conf index d9c8895ea..eb971b222 100644 --- a/src/modules/services/services.conf +++ b/src/modules/services/services.conf @@ -1,20 +1,45 @@ +# Systemd services manipulation. +# +# This module can enable services and targets for systemd +# (if packaging doesn't already do that). It can calso +# disable services (but not targets). +# +# First, services are enabled; then targets; then services +# are disabled -- this order of operations is fixed. --- -#systemd services and targets are enabled in this precise order -services: - - name: "NetworkManager" #name of the service file - mandatory: false #true=> if enabling fails the installer errors out and quits - #false=>if enabling fails print warning to console and continue - - name: "cups" - mandatory: false +# There are three configuration keys for this module: +# *services*, *targets* and *disable*. The value of each +# key is a list of entries. Each entry has two keys: +# - *name* is the (string) name of the service or target that is being +# changed. Use quotes. +# - *mandatory* is a boolean option, which states whether the change +# must be done successfully. If systemd reports an error while changing +# a mandatory entry, the installation will fail. When mandatory is false, +# errors for that entry (service or target) are ignored. +# +# Use [] to express an empty list. -targets: - - name: "graphical" - mandatory: true +# # This example enables NetworkManager (and fails if it can't), +# # disables cups (and ignores failure). Then it enables the +# # graphical target (e.g. so that SDDM runs for login), and +# # finally disables pacman-init (an ArchLinux-only service). +# # +# services: +# - name: "NetworkManager" +# mandatory: true +# - name: "cups" +# mandatory: false +# +# targets: +# - name: "graphical" +# mandatory: true +# +# disable: +# - name: "pacman-init" +# mandatory: false -disable: - - name: "pacman-init" - mandatory: false - -# Example to express an empty list: -# disable: [] +# By default, no changes are made. +services: [] +targets: [] +disable: []