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 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
|
# Copyright (c) 2015 SUSE Linux GmbH. All rights reserved.
#
# This file is part of kiwi.
#
# kiwi 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.
#
# kiwi 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 kiwi. If not, see <http://www.gnu.org/licenses/>
#
"""
usage: kiwi-ng -h | --help
kiwi-ng [--profile=<name>...]
[--temp-dir=<directory>]
[--target-arch=<name>]
[--type=<build_type>]
[--logfile=<filename>]
[--logsocket=<socketfile>]
[--loglevel=<number>]
[--debug]
[--debug-run-scripts-in-screen]
[--color-output]
[--config=<configfile>]
[--kiwi-file=<kiwifile>]
image <command> [<args>...]
kiwi-ng [--logfile=<filename>]
[--logsocket=<socketfile>]
[--loglevel=<number>]
[--debug]
[--debug-run-scripts-in-screen]
[--color-output]
[--config=<configfile>]
result <command> [<args>...]
kiwi-ng [--profile=<name>...]
[--shared-cache-dir=<directory>]
[--temp-dir=<directory>]
[--target-arch=<name>]
[--type=<build_type>]
[--logfile=<filename>]
[--logsocket=<socketfile>]
[--loglevel=<number>]
[--debug]
[--debug-run-scripts-in-screen]
[--color-output]
[--config=<configfile>]
[--kiwi-file=<kiwifile>]
system <command> [<args>...]
kiwi-ng compat <legacy_args>...
kiwi-ng --compat <legacy_args>...
kiwi-ng -v | --version
kiwi-ng help
global options:
--color-output
use colors for warning and error messages
--config=<configfile>
use specified runtime configuration file. If
not specified the runtime configuration is looked
up at ~/.config/kiwi/config.yml or /etc/kiwi.yml
--logfile=<filename>
create a log file containing all log information including
debug information even if this is was not requested by the
debug switch. The special call: '--logfile stdout' sends all
information to standard out instead of writing to a file
--logsocket=<socketfile>
send log data to the given Unix Domain socket in the same
format as with --logfile
--loglevel=<number>
specify logging level as number. Details about the
available log levels can be found at:
https://docs.python.org/3/library/logging.html#logging-levels
Setting a log level causes all message >= level to be
displayed.
--debug
print debug information, same as: '--loglevel 10'
--debug-run-scripts-in-screen
run scripts called by kiwi in a screen session
-v --version
show program version
help
show manual page
global options for services: image, system
--profile=<name>
profile name, multiple profiles can be selected by passing
this option multiple times
--shared-cache-dir=<directory>
specify an alternative shared cache directory. The directory
is shared via bind mount between the build host and image
root system and contains information about package repositories
and their cache and meta data.
--temp-dir=<directory>
specify an alternative base temporary directory. The
provided path is used as base directory to store temporary
files and directories. By default /var/tmp is used.
--type=<build_type>
image build type. If not set the default XML specified
build type will be used
--kiwi-file=<kiwifile>
Basename of kiwi file which contains the main image
configuration elements. If not specified kiwi searches for
a file named config.xml or a file matching *.kiwi
global options for services: image, system
--target-arch=<name>
set the image architecture. By default the host architecture is
used as the image architecture. If the specified architecture name
does not match the host architecture and is therefore requesting
a cross architecture image build, it's important to understand that
for this process to work a preparatory step to support the image
architecture and binary format on the building host is required
and not a responsibility of kiwi.
"""
import logging
import sys
import os
import pkg_resources
from docopt import docopt
# project
from kiwi.exceptions import (
KiwiUnknownServiceName,
KiwiCommandNotLoaded,
KiwiLoadCommandUndefined,
KiwiCompatError
)
from kiwi.path import Path
from kiwi.version import __version__
from kiwi.help import Help
from kiwi.defaults import Defaults
log = logging.getLogger('kiwi')
class Cli:
"""
**Implements the main command line interface**
An instance of the Cli class builds the entry point for the
application and implements methods to load further command plugins
which itself provides their own command line interface
"""
def __init__(self):
self.all_args = docopt(
__doc__,
version='KIWI (next generation) version ' + __version__,
options_first=True
)
self.command_args = self.all_args['<args>']
self.command_loaded = None
def show_and_exit_on_help_request(self):
"""
Execute man to show the selected manual page
"""
if self.all_args['help']:
manual = Help()
manual.show('kiwi')
sys.exit(0)
def get_servicename(self):
"""
Extract service name from argument parse result
:return: service name
:rtype: str
"""
if self.all_args.get('image') is True:
return 'image'
elif self.all_args.get('system') is True:
return 'system'
elif self.all_args.get('result') is True:
return 'result'
elif self.all_args.get('--compat') is True:
return 'compat'
elif self.all_args.get('compat') is True:
return 'compat'
else:
raise KiwiUnknownServiceName(
'Unknown/Invalid Servicename'
)
def invoke_kiwicompat(self, compat_args):
"""
Execute kiwicompat with provided legacy KIWI command line arguments
Example:
.. code:: python
invoke_kiwicompat(
'--build', 'description', '--type', 'vmx',
'-d', 'destination'
)
:param list compat_args: legacy kiwi command arguments
"""
kiwicompat = Path.which('kiwicompat', access_mode=os.X_OK)
try:
os.execvp(kiwicompat, ['kiwicompat'] + compat_args)
except Exception as e:
raise KiwiCompatError(
'%s: %s' % (type(e).__name__, format(e))
)
def get_command(self):
"""
Extract selected command name
:return: command name
:rtype: str
"""
return self.all_args['<command>']
def get_command_args(self):
"""
Extract argument dict for selected command
:return:
Contains dictionary of command arguments
.. code:: python
{
'--command-option': 'value'
}
:rtype: dict
"""
return self._load_command_args()
def get_global_args(self):
"""
Extract argument dict for global arguments
:return:
Contains dictionary of global arguments
.. code:: python
{
'--global-option': 'value'
}
:rtype: dict
"""
result = {}
for arg, value in list(self.all_args.items()):
if not arg == '<command>' and not arg == '<args>':
if arg == '--type' and value == 'vmx':
log.warning(
'vmx type is now a subset of oem, --type set to oem'
)
value = 'oem'
if arg == '--shared-cache-dir' and not value:
value = os.sep + Defaults.get_shared_cache_location()
if arg == '--shared-cache-dir' and value:
Defaults.set_shared_cache_location(value)
if arg == '--temp-dir' and not value:
value = Defaults.get_temp_location()
if arg == '--temp-dir' and value:
Defaults.set_temp_location(value)
if arg == '--target-arch' and value:
Defaults.set_platform_name(value)
if arg == '--config' and value: # pragma: no cover
Defaults.set_custom_runtime_config_file(value)
result[arg] = value
return result
def load_command(self):
"""
Loads task class plugin according to service and command name
:return: loaded task module
:rtype: object
"""
discovered_tasks = {
entry_point.name: entry_point.load()
for entry_point in pkg_resources.iter_entry_points('kiwi.tasks')
}
service = self.get_servicename()
command = self.get_command()
if service == 'compat':
compat_arguments = self.all_args['<legacy_args>']
if '--' in compat_arguments:
compat_arguments.remove('--')
return self.invoke_kiwicompat(compat_arguments)
if not command:
raise KiwiLoadCommandUndefined(
'No command specified for {0} service'.format(service)
)
self.command_loaded = discovered_tasks.get(
service + '_' + command
)
if not self.command_loaded:
prefix = 'usage:'
discovered_tasks_for_service = ''
for task in discovered_tasks:
if task.startswith(service):
discovered_tasks_for_service += '{0} kiwi-ng {1}\n'.format(
prefix, task.replace('_', ' ')
)
prefix = ' '
raise KiwiCommandNotLoaded(
'Command "{0}" not found\n\n{1}'.format(
command, discovered_tasks_for_service
)
)
return self.command_loaded
def _load_command_args(self):
try:
argv = [
self.get_servicename(), self.get_command()
] + self.command_args
return docopt(self.command_loaded.__doc__, argv=argv)
except Exception:
raise KiwiCommandNotLoaded(
'%s command not loaded' % self.get_command()
)
|