1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
|
# === This file is part of Calamares - <https://calamares.io> ===
#
# SPDX-FileCopyrightText: 2014 Teo Mrnjavac <teo@kde.org>
# SPDX-FileCopyrightText: 2017 Adriaan de Groot <groot@kde.org>
# SPDX-License-Identifier: BSD-2-Clause
#
# Calamares is Free Software: see the License-Identifier above.
#
#
###
#
# Function and support code for adding a Calamares module (either a Qt / C++ plugin,
# or a Python module, or whatever) to the build.
#
# # Usage
#
# The public API is one single function:
#
# - calamares_add_module_subdirectory(subdirectory [skiplistvar])
# Adds a given *subdirectory* to the modules list, building the
# module that is there. The *subdirectory* must contain a `module.desc`
# (generally non-C++ modules) or a `CMakeLists.txt` (for C++ modules,
# or special cases). The module is assumed to be named after the
# (last component of) the subdirectory.
#
# If the module would be skipped (by the global SKIP_MODULES setting
# or a USE_* setting) or the module itself sets a reason to skip
# via the calamares_skip_module() function, the module is added to
# the list of skipped-modules in *skiplistvar*. If no variable is
# given, the reason is set in the parent scope variable
# SKIPPED_MODULES . Do **not** use SKIPPED_MODULES as the name of
# *skiplistvar*, things will get weird.
#
# Do note that the name of a module must be the same as the name of
# the directory containing it (as documented in src/modules/README.md).
# This applies to both C++ and Python modules, and allows the use of
# the subdirectory as a proxy for the module name inside.
#
include( CalamaresAddTranslations )
include( CalamaresCheckModuleSelection )
set( MODULE_DATA_DESTINATION share/calamares/modules )
# We look for Pylint (just once) so that unittests can be added that
# check the syntax / variables of Python modules. This should help
# avoid more typo's-in-releases.
if(BUILD_TESTING AND NOT PYLINT_COMMAND_SEARCHED)
set(PYLINT_COMMAND_SEARCHED TRUE)
find_program(
PYLINT_COMMAND
NAMES pylint3 pylint
PATHS $ENV{HOME}/.local/bin
)
endif()
function( _calamares_add_module_subdirectory_impl )
set( SUBDIRECTORY ${ARGV0} )
# Set SKIPPED_MODULES here, so CMake-based modules have a
# parent scope to set it in; this function, in turn sets it
# in **its** parent scope.
set( SKIPPED_MODULES "" )
set( MODULE_CONFIG_FILES "" )
# The module subdirectory may be given as a/b/c, but the module
# needs to be installed as "c", so we split off any intermediate
# directories.
#
# Compute _modulename (the last directory name) and _mod_dir
# (the full path to the module sources).
get_filename_component(_dirname "${SUBDIRECTORY}" DIRECTORY)
if( _dirname )
# Remove the dirname and any leftover leading /s
string( REGEX REPLACE "^${_dirname}/*" "" _modulename "${SUBDIRECTORY}" )
else()
set( _modulename ${SUBDIRECTORY} )
endif()
# Strip any remaining /
string( REGEX REPLACE "/" "" _modulename "${_modulename}" )
set( _mod_dir "${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIRECTORY}" )
# Skip list check applies to all kinds of modules
calamares_check_skip( ${_modulename} SKIPPED_MODULES )
if ( SKIPPED_MODULES )
# If it's skipped by infrastucture, the message already includes the module
# name. We don't need to do any further checking.
set( SKIPPED_MODULES "${SKIPPED_MODULES}" PARENT_SCOPE )
return()
endif()
# If this subdirectory has a CMakeLists.txt, we add_subdirectory it...
if( EXISTS "${_mod_dir}/CMakeLists.txt" )
add_subdirectory( ${SUBDIRECTORY} )
file( GLOB MODULE_CONFIG_FILES RELATIVE ${_mod_dir} "${SUBDIRECTORY}/*.conf" )
# Module has indicated it should be skipped, show that in
# the calling CMakeLists (which is src/modules/CMakeLists.txt normally).
if ( SKIPPED_MODULES )
set( SKIPPED_MODULES ${SKIPPED_MODULES} PARENT_SCOPE )
set( MODULE_CONFIG_FILES "" )
else()
# The SKIPPED_MODULES may be set in the directory itself
get_directory_property( _skip DIRECTORY ${SUBDIRECTORY} DEFINITION SKIPPED_MODULES )
if ( _skip )
set( SKIPPED_MODULES ${_skip} PARENT_SCOPE )
set( MODULE_CONFIG_FILES "" )
endif()
endif()
if ( SKIPPED_MODULES )
return()
endif()
# ...otherwise, we look for a module.desc.
elseif( EXISTS "${_mod_dir}/module.desc" )
set( MODULES_DIR ${CMAKE_INSTALL_LIBDIR}/calamares/modules )
set( MODULE_DESTINATION ${MODULES_DIR}/${_modulename} )
# 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" )
message( FATAL_ERROR "PythonQt is no longer supported" )
set( _mod_enabled OFF )
set( _mod_reason "No PythonQt support" )
set( _mod_testing OFF )
elseif ( MODULE_INTERFACE MATCHES "python" )
set( _mod_enabled ${Calamares_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 )
# We glob all the files inside the subdirectory, and we make sure they are
# synced with the bindir structure and installed.
file( GLOB MODULE_FILES RELATIVE ${_mod_dir} "${SUBDIRECTORY}/*" )
foreach( MODULE_FILE ${MODULE_FILES} )
if( NOT IS_DIRECTORY ${_mod_dir}/${MODULE_FILE} )
configure_file( ${SUBDIRECTORY}/${MODULE_FILE} ${SUBDIRECTORY}/${MODULE_FILE} COPYONLY )
get_filename_component( FLEXT ${MODULE_FILE} EXT )
if( "${FLEXT}" STREQUAL ".conf" )
message(STATUS "Config ${MODULE_FILE}")
list( APPEND MODULE_CONFIG_FILES ${MODULE_FILE} )
else()
message(STATUS "Non-Config ${MODULE_FILE}")
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIRECTORY}/${MODULE_FILE}
DESTINATION ${MODULE_DESTINATION} )
endif()
endif()
endforeach()
message( "-- ${BoldYellow}Found ${CALAMARES_APPLICATION_NAME} module: ${BoldRed}${_modulename}${ColorReset}" )
message( " ${Green}TYPE:${ColorReset} jobmodule" )
message( " ${Green}MODULE_DESTINATION:${ColorReset} ${MODULE_DESTINATION}" )
if( MODULE_CONFIG_FILES )
if (INSTALL_CONFIG)
message(" ${Green}CONFIGURATION_FILES:${ColorReset} ${MODULE_CONFIG_FILES} => [Build directory and ${MODULE_DATA_DESTINATION}]")
foreach(_cf ${MODULE_CONFIG_FILES})
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIRECTORY}/${_cf} DESTINATION ${MODULE_DATA_DESTINATION})
endforeach()
else()
message(" ${Green}CONFIGURATION_FILES:${ColorReset} ${MODULE_CONFIG_FILES} => [Build directory only]")
endif()
endif()
message( "" )
# We copy over the lang directory, if any
if( IS_DIRECTORY "${_mod_dir}/lang" )
install_calamares_gettext_translations(
${SUBDIRECTORY}
SOURCE_DIR "${_mod_dir}/lang"
FILENAME ${SUBDIRECTORY}.mo
RENAME calamares-${SUBDIRECTORY}.mo
)
endif()
else()
# Module disabled due to missing dependencies / unsupported interface
set( SKIPPED_MODULES "${SUBDIRECTORY} (${_mod_reason})" PARENT_SCOPE )
endif()
else()
message( "-- ${BoldYellow}Warning:${ColorReset} tried to add module subdirectory ${BoldRed}${SUBDIRECTORY}${ColorReset} which has no CMakeLists.txt or module.desc." )
message( "" )
endif()
# Check any config files for basic correctness
if ( BUILD_TESTING AND MODULE_CONFIG_FILES )
set( _count 0 )
foreach( _config_file ${MODULE_CONFIG_FILES} )
set( _count_str "-${_count}" )
if ( _count EQUAL 0 )
set( _count_str "" )
endif()
add_test(
NAME config-${SUBDIRECTORY}${_count_str}
COMMAND test_conf ${CMAKE_CURRENT_BINARY_DIR}/${SUBDIRECTORY}/${_config_file} )
math( EXPR _count "${_count} + 1" )
endforeach()
endif()
# Adding general tests
#
# Add a check that the module can be loaded. Since this calls exec(), the module
# may try to do things to the running system. Needs work to make that a
# safe thing to do.
#
# If the module has a tests/ subdirectory with *.global and *.job
# files (YAML files holding global and job-configurations for
# testing purposes) then those files are used to drive additional
# tests. The files must be numbered (starting from 1) for this to work;
# 1.global and 1.job together make the configuration for test 1.
#
# If the module has a tests/CMakeLists.txt while it doesn't have its
# own CMakeLists.txt (e.g. a Python module), then the subdirectory
# for tests/ is added on its own.
#
if ( BUILD_TESTING AND _mod_enabled AND _mod_testing )
add_test(
NAME load-${SUBDIRECTORY}
COMMAND loadmodule ${SUBDIRECTORY}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
# Try it with the tests/ configurations shipped with the module
set( _count 1 )
set( _testdir ${_mod_dir}/tests )
while ( EXISTS "${_testdir}/${_count}.global" OR EXISTS "${_testdir}/${_count}.job" )
set( _dash_g "" )
set( _dash_j "" )
if ( EXISTS "${_testdir}/${_count}.global" )
set( _dash_g -g ${_testdir}/${_count}.global )
endif()
if ( EXISTS "${_testdir}/${_count}.job" )
set( _dash_j -j ${_testdir}/${_count}.job )
endif()
add_test(
NAME load-${SUBDIRECTORY}-${_count}
COMMAND loadmodule ${_dash_g} ${_dash_j} ${SUBDIRECTORY}
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
math( EXPR _count "${_count} + 1" )
endwhile()
if ( EXISTS ${_testdir}/CMakeTests.txt AND NOT EXISTS ${_mod_dir}/CMakeLists.txt )
include( ${_testdir}/CMakeTests.txt )
endif()
if ( PYLINT_COMMAND AND MODULE_INTERFACE MATCHES "python" )
# Python modules get an additional test via pylint; this
# needs to run at top-level because the ci/libcalamares directory
# contains API stubs.
#
# TODO: the entry point is assumed to be `main.py`, but that is
# configurable through module.desc
add_test(
NAME lint-${SUBDIRECTORY}
COMMAND env PYTHONPATH=ci: ${PYLINT_COMMAND} -E ${_mod_dir}/main.py
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
endif()
endif()
endfunction()
function( calamares_add_module_subdirectory )
set( SUBDIRECTORY ${ARGV0} )
set( _ams_SKIP_LIST ${ARGV1} )
set( SKIPPED_MODULES "" )
_calamares_add_module_subdirectory_impl( ${SUBDIRECTORY} )
if ( SKIPPED_MODULES )
if ( _ams_SKIP_LIST )
list( APPEND ${_ams_SKIP_LIST} "${SKIPPED_MODULES}" )
set( ${_ams_SKIP_LIST} "${${_ams_SKIP_LIST}}" PARENT_SCOPE )
else()
set( SKIPPED_MODULES "${SKIPPED_MODULES}" PARENT_SCOPE )
endif()
endif()
endfunction()
|