3 # Copyright (c) 2013, Intel Corporation.
5 # SPDX-License-Identifier: GPL-2.0-only
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.
15 # Tom Zanussi <tom.zanussi (at] linux.intel.com>
19 # Python Standard Library modules
27 from collections
import namedtuple
30 scripts_path
= os
.path
.dirname(os
.path
.realpath(__file__
))
31 lib_path
= scripts_path
+ '/lib'
32 sys
.path
.insert(0, lib_path
)
34 scriptpath
.add_oe_lib_path()
36 # Check whether wic is running within eSDK environment
37 sdkroot
= scripts_path
38 if os
.environ
.get('SDKTARGETSYSROOT'):
39 while sdkroot
!= '' and sdkroot
!= os
.sep
:
40 if os
.path
.exists(os
.path
.join(sdkroot
, '.devtoolbase')):
41 # Set BUILDDIR for wic to work within eSDK
42 os
.environ
['BUILDDIR'] = sdkroot
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
48 sdkroot
= os
.path
.dirname(sdkroot
)
50 bitbake_exe
= shutil
.which('bitbake')
52 bitbake_path
= scriptpath
.add_bitbake_lib_path()
55 from wic
import WicError
56 from wic
.misc
import get_bitbake_var
, BB_VARS
57 from wic
import engine
58 from wic
import help as hlp
62 """Create and convfigure wic logger."""
63 logger
= logging
.getLogger('wic')
64 logger
.setLevel(logging
.INFO
)
66 handler
= logging
.StreamHandler()
68 formatter
= logging
.Formatter('%(levelname)s: %(message)s')
69 handler
.setFormatter(formatter
)
71 logger
.addHandler(handler
)
77 def rootfs_dir_to_args(krootfs_dir
):
79 Get a rootfs_dir dict and serialize to string
82 for key
, val
in krootfs_dir
.items():
84 rootfs_dir
+= '='.join([key
, val
])
85 return rootfs_dir
.strip()
88 class RootfsArgAction(argparse
.Action
):
89 def __init__(self
, **kwargs
):
90 super().__init
__(**kwargs
)
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'] = {}
98 (key
, rootfs_dir
) = value
.split('=')
103 namespace
.__dict
__['rootfs_dir'][key
] = rootfs_dir
106 def wic_create_subcommand(options
, usage_str
):
108 Command-line handling for image creation. The real work is done
109 by image.engine.wic_create()
111 if options
.build_rootfs
and not bitbake_exe
:
112 raise WicError("Can't build rootfs as bitbake is not in the $PATH")
114 if not options
.image_name
:
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')]:
123 raise WicError("The following build artifacts are not specified: %s" %
126 if options
.image_name
:
127 BB_VARS
.default_image
= options
.image_name
129 options
.build_check
= False
132 BB_VARS
.vars_dir
= options
.vars_dir
134 if options
.build_check
and not engine
.verify_build_env():
135 raise WicError("Couldn't verify build environment, exiting")
138 logger
.setLevel(logging
.DEBUG
)
140 if options
.image_name
:
141 if options
.build_rootfs
:
142 argv
= ["bitbake", options
.image_name
]
144 argv
.append("--debug")
146 logger
.info("Building rootfs...\n")
147 subprocess
.check_call(argv
)
149 rootfs_dir
= get_bitbake_var("IMAGE_ROOTFS", options
.image_name
)
150 kernel_dir
= get_bitbake_var("DEPLOY_DIR_IMAGE", options
.image_name
)
151 bootimg_dir
= get_bitbake_var("STAGING_DATADIR", options
.image_name
)
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
)
157 if options
.build_rootfs
:
158 raise WicError("Image name is not specified, exiting. "
159 "(Use -e/--image-name to specify it)")
160 native_sysroot
= options
.native_sysroot
162 if options
.kernel_dir
:
163 kernel_dir
= options
.kernel_dir
165 if not options
.vars_dir
and (not native_sysroot
or not os
.path
.isdir(native_sysroot
)):
166 logger
.info("Building wic-tools...\n")
167 subprocess
.check_call(["bitbake", "wic-tools"])
168 native_sysroot
= get_bitbake_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
170 if not native_sysroot
:
171 raise WicError("Unable to find the location of the native tools sysroot")
173 wks_file
= options
.wks_file
175 if not wks_file
.endswith(".wks"):
176 wks_file
= engine
.find_canned_image(scripts_path
, wks_file
)
178 raise WicError("No image named %s found, exiting. (Use 'wic list images' "
179 "to list available images, or specify a fully-qualified OE "
180 "kickstart (.wks) filename)" % options
.wks_file
)
182 if not options
.image_name
:
184 if 'ROOTFS_DIR' in options
.rootfs_dir
:
185 rootfs_dir
= options
.rootfs_dir
['ROOTFS_DIR']
186 bootimg_dir
= options
.bootimg_dir
187 kernel_dir
= options
.kernel_dir
188 native_sysroot
= options
.native_sysroot
189 if rootfs_dir
and not os
.path
.isdir(rootfs_dir
):
190 raise WicError("--rootfs-dir (-r) not found, exiting")
191 if not os
.path
.isdir(bootimg_dir
):
192 raise WicError("--bootimg-dir (-b) not found, exiting")
193 if not os
.path
.isdir(kernel_dir
):
194 raise WicError("--kernel-dir (-k) not found, exiting")
195 if not os
.path
.isdir(native_sysroot
):
196 raise WicError("--native-sysroot (-n) not found, exiting")
198 not_found
= not_found_dir
= ""
199 if not os
.path
.isdir(rootfs_dir
):
200 (not_found
, not_found_dir
) = ("rootfs-dir", rootfs_dir
)
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
)
206 if not not_found_dir
:
207 not_found_dir
= "Completely missing artifact - wrong image (.wks) used?"
208 logger
.info("Build artifacts not found, exiting.")
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")
212 raise WicError("The artifact that couldn't be found was %s:\n %s" % (not_found
, not_found_dir
))
214 krootfs_dir
= options
.rootfs_dir
215 if krootfs_dir
is None:
217 krootfs_dir
['ROOTFS_DIR'] = rootfs_dir
219 rootfs_dir
= rootfs_dir_to_args(krootfs_dir
)
221 logger
.info("Creating image(s)...\n")
222 engine
.wic_create(wks_file
, rootfs_dir
, bootimg_dir
, kernel_dir
,
223 native_sysroot
, options
)
226 def wic_list_subcommand(args
, usage_str
):
228 Command-line handling for listing available images.
229 The real work is done by image.engine.wic_list()
231 if not engine
.wic_list(args
, scripts_path
):
232 raise WicError("Bad list arguments, exiting")
235 def wic_ls_subcommand(args
, usage_str
):
237 Command-line handling for list content of images.
238 The real work is done by engine.wic_ls()
240 engine
.wic_ls(args
, args
.native_sysroot
)
242 def wic_cp_subcommand(args
, usage_str
):
244 Command-line handling for copying files/dirs to images.
245 The real work is done by engine.wic_cp()
247 engine
.wic_cp(args
, args
.native_sysroot
)
249 def wic_rm_subcommand(args
, usage_str
):
251 Command-line handling for removing files/dirs from images.
252 The real work is done by engine.wic_rm()
254 engine
.wic_rm(args
, args
.native_sysroot
)
256 def wic_write_subcommand(args
, usage_str
):
258 Command-line handling for writing images.
259 The real work is done by engine.wic_write()
261 engine
.wic_write(args
, args
.native_sysroot
)
263 def wic_help_subcommand(args
, usage_str
):
265 Command-line handling for help subcommand to keep the current
266 structure of the function definitions.
271 def wic_help_topic_subcommand(usage_str
, help_str
):
273 Display function for help 'sub-subcommands'.
279 wic_help_topic_usage
= """
283 "plugins": [wic_help_topic_subcommand
,
284 wic_help_topic_usage
,
285 hlp
.wic_plugins_help
],
286 "overview": [wic_help_topic_subcommand
,
287 wic_help_topic_usage
,
288 hlp
.wic_overview_help
],
289 "kickstart": [wic_help_topic_subcommand
,
290 wic_help_topic_usage
,
291 hlp
.wic_kickstart_help
],
292 "create": [wic_help_topic_subcommand
,
293 wic_help_topic_usage
,
294 hlp
.wic_create_help
],
295 "ls": [wic_help_topic_subcommand
,
296 wic_help_topic_usage
,
298 "cp": [wic_help_topic_subcommand
,
299 wic_help_topic_usage
,
301 "rm": [wic_help_topic_subcommand
,
302 wic_help_topic_usage
,
304 "write": [wic_help_topic_subcommand
,
305 wic_help_topic_usage
,
307 "list": [wic_help_topic_subcommand
,
308 wic_help_topic_usage
,
313 def wic_init_parser_create(subparser
):
314 subparser
.add_argument("wks_file")
316 subparser
.add_argument("-o", "--outdir", dest
="outdir", default
='.',
317 help="name of directory to create image in")
318 subparser
.add_argument("-w", "--workdir",
319 help="temporary workdir to use for intermediate files")
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"),
341 help="compress image with specified compressor")
342 subparser
.add_argument("-m", "--bmap", action
="store_true", help="generate .bmap")
343 subparser
.add_argument("--no-fstab-update" ,action
="store_true",
344 help="Do not change fstab file.")
345 subparser
.add_argument("-v", "--vars", dest
='vars_dir',
346 help="directory with <image>.env files that store "
348 subparser
.add_argument("-D", "--debug", dest
="debug", action
="store_true",
349 default
=False, help="output debug information")
350 subparser
.add_argument("-i", "--imager", dest
="imager",
351 default
="direct", help="the wic imager plugin")
352 subparser
.add_argument("--extra-space", type=int, dest
="extra_space",
353 default
=0, help="additional free disk space to add to the image")
357 def wic_init_parser_list(subparser
):
358 subparser
.add_argument("list_type",
359 help="can be 'images' or 'source-plugins' "
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")
370 Custom type for ArgumentParser
371 Converts path spec to named tuple: (image, partition, path)
376 image
, part
= image
.split(':')
378 part
, path
= part
.split('/', 1)
382 if not os
.path
.isfile(image
):
383 err
= "%s is not a regular file or symlink" % image
384 raise argparse
.ArgumentTypeError(err
)
386 return namedtuple('ImgType', 'image part path')(image
, part
, path
)
388 def 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")
394 def imgpathtype(arg
):
397 raise argparse
.ArgumentTypeError("partition number is not specified")
400 def wic_init_parser_cp(subparser
):
401 subparser
.add_argument("src",
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>")
405 subparser
.add_argument("-n", "--native-sysroot",
406 help="path to the native sysroot containing the tools")
408 def 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")
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")
417 def expandtype(rules
):
419 Custom type for ArgumentParser
420 Converts expand rules to the dictionary {<partition>: size}
425 for rule
in rules
.split(','):
427 part
, size
= rule
.split(':')
429 raise argparse
.ArgumentTypeError("Incorrect rule format: %s" % rule
)
431 if not part
.isdigit():
432 raise argparse
.ArgumentTypeError("Rule '%s': partition number must be integer" % rule
)
436 for suffix
, mult
in [('K', 1024), ('M', 1024 * 1024), ('G', 1024 * 1024 * 1024)]:
437 if size
.upper().endswith(suffix
):
441 if not size
.isdigit():
442 raise argparse
.ArgumentTypeError("Rule '%s': size must be integer" % rule
)
444 result
[int(part
)] = int(size
) * multiplier
448 def 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")
458 def 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])
466 "create": [wic_create_subcommand
,
467 hlp
.wic_create_usage
,
469 wic_init_parser_create
],
470 "list": [wic_list_subcommand
,
473 wic_init_parser_list
],
474 "ls": [wic_ls_subcommand
,
478 "cp": [wic_cp_subcommand
,
482 "rm": [wic_rm_subcommand
,
486 "write": [wic_write_subcommand
,
489 wic_init_parser_write
],
490 "help": [wic_help_subcommand
,
491 wic_help_topic_usage
,
493 wic_init_parser_help
]
497 def init_parser(parser
):
498 parser
.add_argument("--version", action
="version",
499 version
="%(prog)s {version}".format(version
=__version__
))
500 parser
.add_argument("-D", "--debug", dest
="debug", action
="store_true",
501 default
=False, help="output debug information")
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
)
508 class WicArgumentParser(argparse
.ArgumentParser
):
509 def format_help(self
):
513 parser
= WicArgumentParser(
514 description
="wic version %s" % __version__
)
518 args
= parser
.parse_args(argv
)
521 logger
.setLevel(logging
.DEBUG
)
523 if "command" in vars(args
):
524 if args
.command
== "help":
525 if args
.help_topic
is None:
527 elif args
.help_topic
in helptopics
:
528 hlpt
= helptopics
[args
.help_topic
]
529 hlpt
[0](hlpt
[1], hlpt
[2])
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":
536 args
.dest
= imgtype(args
.dest
)
537 elif ":" in args
.src
:
538 args
.src
= imgtype(args
.src
)
540 raise argparse
.ArgumentTypeError("no image or partition number specified.")
542 return hlp
.invoke_subcommand(args
, parser
, hlp
.wic_help_usage
, subcommands
)
545 if __name__
== "__main__":
547 sys
.exit(main(sys
.argv
[1:]))
548 except WicError
as err
: