diff --git a/CMakeModules/CalamaresAddModuleSubdirectory.cmake b/CMakeModules/CalamaresAddModuleSubdirectory.cmake index 7c0159ac0..53b952b54 100644 --- a/CMakeModules/CalamaresAddModuleSubdirectory.cmake +++ b/CMakeModules/CalamaresAddModuleSubdirectory.cmake @@ -64,22 +64,31 @@ function( calamares_add_module_subdirectory ) set( MODULE_DESTINATION ${MODULES_DIR}/${SUBDIRECTORY} ) # Read module.desc, check that the interface type is supported. + # + # _mod_enabled boolean if the module should be built (only if the interface is supported) + # _mod_reason is a human-readable explanation why it isn't built + # _mod_testing boolean if the module should be added to the loadmodule tests file(STRINGS "${_mod_dir}/module.desc" MODULE_INTERFACE REGEX "^interface") if ( MODULE_INTERFACE MATCHES "pythonqt" ) set( _mod_enabled ${WITH_PYTHONQT} ) set( _mod_reason "No PythonQt support" ) + set( _mod_testing OFF ) elseif ( MODULE_INTERFACE MATCHES "python" ) set( _mod_enabled ${WITH_PYTHON} ) set( _mod_reason "No Python support" ) + set( _mod_testing ON ) # Will check syntax and imports, at least elseif ( MODULE_INTERFACE MATCHES "qtplugin" ) set( _mod_enabled OFF ) set( _mod_reason "C++ modules must have a CMakeLists.txt instead" ) + set( _mod_testing OFF ) elseif ( MODULE_INTERFACE MATCHES "process" ) set( _mod_enabled ON ) set( _mod_reason "" ) + set( _mod_testing OFF ) else() set( _mod_enabled OFF ) set( _mod_reason "Unknown module interface '${MODULE_INTERFACE}'" ) + set( _mod_testing OFF ) endif() if ( _mod_enabled ) @@ -153,11 +162,11 @@ function( calamares_add_module_subdirectory ) # may try to do things to the running system. Needs work to make that a # safe thing to do. # - # if ( BUILD_TESTING ) - # add_test( - # NAME load-${SUBDIRECTORY} - # COMMAND loadmodule ${SUBDIRECTORY} - # WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - # ) - # endif() + if ( BUILD_TESTING AND _mod_enabled AND _mod_testing ) + add_test( + NAME load-${SUBDIRECTORY} + COMMAND loadmodule ${SUBDIRECTORY} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) + endif() endfunction() diff --git a/src/calamares/testmain.cpp b/src/calamares/testmain.cpp index 195701024..982a9b4c2 100644 --- a/src/calamares/testmain.cpp +++ b/src/calamares/testmain.cpp @@ -227,7 +227,8 @@ main( int argc, char* argv[] ) cError() << "Job #" << count << "failed" << TR( "summary", r.message() ) << TR( "details", r.details() ); - ++failure_count; + if ( r.errorCode() > 0 ) + ++failure_count; } ++count; } diff --git a/src/libcalamares/Job.cpp b/src/libcalamares/Job.cpp index b5d626854..d2118451f 100644 --- a/src/libcalamares/Job.cpp +++ b/src/libcalamares/Job.cpp @@ -21,16 +21,16 @@ namespace Calamares { -JobResult::JobResult( JobResult&& rhs ) : - m_ok( rhs.m_ok ) - , m_message( std::move( rhs.m_message ) ) +JobResult::JobResult( JobResult&& rhs ) + : m_message( std::move( rhs.m_message ) ) , m_details( std::move( rhs.m_details ) ) + , m_number( rhs.m_number ) { } JobResult::operator bool() const { - return m_ok; + return m_number == 0; } @@ -64,21 +64,26 @@ JobResult::setDetails( const QString& details ) JobResult JobResult::ok() { - return JobResult( true, QString(), QString() ); + return JobResult( QString(), QString(), NoError ); } JobResult JobResult::error( const QString& message, const QString& details ) { - return JobResult( false, message, details ); + return JobResult( message, details, GenericError ); } +JobResult +JobResult::internalError( const QString& message, const QString& details, int number ) +{ + return JobResult( message, details, number ? number : GenericError ); +} -JobResult::JobResult( bool ok, const QString& message, const QString& details ) - : m_ok( ok ) - , m_message( message ) +JobResult::JobResult( const QString& message, const QString& details, int number ) + : m_message( message ) , m_details( details ) + , m_number( number ) {} diff --git a/src/libcalamares/Job.h b/src/libcalamares/Job.h index 040ead6ad..04b4560a4 100644 --- a/src/libcalamares/Job.h +++ b/src/libcalamares/Job.h @@ -29,6 +29,20 @@ namespace Calamares { class DLLEXPORT JobResult { public: + /** @brief Distinguish classes of errors + * + * All "ok result" have errorCode 0 (NoError). + * Errors returned from job execution have values < 0. + * Errors before job execution, or not returned by the job execution + * itself, have values > 0. + */ + enum + { + NoError = 0, + GenericError = -1, + PythonUncaughtException = 1 + } ; + JobResult( const JobResult& rhs ) = delete; JobResult( JobResult&& rhs ); @@ -42,17 +56,22 @@ public: virtual QString details() const; virtual void setDetails( const QString& details ); - static JobResult ok(); + int errorCode() const { return m_number; } + /// @brief an "ok status" result + static JobResult ok(); + /// @brief an "error" result resulting from the execution of the job static JobResult error( const QString& message, const QString& details = QString() ); + /// @brief an "internal error" meaning the job itself has a problem (usually for python) + static JobResult internalError( const QString&, const QString& details, int errorCode ); protected: - explicit JobResult( bool ok, const QString& message, const QString& details ); + explicit JobResult( const QString& message, const QString& details, int errorCode ); private: - bool m_ok; QString m_message; QString m_details; + int m_number; }; class DLLEXPORT Job : public QObject diff --git a/src/libcalamares/PythonJob.cpp b/src/libcalamares/PythonJob.cpp index 6e8323e49..32792e737 100644 --- a/src/libcalamares/PythonJob.cpp +++ b/src/libcalamares/PythonJob.cpp @@ -373,8 +373,10 @@ PythonJob::exec() } bp::handle_exception(); PyErr_Clear(); - return JobResult::error( tr( "Boost.Python error in job \"%1\"." ).arg( prettyName() ), - msg ); + return JobResult::internalError( + tr( "Boost.Python error in job \"%1\"." ).arg( prettyName() ), + msg, + JobResult::PythonUncaughtException ); } } diff --git a/src/libcalamaresui/viewpages/PythonQtJob.h b/src/libcalamaresui/viewpages/PythonQtJob.h index f356e85cc..2b50c0ded 100644 --- a/src/libcalamaresui/viewpages/PythonQtJob.h +++ b/src/libcalamaresui/viewpages/PythonQtJob.h @@ -36,7 +36,7 @@ public: const QString& message, const QString& details ) : QObject( nullptr ) - , Calamares::JobResult( ok, message, details ) + , Calamares::JobResult( message, details, ok ? 0 : Calamares::JobResult::GenericError ) {} }; diff --git a/src/modules/fstab/main.py b/src/modules/fstab/main.py index 1aea5523e..abbd3d524 100644 --- a/src/modules/fstab/main.py +++ b/src/modules/fstab/main.py @@ -306,6 +306,16 @@ def run(): conf = libcalamares.job.configuration partitions = global_storage.value("partitions") root_mount_point = global_storage.value("rootMountPoint") + + if not partitions: + libcalamares.utils.warning("partitions is empty, {!s}".format(partitions)) + return (_("Configuration Error"), + _("No partitions are defined for
{!s}to use." ).format("fstab")) + if not root_mount_point: + libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(root_mount_point)) + return (_("Configuration Error"), + _("No root mount point is given for
{!s}to use." ).format("fstab")) + mount_options = conf["mountOptions"] ssd_extra_mount_options = conf.get("ssdExtraMountOptions", {}) crypttab_options = conf.get("crypttabOptions", "luks") diff --git a/src/modules/initcpio/main.py b/src/modules/initcpio/main.py index 20d841de5..796f68721 100644 --- a/src/modules/initcpio/main.py +++ b/src/modules/initcpio/main.py @@ -32,18 +32,19 @@ _ = gettext.translation("calamares-python", def pretty_name(): return _("Creating initramfs with mkinitcpio.") - -def run_mkinitcpio(): - """ Runs mkinitcpio with given kernel profile """ - kernel = libcalamares.job.configuration['kernel'] - check_target_env_call(['mkinitcpio', '-p', kernel]) - - def run(): """ Calls routine to create kernel initramfs image. :return: """ - run_mkinitcpio() + from subprocess import CalledProcessError + + kernel = libcalamares.job.configuration['kernel'] + try: + check_target_env_call(['mkinitcpio', '-p', kernel]) + except CalledProcessError as e: + libcalamares.utils.warning(str(e)) + return ( _( "Process Failed" ), + _( "Process
mkinitcpiofailed with error code {!s}. The command was
{!s}." ).format( e.returncode, e.cmd ) ) return None diff --git a/src/modules/initcpiocfg/main.py b/src/modules/initcpiocfg/main.py index 2207816d9..166cccf44 100644 --- a/src/modules/initcpiocfg/main.py +++ b/src/modules/initcpiocfg/main.py @@ -181,6 +181,16 @@ def run(): """ partitions = libcalamares.globalstorage.value("partitions") root_mount_point = libcalamares.globalstorage.value("rootMountPoint") + + if not partitions: + libcalamares.utils.warning("partitions is empty, {!s}".format(partitions)) + return (_("Configuration Error"), + _("No partitions are defined for
{!s}to use." ).format("initcpiocfg")) + if not root_mount_point: + libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(root_mount_point)) + return (_("Configuration Error"), + _("No root mount point is given for
{!s}to use." ).format("initcpiocfg")) + modify_mkinitcpio_conf(partitions, root_mount_point) return None diff --git a/src/modules/initramfscfg/main.py b/src/modules/initramfscfg/main.py index b62e4e5f7..52d512567 100644 --- a/src/modules/initramfscfg/main.py +++ b/src/modules/initramfscfg/main.py @@ -88,6 +88,16 @@ def run(): """ partitions = libcalamares.globalstorage.value("partitions") root_mount_point = libcalamares.globalstorage.value("rootMountPoint") + + if not partitions: + libcalamares.utils.warning("partitions is empty, {!s}".format(partitions)) + return (_("Configuration Error"), + _("No partitions are defined for
{!s}to use." ).format("initramfscfg")) + if not root_mount_point: + libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(root_mount_point)) + return (_("Configuration Error"), + _("No root mount point is given for
{!s}to use." ).format("initramfscfg")) + copy_initramfs_hooks(partitions, root_mount_point) return None diff --git a/src/modules/localecfg/main.py b/src/modules/localecfg/main.py index 09e700409..9e50fb448 100644 --- a/src/modules/localecfg/main.py +++ b/src/modules/localecfg/main.py @@ -138,6 +138,12 @@ def run(): } install_path = libcalamares.globalstorage.value("rootMountPoint") + + if install_path is None: + libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(install_path)) + return (_("Configuration Error"), + _("No root mount point is given for
{!s}to use." ).format("localecfg")) + target_locale_gen = "{!s}/etc/locale.gen".format(install_path) target_locale_gen_bak = target_locale_gen + ".bak" target_locale_conf_path = "{!s}/etc/locale.conf".format(install_path) diff --git a/src/modules/luksbootkeyfile/main.py b/src/modules/luksbootkeyfile/main.py index 0c025ca31..fb0146cf8 100644 --- a/src/modules/luksbootkeyfile/main.py +++ b/src/modules/luksbootkeyfile/main.py @@ -46,6 +46,11 @@ def run(): partitions = libcalamares.globalstorage.value("partitions") + if not partitions: + libcalamares.utils.warning("partitions is empty, {!s}".format(partitions)) + return (_("Configuration Error"), + _("No partitions are defined for
{!s}to use." ).format("luksbootkey")) + luks_root_device = "" luks_root_passphrase = "" diff --git a/src/modules/luksopenswaphookcfg/main.py b/src/modules/luksopenswaphookcfg/main.py index e32fbbbfd..a30ae999b 100644 --- a/src/modules/luksopenswaphookcfg/main.py +++ b/src/modules/luksopenswaphookcfg/main.py @@ -90,6 +90,15 @@ def run(): openswap_conf_path = libcalamares.job.configuration["configFilePath"] partitions = libcalamares.globalstorage.value("partitions") + if not partitions: + libcalamares.utils.warning("partitions is empty, {!s}".format(partitions)) + return (_("Configuration Error"), + _("No partitions are defined for
{!s}to use." ).format("luksopenswaphookcfg")) + if not root_mount_point: + libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(root_mount_point)) + return (_("Configuration Error"), + _("No root mount point is given for
{!s}to use." ).format("luksopenswaphookcfg")) + openswap_conf_path = openswap_conf_path.lstrip('/') return write_openswap_conf(partitions, root_mount_point, openswap_conf_path) diff --git a/src/modules/machineid/main.py b/src/modules/machineid/main.py index 092fc7196..cde47cfd9 100644 --- a/src/modules/machineid/main.py +++ b/src/modules/machineid/main.py @@ -43,6 +43,12 @@ def run(): :return: """ root_mount_point = libcalamares.globalstorage.value("rootMountPoint") + + if root_mount_point is None: + libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(root_mount_point)) + return (_("Configuration Error"), + _("No root mount point is given for
{!s}to use." ).format("machineid")) + enable_systemd = libcalamares.job.configuration["systemd"] enable_dbus = libcalamares.job.configuration["dbus"] enable_symlink = libcalamares.job.configuration["symlink"] diff --git a/src/modules/mount/main.py b/src/modules/mount/main.py index 50b74b844..aab3568d1 100644 --- a/src/modules/mount/main.py +++ b/src/modules/mount/main.py @@ -133,9 +133,15 @@ def run(): :return: """ - root_mount_point = tempfile.mkdtemp(prefix="calamares-root-") partitions = libcalamares.globalstorage.value("partitions") + if not partitions: + libcalamares.utils.warning("partitions is empty, {!s}".format(partitions)) + return (_("Configuration Error"), + _("No partitions are defined for
{!s}to use." ).format("mount")) + + root_mount_point = tempfile.mkdtemp(prefix="calamares-root-") + # Guard against missing keys (generally a sign that the config file is bad) extra_mounts = libcalamares.job.configuration.get("extraMounts") or [] extra_mounts_efi = libcalamares.job.configuration.get("extraMountsEfi") or [] diff --git a/src/modules/networkcfg/main.py b/src/modules/networkcfg/main.py index 5509be205..00a264f53 100644 --- a/src/modules/networkcfg/main.py +++ b/src/modules/networkcfg/main.py @@ -41,8 +41,13 @@ def run(): """ Setup network configuration """ - root_mount_point = libcalamares.globalstorage.value("rootMountPoint") + + if root_mount_point is None: + libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(root_mount_point)) + return (_("Configuration Error"), + _("No root mount point is given for
{!s}to use." ).format("networkcfg")) + source_nm = "/etc/NetworkManager/system-connections/" target_nm = os.path.join( root_mount_point, "etc/NetworkManager/system-connections/" diff --git a/src/modules/openrcdmcryptcfg/main.py b/src/modules/openrcdmcryptcfg/main.py index 20b306442..0362b421b 100644 --- a/src/modules/openrcdmcryptcfg/main.py +++ b/src/modules/openrcdmcryptcfg/main.py @@ -73,6 +73,15 @@ def run(): dmcrypt_conf_path = libcalamares.job.configuration["configFilePath"] partitions = libcalamares.globalstorage.value("partitions") + if not partitions: + libcalamares.utils.warning("partitions is empty, {!s}".format(partitions)) + return (_("Configuration Error"), + _("No partitions are defined for
{!s}to use." ).format("openrcdmcryptcfg")) + if not root_mount_point: + libcalamares.utils.warning("rootMountPoint is empty, {!s}".format(root_mount_point)) + return (_("Configuration Error"), + _("No root mount point is given for
{!s}to use." ).format("openrcdmcryptcfg")) + dmcrypt_conf_path = dmcrypt_conf_path.lstrip('/') return write_dmcrypt_conf(partitions, root_mount_point, dmcrypt_conf_path) diff --git a/src/modules/rawfs/main.py b/src/modules/rawfs/main.py index acf901a0c..af8a8198c 100644 --- a/src/modules/rawfs/main.py +++ b/src/modules/rawfs/main.py @@ -166,6 +166,11 @@ def run(): filesystems = list() partitions = libcalamares.globalstorage.value("partitions") + if not partitions: + libcalamares.utils.warning("partitions is empty, {!s}".format(partitions)) + return (_("Configuration Error"), + _("No partitions are defined for
{!s}to use." ).format("rawfs")) + for partition in partitions: if partition["mountPoint"]: for src in libcalamares.job.configuration["targets"]: