]> git.ipfire.org Git - thirdparty/openembedded/openembedded-core.git/blame - scripts/wic
conf/licenses: Add FreeType SPDX mapping
[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
8db6f74b
EB
25
26from collections import namedtuple
9d86eb2e 27from distutils import spawn
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
9d86eb2e
EB
50bitbake_exe = spawn.find_executable('bitbake')
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
522ac218 162 if not options.vars_dir and (not native_sysroot or not os.path.isdir(native_sysroot)):
f7d9e33a 163 logger.info("Building wic-tools...\n")
0507ec4c 164 subprocess.check_call(["bitbake", "wic-tools"])
0d005d09 165 native_sysroot = get_bitbake_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
522ac218
EB
166
167 if not native_sysroot:
168 raise WicError("Unable to find the location of the native tools sysroot")
75ae0b7c 169
4fe9635c 170 wks_file = options.wks_file
95455ae4
TZ
171
172 if not wks_file.endswith(".wks"):
13416c19 173 wks_file = engine.find_canned_image(scripts_path, wks_file)
95455ae4 174 if not wks_file:
b7c19f1e
EB
175 raise WicError("No image named %s found, exiting. (Use 'wic list images' "
176 "to list available images, or specify a fully-qualified OE "
4fe9635c 177 "kickstart (.wks) filename)" % options.wks_file)
95455ae4 178
95455ae4 179 if not options.image_name:
719d093c
JHFF
180 rootfs_dir = ''
181 if 'ROOTFS_DIR' in options.rootfs_dir:
182 rootfs_dir = options.rootfs_dir['ROOTFS_DIR']
95455ae4
TZ
183 bootimg_dir = options.bootimg_dir
184 kernel_dir = options.kernel_dir
185 native_sysroot = options.native_sysroot
719d093c 186 if rootfs_dir and not os.path.isdir(rootfs_dir):
131629ca 187 raise WicError("--rootfs-dir (-r) not found, exiting")
24a585e3 188 if not os.path.isdir(bootimg_dir):
b7c19f1e 189 raise WicError("--bootimg-dir (-b) not found, exiting")
24a585e3 190 if not os.path.isdir(kernel_dir):
b7c19f1e 191 raise WicError("--kernel-dir (-k) not found, exiting")
24a585e3 192 if not os.path.isdir(native_sysroot):
b7c19f1e 193 raise WicError("--native-sysroot (-n) not found, exiting")
a5b9ccad
TZ
194 else:
195 not_found = not_found_dir = ""
196 if not os.path.isdir(rootfs_dir):
197 (not_found, not_found_dir) = ("rootfs-dir", rootfs_dir)
a5b9ccad
TZ
198 elif not os.path.isdir(kernel_dir):
199 (not_found, not_found_dir) = ("kernel-dir", kernel_dir)
200 elif not os.path.isdir(native_sysroot):
201 (not_found, not_found_dir) = ("native-sysroot", native_sysroot)
202 if not_found:
203 if not not_found_dir:
204 not_found_dir = "Completely missing artifact - wrong image (.wks) used?"
b7c19f1e 205 logger.info("Build artifacts not found, exiting.")
f7d9e33a
EB
206 logger.info(" (Please check that the build artifacts for the machine")
207 logger.info(" selected in local.conf actually exist and that they")
208 logger.info(" are the correct artifacts for the image (.wks file)).\n")
b7c19f1e 209 raise WicError("The artifact that couldn't be found was %s:\n %s", not_found, not_found_dir)
95455ae4 210
719d093c
JHFF
211 krootfs_dir = options.rootfs_dir
212 if krootfs_dir is None:
ee71acc6
EB
213 krootfs_dir = {}
214 krootfs_dir['ROOTFS_DIR'] = rootfs_dir
719d093c
JHFF
215
216 rootfs_dir = rootfs_dir_to_args(krootfs_dir)
217
f7d9e33a 218 logger.info("Creating image(s)...\n")
6b81c898 219 engine.wic_create(wks_file, rootfs_dir, bootimg_dir, kernel_dir,
f9f72c50 220 native_sysroot, options)
95455ae4
TZ
221
222
223def wic_list_subcommand(args, usage_str):
224 """
af0a6d54
EB
225 Command-line handling for listing available images.
226 The real work is done by image.engine.wic_list()
95455ae4 227 """
af0a6d54 228 if not engine.wic_list(args, scripts_path):
b7c19f1e 229 raise WicError("Bad list arguments, exiting")
95455ae4
TZ
230
231
ba461346
EB
232def wic_ls_subcommand(args, usage_str):
233 """
234 Command-line handling for list content of images.
235 The real work is done by engine.wic_ls()
236 """
237 engine.wic_ls(args, args.native_sysroot)
238
f0dcf39d
EB
239def wic_cp_subcommand(args, usage_str):
240 """
241 Command-line handling for copying files/dirs to images.
242 The real work is done by engine.wic_cp()
243 """
244 engine.wic_cp(args, args.native_sysroot)
ba461346 245
f8e42c13
EB
246def wic_rm_subcommand(args, usage_str):
247 """
248 Command-line handling for removing files/dirs from images.
249 The real work is done by engine.wic_rm()
250 """
251 engine.wic_rm(args, args.native_sysroot)
252
ac5fc0d6
EB
253def wic_write_subcommand(args, usage_str):
254 """
255 Command-line handling for writing images.
256 The real work is done by engine.wic_write()
257 """
258 engine.wic_write(args, args.native_sysroot)
259
4fe9635c 260def wic_help_subcommand(args, usage_str):
d03f39a9 261 """
4fe9635c
AR
262 Command-line handling for help subcommand to keep the current
263 structure of the function definitions.
d03f39a9
TZ
264 """
265 pass
266
267
4fe9635c
AR
268def wic_help_topic_subcommand(usage_str, help_str):
269 """
270 Display function for help 'sub-subcommands'.
271 """
272 print(help_str)
273 return
274
275
d03f39a9
TZ
276wic_help_topic_usage = """
277"""
278
4fe9635c 279helptopics = {
136137ec
TZ
280 "plugins": [wic_help_topic_subcommand,
281 wic_help_topic_usage,
4fe9635c 282 hlp.wic_plugins_help],
13416c19 283 "overview": [wic_help_topic_subcommand,
136137ec 284 wic_help_topic_usage,
13416c19
EB
285 hlp.wic_overview_help],
286 "kickstart": [wic_help_topic_subcommand,
136137ec 287 wic_help_topic_usage,
13416c19 288 hlp.wic_kickstart_help],
4fe9635c
AR
289 "create": [wic_help_topic_subcommand,
290 wic_help_topic_usage,
291 hlp.wic_create_help],
ba461346
EB
292 "ls": [wic_help_topic_subcommand,
293 wic_help_topic_usage,
294 hlp.wic_ls_help],
f0dcf39d
EB
295 "cp": [wic_help_topic_subcommand,
296 wic_help_topic_usage,
297 hlp.wic_cp_help],
f8e42c13
EB
298 "rm": [wic_help_topic_subcommand,
299 wic_help_topic_usage,
300 hlp.wic_rm_help],
ac5fc0d6
EB
301 "write": [wic_help_topic_subcommand,
302 wic_help_topic_usage,
303 hlp.wic_write_help],
4fe9635c
AR
304 "list": [wic_help_topic_subcommand,
305 wic_help_topic_usage,
306 hlp.wic_list_help]
95455ae4
TZ
307}
308
309
4fe9635c
AR
310def wic_init_parser_create(subparser):
311 subparser.add_argument("wks_file")
312
313 subparser.add_argument("-o", "--outdir", dest="outdir", default='.',
314 help="name of directory to create image in")
2e7314ac
PB
315 subparser.add_argument("-w", "--workdir",
316 help="temporary workdir to use for intermediate files")
4fe9635c
AR
317 subparser.add_argument("-e", "--image-name", dest="image_name",
318 help="name of the image to use the artifacts from "
319 "e.g. core-image-sato")
320 subparser.add_argument("-r", "--rootfs-dir", action=RootfsArgAction,
321 help="path to the /rootfs dir to use as the "
322 ".wks rootfs source")
323 subparser.add_argument("-b", "--bootimg-dir", dest="bootimg_dir",
324 help="path to the dir containing the boot artifacts "
325 "(e.g. /EFI or /syslinux dirs) to use as the "
326 ".wks bootimg source")
327 subparser.add_argument("-k", "--kernel-dir", dest="kernel_dir",
328 help="path to the dir containing the kernel to use "
329 "in the .wks bootimg")
330 subparser.add_argument("-n", "--native-sysroot", dest="native_sysroot",
331 help="path to the native sysroot containing the tools "
332 "to use to build the image")
333 subparser.add_argument("-s", "--skip-build-check", dest="build_check",
334 action="store_false", default=True, help="skip the build check")
335 subparser.add_argument("-f", "--build-rootfs", action="store_true", help="build rootfs")
336 subparser.add_argument("-c", "--compress-with", choices=("gzip", "bzip2", "xz"),
337 dest='compressor',
338 help="compress image with specified compressor")
339 subparser.add_argument("-m", "--bmap", action="store_true", help="generate .bmap")
00420ec4
FB
340 subparser.add_argument("--no-fstab-update" ,action="store_true",
341 help="Do not change fstab file.")
4fe9635c
AR
342 subparser.add_argument("-v", "--vars", dest='vars_dir',
343 help="directory with <image>.env files that store "
344 "bitbake variables")
345 subparser.add_argument("-D", "--debug", dest="debug", action="store_true",
346 default=False, help="output debug information")
f24c3538
DM
347 subparser.add_argument("-i", "--imager", dest="imager",
348 default="direct", help="the wic imager plugin")
4fe9635c
AR
349 return
350
351
352def wic_init_parser_list(subparser):
353 subparser.add_argument("list_type",
354 help="can be 'images' or 'source-plugins' "
355 "to obtain a list. "
356 "If value is a valid .wks image file")
357 subparser.add_argument("help_for", default=[], nargs='*',
358 help="If 'list_type' is a valid .wks image file "
359 "this value can be 'help' to show the help information "
360 "defined inside the .wks file")
361 return
362
8db6f74b
EB
363def imgtype(arg):
364 """
365 Custom type for ArgumentParser
366 Converts path spec to named tuple: (image, partition, path)
367 """
368 image = arg
369 part = path = None
370 if ':' in image:
371 image, part = image.split(':')
372 if '/' in part:
373 part, path = part.split('/', 1)
df5152c8
EB
374 if not path:
375 path = '/'
8db6f74b
EB
376
377 if not os.path.isfile(image):
378 err = "%s is not a regular file or symlink" % image
379 raise argparse.ArgumentTypeError(err)
380
381 return namedtuple('ImgType', 'image part path')(image, part, path)
382
383def wic_init_parser_ls(subparser):
384 subparser.add_argument("path", type=imgtype,
385 help="image spec: <image>[:<vfat partition>[<path>]]")
386 subparser.add_argument("-n", "--native-sysroot",
387 help="path to the native sysroot containing the tools")
4fe9635c 388
df5152c8
EB
389def imgpathtype(arg):
390 img = imgtype(arg)
391 if img.part is None:
392 raise argparse.ArgumentTypeError("partition number is not specified")
393 return img
394
395def wic_init_parser_cp(subparser):
396 subparser.add_argument("src",
bd669c18
CYL
397 help="image spec: <image>:<vfat partition>[<path>] or <file>")
398 subparser.add_argument("dest",
399 help="image spec: <image>:<vfat partition>[<path>] or <file>")
df5152c8
EB
400 subparser.add_argument("-n", "--native-sysroot",
401 help="path to the native sysroot containing the tools")
402
234b20ae
EB
403def wic_init_parser_rm(subparser):
404 subparser.add_argument("path", type=imgpathtype,
405 help="path: <image>:<vfat partition><path>")
406 subparser.add_argument("-n", "--native-sysroot",
407 help="path to the native sysroot containing the tools")
5cb7a329
CYL
408 subparser.add_argument("-r", dest="recursive_delete", action="store_true", default=False,
409 help="remove directories and their contents recursively, "
410 " this only applies to ext* partition")
234b20ae 411
ac5fc0d6
EB
412def expandtype(rules):
413 """
414 Custom type for ArgumentParser
415 Converts expand rules to the dictionary {<partition>: size}
416 """
417 if rules == 'auto':
418 return {}
419 result = {}
a210e28b 420 for rule in rules.split(','):
ac5fc0d6
EB
421 try:
422 part, size = rule.split(':')
423 except ValueError:
424 raise argparse.ArgumentTypeError("Incorrect rule format: %s" % rule)
425
426 if not part.isdigit():
427 raise argparse.ArgumentTypeError("Rule '%s': partition number must be integer" % rule)
428
429 # validate size
430 multiplier = 1
431 for suffix, mult in [('K', 1024), ('M', 1024 * 1024), ('G', 1024 * 1024 * 1024)]:
432 if size.upper().endswith(suffix):
433 multiplier = mult
434 size = size[:-1]
435 break
436 if not size.isdigit():
437 raise argparse.ArgumentTypeError("Rule '%s': size must be integer" % rule)
438
439 result[int(part)] = int(size) * multiplier
440
441 return result
442
443def wic_init_parser_write(subparser):
444 subparser.add_argument("image",
445 help="path to the wic image")
446 subparser.add_argument("target",
447 help="target file or device")
448 subparser.add_argument("-e", "--expand", type=expandtype,
449 help="expand rules: auto or <partition>:<size>[,<partition>:<size>]")
450 subparser.add_argument("-n", "--native-sysroot",
451 help="path to the native sysroot containing the tools")
452
4fe9635c
AR
453def wic_init_parser_help(subparser):
454 helpparsers = subparser.add_subparsers(dest='help_topic', help=hlp.wic_usage)
455 for helptopic in helptopics:
456 helpparsers.add_parser(helptopic, help=helptopics[helptopic][2])
457 return
458
459
460subcommands = {
461 "create": [wic_create_subcommand,
462 hlp.wic_create_usage,
463 hlp.wic_create_help,
464 wic_init_parser_create],
465 "list": [wic_list_subcommand,
466 hlp.wic_list_usage,
467 hlp.wic_list_help,
468 wic_init_parser_list],
ba461346
EB
469 "ls": [wic_ls_subcommand,
470 hlp.wic_ls_usage,
471 hlp.wic_ls_help,
472 wic_init_parser_ls],
f0dcf39d
EB
473 "cp": [wic_cp_subcommand,
474 hlp.wic_cp_usage,
475 hlp.wic_cp_help,
476 wic_init_parser_cp],
f8e42c13
EB
477 "rm": [wic_rm_subcommand,
478 hlp.wic_rm_usage,
479 hlp.wic_rm_help,
480 wic_init_parser_rm],
ac5fc0d6
EB
481 "write": [wic_write_subcommand,
482 hlp.wic_write_usage,
483 hlp.wic_write_help,
484 wic_init_parser_write],
4fe9635c
AR
485 "help": [wic_help_subcommand,
486 wic_help_topic_usage,
487 hlp.wic_help_help,
488 wic_init_parser_help]
489}
490
491
492def init_parser(parser):
493 parser.add_argument("--version", action="version",
494 version="%(prog)s {version}".format(version=__version__))
4e23b22b
RB
495 parser.add_argument("-D", "--debug", dest="debug", action="store_true",
496 default=False, help="output debug information")
497
4fe9635c
AR
498 subparsers = parser.add_subparsers(dest='command', help=hlp.wic_usage)
499 for subcmd in subcommands:
500 subparser = subparsers.add_parser(subcmd, help=subcommands[subcmd][2])
501 subcommands[subcmd][3](subparser)
502
d29d553c
CYL
503class WicArgumentParser(argparse.ArgumentParser):
504 def format_help(self):
505 return hlp.wic_help
4fe9635c 506
8cb9ac37 507def main(argv):
d29d553c 508 parser = WicArgumentParser(
4fe9635c 509 description="wic version %s" % __version__)
95455ae4 510
4fe9635c 511 init_parser(parser)
95455ae4 512
4fe9635c 513 args = parser.parse_args(argv)
d29d553c 514
4e23b22b
RB
515 if args.debug:
516 logger.setLevel(logging.DEBUG)
95455ae4 517
4fe9635c
AR
518 if "command" in vars(args):
519 if args.command == "help":
520 if args.help_topic is None:
95455ae4 521 parser.print_help()
4fe9635c
AR
522 elif args.help_topic in helptopics:
523 hlpt = helptopics[args.help_topic]
524 hlpt[0](hlpt[1], hlpt[2])
525 return 0
95455ae4 526
bd669c18
CYL
527 # validate wic cp src and dest parameter to identify which one of it is
528 # image and cast it into imgtype
529 if args.command == "cp":
530 if ":" in args.dest:
531 args.dest = imgtype(args.dest)
532 elif ":" in args.src:
533 args.src = imgtype(args.src)
534 else:
535 raise argparse.ArgumentTypeError("no image or partition number specified.")
536
ebd9f7b1 537 return hlp.invoke_subcommand(args, parser, hlp.wic_help_usage, subcommands)
95455ae4
TZ
538
539
540if __name__ == "__main__":
541 try:
e5e2c18c
EB
542 sys.exit(main(sys.argv[1:]))
543 except WicError as err:
b7c19f1e 544 print()
f7d9e33a 545 logger.error(err)
e5e2c18c 546 sys.exit(1)