]> git.ipfire.org Git - thirdparty/openembedded/openembedded-core.git/blame - scripts/wic
python3: move dataclasses to python3-core
[thirdparty/openembedded/openembedded-core.git] / scripts / wic
CommitLineData
ea6245d2 1#!/usr/bin/env python3
95455ae4
TZ
2#
3# Copyright (c) 2013, Intel Corporation.
95455ae4 4#
f8c9c511 5# SPDX-License-Identifier: GPL-2.0-only
95455ae4
TZ
6#
7# DESCRIPTION 'wic' is the OpenEmbedded Image Creator that users can
8# use to generate bootable images. Invoking it without any arguments
9# will display help screens for the 'wic' command and list the
10# available 'wic' subcommands. Invoking a subcommand without any
11# arguments will likewise display help screens for the specified
12# subcommand. Please use that interface for detailed help.
13#
14# AUTHORS
15# Tom Zanussi <tom.zanussi (at] linux.intel.com>
16#
a0f8cb7e 17__version__ = "0.2.0"
95455ae4 18
b11bfadb 19# Python Standard Library modules
95455ae4
TZ
20import os
21import sys
4fe9635c 22import argparse
95455ae4 23import logging
0507ec4c 24import subprocess
3966cbf5 25import shutil
8db6f74b
EB
26
27from collections import namedtuple
95455ae4 28
b11bfadb 29# External modules
dcea30b8 30scripts_path = os.path.dirname(os.path.realpath(__file__))
95455ae4 31lib_path = scripts_path + '/lib'
59b1eefb 32sys.path.insert(0, lib_path)
8aba1fd0
CRSF
33import scriptpath
34scriptpath.add_oe_lib_path()
9d86eb2e 35
89df0d31
CRSF
36# Check whether wic is running within eSDK environment
37sdkroot = scripts_path
38if os.environ.get('SDKTARGETSYSROOT'):
39 while sdkroot != '' and sdkroot != os.sep:
40 if os.path.exists(os.path.join(sdkroot, '.devtoolbase')):
03fa13a2
CRSF
41 # Set BUILDDIR for wic to work within eSDK
42 os.environ['BUILDDIR'] = sdkroot
89df0d31
CRSF
43 # .devtoolbase only exists within eSDK
44 # If found, initialize bitbake path for eSDK environment and append to PATH
45 sdkroot = os.path.join(os.path.dirname(scripts_path), 'bitbake', 'bin')
46 os.environ['PATH'] += ":" + sdkroot
47 break
48 sdkroot = os.path.dirname(sdkroot)
49
3966cbf5 50bitbake_exe = shutil.which('bitbake')
9d86eb2e 51if bitbake_exe:
8aba1fd0 52 bitbake_path = scriptpath.add_bitbake_lib_path()
0507ec4c 53 import bb
95455ae4 54
15442d07 55from wic import WicError
df906f3c 56from wic.misc import get_bitbake_var, BB_VARS
5dc02d57
EB
57from wic import engine
58from wic import help as hlp
95455ae4 59
f7d9e33a
EB
60
61def wic_logger():
62 """Create and convfigure wic logger."""
63 logger = logging.getLogger('wic')
64 logger.setLevel(logging.INFO)
65
66 handler = logging.StreamHandler()
67
68 formatter = logging.Formatter('%(levelname)s: %(message)s')
69 handler.setFormatter(formatter)
70
71 logger.addHandler(handler)
72
73 return logger
74
75logger = wic_logger()
76
719d093c
JHFF
77def rootfs_dir_to_args(krootfs_dir):
78 """
79 Get a rootfs_dir dict and serialize to string
80 """
81 rootfs_dir = ''
872cb0d5 82 for key, val in krootfs_dir.items():
719d093c 83 rootfs_dir += ' '
872cb0d5 84 rootfs_dir += '='.join([key, val])
719d093c
JHFF
85 return rootfs_dir.strip()
86
719d093c 87
4fe9635c
AR
88class RootfsArgAction(argparse.Action):
89 def __init__(self, **kwargs):
90 super().__init__(**kwargs)
91
92 def __call__(self, parser, namespace, value, option_string=None):
93 if not "rootfs_dir" in vars(namespace) or \
94 not type(namespace.__dict__['rootfs_dir']) is dict:
95 namespace.__dict__['rootfs_dir'] = {}
96
97 if '=' in value:
98 (key, rootfs_dir) = value.split('=')
99 else:
100 key = 'ROOTFS_DIR'
101 rootfs_dir = value
719d093c 102
4fe9635c 103 namespace.__dict__['rootfs_dir'][key] = rootfs_dir
95455ae4 104
4fe9635c
AR
105
106def wic_create_subcommand(options, usage_str):
95455ae4
TZ
107 """
108 Command-line handling for image creation. The real work is done
109 by image.engine.wic_create()
110 """
0507ec4c 111 if options.build_rootfs and not bitbake_exe:
131629ca 112 raise WicError("Can't build rootfs as bitbake is not in the $PATH")
9d86eb2e 113
98912687
EB
114 if not options.image_name:
115 missed = []
116 for val, opt in [(options.rootfs_dir, 'rootfs-dir'),
117 (options.bootimg_dir, 'bootimg-dir'),
118 (options.kernel_dir, 'kernel-dir'),
119 (options.native_sysroot, 'native-sysroot')]:
120 if not val:
121 missed.append(opt)
122 if missed:
b7c19f1e
EB
123 raise WicError("The following build artifacts are not specified: %s" %
124 ", ".join(missed))
9116a17e 125
d4652335
EB
126 if options.image_name:
127 BB_VARS.default_image = options.image_name
128 else:
95455ae4
TZ
129 options.build_check = False
130
abdfad1c
EB
131 if options.vars_dir:
132 BB_VARS.vars_dir = options.vars_dir
133
f7d9e33a 134 if options.build_check and not engine.verify_build_env():
b7c19f1e 135 raise WicError("Couldn't verify build environment, exiting")
95455ae4 136
f7d9e33a
EB
137 if options.debug:
138 logger.setLevel(logging.DEBUG)
139
95455ae4 140 if options.image_name:
75ae0b7c
EB
141 if options.build_rootfs:
142 argv = ["bitbake", options.image_name]
143 if options.debug:
144 argv.append("--debug")
145
f7d9e33a 146 logger.info("Building rootfs...\n")
0507ec4c 147 subprocess.check_call(argv)
75ae0b7c 148
3abe23bd
EB
149 rootfs_dir = get_bitbake_var("IMAGE_ROOTFS", options.image_name)
150 kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE", options.image_name)
afc486b6 151 bootimg_dir = get_bitbake_var("STAGING_DATADIR", options.image_name)
33ca15b9
EB
152
153 native_sysroot = options.native_sysroot
154 if options.vars_dir and not native_sysroot:
155 native_sysroot = get_bitbake_var("RECIPE_SYSROOT_NATIVE", options.image_name)
75ae0b7c
EB
156 else:
157 if options.build_rootfs:
b7c19f1e
EB
158 raise WicError("Image name is not specified, exiting. "
159 "(Use -e/--image-name to specify it)")
0d005d09
EB
160 native_sysroot = options.native_sysroot
161
10681022
BP
162 if options.kernel_dir:
163 kernel_dir = options.kernel_dir
164
522ac218 165 if not options.vars_dir and (not native_sysroot or not os.path.isdir(native_sysroot)):
f7d9e33a 166 logger.info("Building wic-tools...\n")
0507ec4c 167 subprocess.check_call(["bitbake", "wic-tools"])
0d005d09 168 native_sysroot = get_bitbake_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
522ac218
EB
169
170 if not native_sysroot:
171 raise WicError("Unable to find the location of the native tools sysroot")
75ae0b7c 172
4fe9635c 173 wks_file = options.wks_file
95455ae4
TZ
174
175 if not wks_file.endswith(".wks"):
13416c19 176 wks_file = engine.find_canned_image(scripts_path, wks_file)
95455ae4 177 if not wks_file:
b7c19f1e
EB
178 raise WicError("No image named %s found, exiting. (Use 'wic list images' "
179 "to list available images, or specify a fully-qualified OE "
4fe9635c 180 "kickstart (.wks) filename)" % options.wks_file)
95455ae4 181
95455ae4 182 if not options.image_name:
719d093c
JHFF
183 rootfs_dir = ''
184 if 'ROOTFS_DIR' in options.rootfs_dir:
185 rootfs_dir = options.rootfs_dir['ROOTFS_DIR']
95455ae4
TZ
186 bootimg_dir = options.bootimg_dir
187 kernel_dir = options.kernel_dir
188 native_sysroot = options.native_sysroot
719d093c 189 if rootfs_dir and not os.path.isdir(rootfs_dir):
131629ca 190 raise WicError("--rootfs-dir (-r) not found, exiting")
24a585e3 191 if not os.path.isdir(bootimg_dir):
b7c19f1e 192 raise WicError("--bootimg-dir (-b) not found, exiting")
24a585e3 193 if not os.path.isdir(kernel_dir):
b7c19f1e 194 raise WicError("--kernel-dir (-k) not found, exiting")
24a585e3 195 if not os.path.isdir(native_sysroot):
b7c19f1e 196 raise WicError("--native-sysroot (-n) not found, exiting")
a5b9ccad
TZ
197 else:
198 not_found = not_found_dir = ""
199 if not os.path.isdir(rootfs_dir):
200 (not_found, not_found_dir) = ("rootfs-dir", rootfs_dir)
a5b9ccad
TZ
201 elif not os.path.isdir(kernel_dir):
202 (not_found, not_found_dir) = ("kernel-dir", kernel_dir)
203 elif not os.path.isdir(native_sysroot):
204 (not_found, not_found_dir) = ("native-sysroot", native_sysroot)
205 if not_found:
206 if not not_found_dir:
207 not_found_dir = "Completely missing artifact - wrong image (.wks) used?"
b7c19f1e 208 logger.info("Build artifacts not found, exiting.")
f7d9e33a
EB
209 logger.info(" (Please check that the build artifacts for the machine")
210 logger.info(" selected in local.conf actually exist and that they")
211 logger.info(" are the correct artifacts for the image (.wks file)).\n")
e104c2b1 212 raise WicError("The artifact that couldn't be found was %s:\n %s" % (not_found, not_found_dir))
95455ae4 213
719d093c
JHFF
214 krootfs_dir = options.rootfs_dir
215 if krootfs_dir is None:
ee71acc6
EB
216 krootfs_dir = {}
217 krootfs_dir['ROOTFS_DIR'] = rootfs_dir
719d093c
JHFF
218
219 rootfs_dir = rootfs_dir_to_args(krootfs_dir)
220
f7d9e33a 221 logger.info("Creating image(s)...\n")
6b81c898 222 engine.wic_create(wks_file, rootfs_dir, bootimg_dir, kernel_dir,
f9f72c50 223 native_sysroot, options)
95455ae4
TZ
224
225
226def wic_list_subcommand(args, usage_str):
227 """
af0a6d54
EB
228 Command-line handling for listing available images.
229 The real work is done by image.engine.wic_list()
95455ae4 230 """
af0a6d54 231 if not engine.wic_list(args, scripts_path):
b7c19f1e 232 raise WicError("Bad list arguments, exiting")
95455ae4
TZ
233
234
ba461346
EB
235def wic_ls_subcommand(args, usage_str):
236 """
237 Command-line handling for list content of images.
238 The real work is done by engine.wic_ls()
239 """
240 engine.wic_ls(args, args.native_sysroot)
241
f0dcf39d
EB
242def wic_cp_subcommand(args, usage_str):
243 """
244 Command-line handling for copying files/dirs to images.
245 The real work is done by engine.wic_cp()
246 """
247 engine.wic_cp(args, args.native_sysroot)
ba461346 248
f8e42c13
EB
249def wic_rm_subcommand(args, usage_str):
250 """
251 Command-line handling for removing files/dirs from images.
252 The real work is done by engine.wic_rm()
253 """
254 engine.wic_rm(args, args.native_sysroot)
255
ac5fc0d6
EB
256def wic_write_subcommand(args, usage_str):
257 """
258 Command-line handling for writing images.
259 The real work is done by engine.wic_write()
260 """
261 engine.wic_write(args, args.native_sysroot)
262
4fe9635c 263def wic_help_subcommand(args, usage_str):
d03f39a9 264 """
4fe9635c
AR
265 Command-line handling for help subcommand to keep the current
266 structure of the function definitions.
d03f39a9
TZ
267 """
268 pass
269
270
4fe9635c
AR
271def wic_help_topic_subcommand(usage_str, help_str):
272 """
273 Display function for help 'sub-subcommands'.
274 """
275 print(help_str)
276 return
277
278
d03f39a9
TZ
279wic_help_topic_usage = """
280"""
281
4fe9635c 282helptopics = {
136137ec
TZ
283 "plugins": [wic_help_topic_subcommand,
284 wic_help_topic_usage,
4fe9635c 285 hlp.wic_plugins_help],
13416c19 286 "overview": [wic_help_topic_subcommand,
136137ec 287 wic_help_topic_usage,
13416c19
EB
288 hlp.wic_overview_help],
289 "kickstart": [wic_help_topic_subcommand,
136137ec 290 wic_help_topic_usage,
13416c19 291 hlp.wic_kickstart_help],
4fe9635c
AR
292 "create": [wic_help_topic_subcommand,
293 wic_help_topic_usage,
294 hlp.wic_create_help],
ba461346
EB
295 "ls": [wic_help_topic_subcommand,
296 wic_help_topic_usage,
297 hlp.wic_ls_help],
f0dcf39d
EB
298 "cp": [wic_help_topic_subcommand,
299 wic_help_topic_usage,
300 hlp.wic_cp_help],
f8e42c13
EB
301 "rm": [wic_help_topic_subcommand,
302 wic_help_topic_usage,
303 hlp.wic_rm_help],
ac5fc0d6
EB
304 "write": [wic_help_topic_subcommand,
305 wic_help_topic_usage,
306 hlp.wic_write_help],
4fe9635c
AR
307 "list": [wic_help_topic_subcommand,
308 wic_help_topic_usage,
309 hlp.wic_list_help]
95455ae4
TZ
310}
311
312
4fe9635c
AR
313def wic_init_parser_create(subparser):
314 subparser.add_argument("wks_file")
315
316 subparser.add_argument("-o", "--outdir", dest="outdir", default='.',
317 help="name of directory to create image in")
2e7314ac
PB
318 subparser.add_argument("-w", "--workdir",
319 help="temporary workdir to use for intermediate files")
4fe9635c
AR
320 subparser.add_argument("-e", "--image-name", dest="image_name",
321 help="name of the image to use the artifacts from "
322 "e.g. core-image-sato")
323 subparser.add_argument("-r", "--rootfs-dir", action=RootfsArgAction,
324 help="path to the /rootfs dir to use as the "
325 ".wks rootfs source")
326 subparser.add_argument("-b", "--bootimg-dir", dest="bootimg_dir",
327 help="path to the dir containing the boot artifacts "
328 "(e.g. /EFI or /syslinux dirs) to use as the "
329 ".wks bootimg source")
330 subparser.add_argument("-k", "--kernel-dir", dest="kernel_dir",
331 help="path to the dir containing the kernel to use "
332 "in the .wks bootimg")
333 subparser.add_argument("-n", "--native-sysroot", dest="native_sysroot",
334 help="path to the native sysroot containing the tools "
335 "to use to build the image")
336 subparser.add_argument("-s", "--skip-build-check", dest="build_check",
337 action="store_false", default=True, help="skip the build check")
338 subparser.add_argument("-f", "--build-rootfs", action="store_true", help="build rootfs")
339 subparser.add_argument("-c", "--compress-with", choices=("gzip", "bzip2", "xz"),
340 dest='compressor',
341 help="compress image with specified compressor")
342 subparser.add_argument("-m", "--bmap", action="store_true", help="generate .bmap")
00420ec4
FB
343 subparser.add_argument("--no-fstab-update" ,action="store_true",
344 help="Do not change fstab file.")
4fe9635c
AR
345 subparser.add_argument("-v", "--vars", dest='vars_dir',
346 help="directory with <image>.env files that store "
347 "bitbake variables")
348 subparser.add_argument("-D", "--debug", dest="debug", action="store_true",
349 default=False, help="output debug information")
f24c3538
DM
350 subparser.add_argument("-i", "--imager", dest="imager",
351 default="direct", help="the wic imager plugin")
f81b188b
KK
352 subparser.add_argument("--extra-space", type=int, dest="extra_space",
353 default=0, help="additional free disk space to add to the image")
4fe9635c
AR
354 return
355
356
357def wic_init_parser_list(subparser):
358 subparser.add_argument("list_type",
359 help="can be 'images' or 'source-plugins' "
360 "to obtain a list. "
361 "If value is a valid .wks image file")
362 subparser.add_argument("help_for", default=[], nargs='*',
363 help="If 'list_type' is a valid .wks image file "
364 "this value can be 'help' to show the help information "
365 "defined inside the .wks file")
366 return
367
8db6f74b
EB
368def imgtype(arg):
369 """
370 Custom type for ArgumentParser
371 Converts path spec to named tuple: (image, partition, path)
372 """
373 image = arg
374 part = path = None
375 if ':' in image:
376 image, part = image.split(':')
377 if '/' in part:
378 part, path = part.split('/', 1)
df5152c8
EB
379 if not path:
380 path = '/'
8db6f74b
EB
381
382 if not os.path.isfile(image):
383 err = "%s is not a regular file or symlink" % image
384 raise argparse.ArgumentTypeError(err)
385
386 return namedtuple('ImgType', 'image part path')(image, part, path)
387
388def wic_init_parser_ls(subparser):
389 subparser.add_argument("path", type=imgtype,
390 help="image spec: <image>[:<vfat partition>[<path>]]")
391 subparser.add_argument("-n", "--native-sysroot",
392 help="path to the native sysroot containing the tools")
4fe9635c 393
df5152c8
EB
394def imgpathtype(arg):
395 img = imgtype(arg)
396 if img.part is None:
397 raise argparse.ArgumentTypeError("partition number is not specified")
398 return img
399
400def wic_init_parser_cp(subparser):
401 subparser.add_argument("src",
bd669c18
CYL
402 help="image spec: <image>:<vfat partition>[<path>] or <file>")
403 subparser.add_argument("dest",
404 help="image spec: <image>:<vfat partition>[<path>] or <file>")
df5152c8
EB
405 subparser.add_argument("-n", "--native-sysroot",
406 help="path to the native sysroot containing the tools")
407
234b20ae
EB
408def wic_init_parser_rm(subparser):
409 subparser.add_argument("path", type=imgpathtype,
410 help="path: <image>:<vfat partition><path>")
411 subparser.add_argument("-n", "--native-sysroot",
412 help="path to the native sysroot containing the tools")
5cb7a329
CYL
413 subparser.add_argument("-r", dest="recursive_delete", action="store_true", default=False,
414 help="remove directories and their contents recursively, "
415 " this only applies to ext* partition")
234b20ae 416
ac5fc0d6
EB
417def expandtype(rules):
418 """
419 Custom type for ArgumentParser
420 Converts expand rules to the dictionary {<partition>: size}
421 """
422 if rules == 'auto':
423 return {}
424 result = {}
a210e28b 425 for rule in rules.split(','):
ac5fc0d6
EB
426 try:
427 part, size = rule.split(':')
428 except ValueError:
429 raise argparse.ArgumentTypeError("Incorrect rule format: %s" % rule)
430
431 if not part.isdigit():
432 raise argparse.ArgumentTypeError("Rule '%s': partition number must be integer" % rule)
433
434 # validate size
435 multiplier = 1
436 for suffix, mult in [('K', 1024), ('M', 1024 * 1024), ('G', 1024 * 1024 * 1024)]:
437 if size.upper().endswith(suffix):
438 multiplier = mult
439 size = size[:-1]
440 break
441 if not size.isdigit():
442 raise argparse.ArgumentTypeError("Rule '%s': size must be integer" % rule)
443
444 result[int(part)] = int(size) * multiplier
445
446 return result
447
448def wic_init_parser_write(subparser):
449 subparser.add_argument("image",
450 help="path to the wic image")
451 subparser.add_argument("target",
452 help="target file or device")
453 subparser.add_argument("-e", "--expand", type=expandtype,
454 help="expand rules: auto or <partition>:<size>[,<partition>:<size>]")
455 subparser.add_argument("-n", "--native-sysroot",
456 help="path to the native sysroot containing the tools")
457
4fe9635c
AR
458def wic_init_parser_help(subparser):
459 helpparsers = subparser.add_subparsers(dest='help_topic', help=hlp.wic_usage)
460 for helptopic in helptopics:
461 helpparsers.add_parser(helptopic, help=helptopics[helptopic][2])
462 return
463
464
465subcommands = {
466 "create": [wic_create_subcommand,
467 hlp.wic_create_usage,
468 hlp.wic_create_help,
469 wic_init_parser_create],
470 "list": [wic_list_subcommand,
471 hlp.wic_list_usage,
472 hlp.wic_list_help,
473 wic_init_parser_list],
ba461346
EB
474 "ls": [wic_ls_subcommand,
475 hlp.wic_ls_usage,
476 hlp.wic_ls_help,
477 wic_init_parser_ls],
f0dcf39d
EB
478 "cp": [wic_cp_subcommand,
479 hlp.wic_cp_usage,
480 hlp.wic_cp_help,
481 wic_init_parser_cp],
f8e42c13
EB
482 "rm": [wic_rm_subcommand,
483 hlp.wic_rm_usage,
484 hlp.wic_rm_help,
485 wic_init_parser_rm],
ac5fc0d6
EB
486 "write": [wic_write_subcommand,
487 hlp.wic_write_usage,
488 hlp.wic_write_help,
489 wic_init_parser_write],
4fe9635c
AR
490 "help": [wic_help_subcommand,
491 wic_help_topic_usage,
492 hlp.wic_help_help,
493 wic_init_parser_help]
494}
495
496
497def init_parser(parser):
498 parser.add_argument("--version", action="version",
499 version="%(prog)s {version}".format(version=__version__))
4e23b22b
RB
500 parser.add_argument("-D", "--debug", dest="debug", action="store_true",
501 default=False, help="output debug information")
502
4fe9635c
AR
503 subparsers = parser.add_subparsers(dest='command', help=hlp.wic_usage)
504 for subcmd in subcommands:
505 subparser = subparsers.add_parser(subcmd, help=subcommands[subcmd][2])
506 subcommands[subcmd][3](subparser)
507
d29d553c
CYL
508class WicArgumentParser(argparse.ArgumentParser):
509 def format_help(self):
510 return hlp.wic_help
4fe9635c 511
8cb9ac37 512def main(argv):
d29d553c 513 parser = WicArgumentParser(
4fe9635c 514 description="wic version %s" % __version__)
95455ae4 515
4fe9635c 516 init_parser(parser)
95455ae4 517
4fe9635c 518 args = parser.parse_args(argv)
d29d553c 519
4e23b22b
RB
520 if args.debug:
521 logger.setLevel(logging.DEBUG)
95455ae4 522
4fe9635c
AR
523 if "command" in vars(args):
524 if args.command == "help":
525 if args.help_topic is None:
95455ae4 526 parser.print_help()
4fe9635c
AR
527 elif args.help_topic in helptopics:
528 hlpt = helptopics[args.help_topic]
529 hlpt[0](hlpt[1], hlpt[2])
530 return 0
95455ae4 531
bd669c18
CYL
532 # validate wic cp src and dest parameter to identify which one of it is
533 # image and cast it into imgtype
534 if args.command == "cp":
535 if ":" in args.dest:
536 args.dest = imgtype(args.dest)
537 elif ":" in args.src:
538 args.src = imgtype(args.src)
539 else:
540 raise argparse.ArgumentTypeError("no image or partition number specified.")
541
ebd9f7b1 542 return hlp.invoke_subcommand(args, parser, hlp.wic_help_usage, subcommands)
95455ae4
TZ
543
544
545if __name__ == "__main__":
546 try:
e5e2c18c
EB
547 sys.exit(main(sys.argv[1:]))
548 except WicError as err:
b7c19f1e 549 print()
f7d9e33a 550 logger.error(err)
e5e2c18c 551 sys.exit(1)