blob: 097084a6033b8c63d234d810a4fc856b19139c60 [file] [log] [blame]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001#!/usr/bin/env python3
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002# ex:ts=4:sw=4:sts=4:et
3# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4#
5# Copyright (c) 2013, Intel Corporation.
6# All rights reserved.
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20#
21# DESCRIPTION 'wic' is the OpenEmbedded Image Creator that users can
22# use to generate bootable images. Invoking it without any arguments
23# will display help screens for the 'wic' command and list the
24# available 'wic' subcommands. Invoking a subcommand without any
25# arguments will likewise display help screens for the specified
26# subcommand. Please use that interface for detailed help.
27#
28# AUTHORS
29# Tom Zanussi <tom.zanussi (at] linux.intel.com>
30#
Patrick Williamsc124f4f2015-09-15 14:41:29 -050031__version__ = "0.2.0"
32
33# Python Standard Library modules
34import os
35import sys
Brad Bishopd7bf8c12018-02-25 22:55:05 -050036import argparse
Patrick Williamsc124f4f2015-09-15 14:41:29 -050037import logging
Brad Bishopd7bf8c12018-02-25 22:55:05 -050038
39from collections import namedtuple
Patrick Williamsc124f4f2015-09-15 14:41:29 -050040from distutils import spawn
41
42# External modules
43scripts_path = os.path.abspath(os.path.dirname(__file__))
44lib_path = scripts_path + '/lib'
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050045sys.path.insert(0, lib_path)
Brad Bishop6e60e8b2018-02-01 10:27:11 -050046oe_lib_path = os.path.join(os.path.dirname(scripts_path), 'meta', 'lib')
47sys.path.insert(0, oe_lib_path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050048
49bitbake_exe = spawn.find_executable('bitbake')
50if bitbake_exe:
51 bitbake_path = os.path.join(os.path.dirname(bitbake_exe), '../lib')
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050052 sys.path.insert(0, bitbake_path)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050053 from bb import cookerdata
54 from bb.main import bitbake_main, BitBakeConfigParameters
55else:
56 bitbake_main = None
57
Brad Bishop6e60e8b2018-02-01 10:27:11 -050058from wic import WicError
Brad Bishopd7bf8c12018-02-25 22:55:05 -050059from wic.misc import get_bitbake_var, BB_VARS
Patrick Williamsc124f4f2015-09-15 14:41:29 -050060from wic import engine
61from wic import help as hlp
62
Brad Bishop6e60e8b2018-02-01 10:27:11 -050063
64def wic_logger():
65 """Create and convfigure wic logger."""
66 logger = logging.getLogger('wic')
67 logger.setLevel(logging.INFO)
68
69 handler = logging.StreamHandler()
70
71 formatter = logging.Formatter('%(levelname)s: %(message)s')
72 handler.setFormatter(formatter)
73
74 logger.addHandler(handler)
75
76 return logger
77
78logger = wic_logger()
79
Patrick Williamsc124f4f2015-09-15 14:41:29 -050080def rootfs_dir_to_args(krootfs_dir):
81 """
82 Get a rootfs_dir dict and serialize to string
83 """
84 rootfs_dir = ''
85 for key, val in krootfs_dir.items():
86 rootfs_dir += ' '
87 rootfs_dir += '='.join([key, val])
88 return rootfs_dir.strip()
89
Patrick Williamsc124f4f2015-09-15 14:41:29 -050090
Brad Bishopd7bf8c12018-02-25 22:55:05 -050091class RootfsArgAction(argparse.Action):
92 def __init__(self, **kwargs):
93 super().__init__(**kwargs)
Patrick Williamsc124f4f2015-09-15 14:41:29 -050094
Brad Bishopd7bf8c12018-02-25 22:55:05 -050095 def __call__(self, parser, namespace, value, option_string=None):
96 if not "rootfs_dir" in vars(namespace) or \
97 not type(namespace.__dict__['rootfs_dir']) is dict:
98 namespace.__dict__['rootfs_dir'] = {}
Patrick Williamsc124f4f2015-09-15 14:41:29 -050099
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500100 if '=' in value:
101 (key, rootfs_dir) = value.split('=')
102 else:
103 key = 'ROOTFS_DIR'
104 rootfs_dir = value
105
106 namespace.__dict__['rootfs_dir'][key] = rootfs_dir
107
108
109def wic_create_subcommand(options, usage_str):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500110 """
111 Command-line handling for image creation. The real work is done
112 by image.engine.wic_create()
113 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500114 if options.build_rootfs and not bitbake_main:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500115 raise WicError("Can't build rootfs as bitbake is not in the $PATH")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500116
117 if not options.image_name:
118 missed = []
119 for val, opt in [(options.rootfs_dir, 'rootfs-dir'),
120 (options.bootimg_dir, 'bootimg-dir'),
121 (options.kernel_dir, 'kernel-dir'),
122 (options.native_sysroot, 'native-sysroot')]:
123 if not val:
124 missed.append(opt)
125 if missed:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500126 raise WicError("The following build artifacts are not specified: %s" %
127 ", ".join(missed))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500128
129 if options.image_name:
130 BB_VARS.default_image = options.image_name
131 else:
132 options.build_check = False
133
134 if options.vars_dir:
135 BB_VARS.vars_dir = options.vars_dir
136
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500137 if options.build_check and not engine.verify_build_env():
138 raise WicError("Couldn't verify build environment, exiting")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500139
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500140 if options.debug:
141 logger.setLevel(logging.DEBUG)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500142
143 if options.image_name:
144 if options.build_rootfs:
145 argv = ["bitbake", options.image_name]
146 if options.debug:
147 argv.append("--debug")
148
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500149 logger.info("Building rootfs...\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500150 if bitbake_main(BitBakeConfigParameters(argv),
151 cookerdata.CookerConfiguration()):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500152 raise WicError("bitbake exited with error")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500153
154 rootfs_dir = get_bitbake_var("IMAGE_ROOTFS", options.image_name)
155 kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE", options.image_name)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500156 bootimg_dir = get_bitbake_var("STAGING_DATADIR", options.image_name)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500157
158 native_sysroot = options.native_sysroot
159 if options.vars_dir and not native_sysroot:
160 native_sysroot = get_bitbake_var("RECIPE_SYSROOT_NATIVE", options.image_name)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500161 else:
162 if options.build_rootfs:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500163 raise WicError("Image name is not specified, exiting. "
164 "(Use -e/--image-name to specify it)")
165 native_sysroot = options.native_sysroot
166
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500167 if not options.vars_dir and (not native_sysroot or not os.path.isdir(native_sysroot)):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500168 logger.info("Building wic-tools...\n")
169 if bitbake_main(BitBakeConfigParameters("bitbake wic-tools".split()),
170 cookerdata.CookerConfiguration()):
171 raise WicError("bitbake wic-tools failed")
172 native_sysroot = get_bitbake_var("RECIPE_SYSROOT_NATIVE", "wic-tools")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500173
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500174 if not native_sysroot:
175 raise WicError("Unable to find the location of the native tools sysroot")
176
177 wks_file = options.wks_file
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500178
179 if not wks_file.endswith(".wks"):
180 wks_file = engine.find_canned_image(scripts_path, wks_file)
181 if not wks_file:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500182 raise WicError("No image named %s found, exiting. (Use 'wic list images' "
183 "to list available images, or specify a fully-qualified OE "
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500184 "kickstart (.wks) filename)" % options.wks_file)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500185
186 if not options.image_name:
187 rootfs_dir = ''
188 if 'ROOTFS_DIR' in options.rootfs_dir:
189 rootfs_dir = options.rootfs_dir['ROOTFS_DIR']
190 bootimg_dir = options.bootimg_dir
191 kernel_dir = options.kernel_dir
192 native_sysroot = options.native_sysroot
193 if rootfs_dir and not os.path.isdir(rootfs_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500194 raise WicError("--rootfs-dir (-r) not found, exiting")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500195 if not os.path.isdir(bootimg_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500196 raise WicError("--bootimg-dir (-b) not found, exiting")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500197 if not os.path.isdir(kernel_dir):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500198 raise WicError("--kernel-dir (-k) not found, exiting")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500199 if not os.path.isdir(native_sysroot):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500200 raise WicError("--native-sysroot (-n) not found, exiting")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500201 else:
202 not_found = not_found_dir = ""
203 if not os.path.isdir(rootfs_dir):
204 (not_found, not_found_dir) = ("rootfs-dir", rootfs_dir)
205 elif not os.path.isdir(kernel_dir):
206 (not_found, not_found_dir) = ("kernel-dir", kernel_dir)
207 elif not os.path.isdir(native_sysroot):
208 (not_found, not_found_dir) = ("native-sysroot", native_sysroot)
209 if not_found:
210 if not not_found_dir:
211 not_found_dir = "Completely missing artifact - wrong image (.wks) used?"
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500212 logger.info("Build artifacts not found, exiting.")
213 logger.info(" (Please check that the build artifacts for the machine")
214 logger.info(" selected in local.conf actually exist and that they")
215 logger.info(" are the correct artifacts for the image (.wks file)).\n")
216 raise WicError("The artifact that couldn't be found was %s:\n %s", not_found, not_found_dir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500217
218 krootfs_dir = options.rootfs_dir
219 if krootfs_dir is None:
220 krootfs_dir = {}
221 krootfs_dir['ROOTFS_DIR'] = rootfs_dir
222
223 rootfs_dir = rootfs_dir_to_args(krootfs_dir)
224
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500225 logger.info("Creating image(s)...\n")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500226 engine.wic_create(wks_file, rootfs_dir, bootimg_dir, kernel_dir,
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500227 native_sysroot, options)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500228
229
230def wic_list_subcommand(args, usage_str):
231 """
232 Command-line handling for listing available images.
233 The real work is done by image.engine.wic_list()
234 """
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500235 if not engine.wic_list(args, scripts_path):
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500236 raise WicError("Bad list arguments, exiting")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500237
238
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500239def wic_ls_subcommand(args, usage_str):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500240 """
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500241 Command-line handling for list content of images.
242 The real work is done by engine.wic_ls()
243 """
244 engine.wic_ls(args, args.native_sysroot)
245
246def wic_cp_subcommand(args, usage_str):
247 """
248 Command-line handling for copying files/dirs to images.
249 The real work is done by engine.wic_cp()
250 """
251 engine.wic_cp(args, args.native_sysroot)
252
253def wic_rm_subcommand(args, usage_str):
254 """
255 Command-line handling for removing files/dirs from images.
256 The real work is done by engine.wic_rm()
257 """
258 engine.wic_rm(args, args.native_sysroot)
259
260def wic_write_subcommand(args, usage_str):
261 """
262 Command-line handling for writing images.
263 The real work is done by engine.wic_write()
264 """
265 engine.wic_write(args, args.native_sysroot)
266
267def wic_help_subcommand(args, usage_str):
268 """
269 Command-line handling for help subcommand to keep the current
270 structure of the function definitions.
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500271 """
272 pass
273
274
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500275def wic_help_topic_subcommand(usage_str, help_str):
276 """
277 Display function for help 'sub-subcommands'.
278 """
279 print(help_str)
280 return
281
282
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500283wic_help_topic_usage = """
284"""
285
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500286helptopics = {
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500287 "plugins": [wic_help_topic_subcommand,
288 wic_help_topic_usage,
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500289 hlp.wic_plugins_help],
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500290 "overview": [wic_help_topic_subcommand,
291 wic_help_topic_usage,
292 hlp.wic_overview_help],
293 "kickstart": [wic_help_topic_subcommand,
294 wic_help_topic_usage,
295 hlp.wic_kickstart_help],
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500296 "create": [wic_help_topic_subcommand,
297 wic_help_topic_usage,
298 hlp.wic_create_help],
299 "ls": [wic_help_topic_subcommand,
300 wic_help_topic_usage,
301 hlp.wic_ls_help],
302 "cp": [wic_help_topic_subcommand,
303 wic_help_topic_usage,
304 hlp.wic_cp_help],
305 "rm": [wic_help_topic_subcommand,
306 wic_help_topic_usage,
307 hlp.wic_rm_help],
308 "write": [wic_help_topic_subcommand,
309 wic_help_topic_usage,
310 hlp.wic_write_help],
311 "list": [wic_help_topic_subcommand,
312 wic_help_topic_usage,
313 hlp.wic_list_help]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500314}
315
316
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500317def wic_init_parser_create(subparser):
318 subparser.add_argument("wks_file")
319
320 subparser.add_argument("-o", "--outdir", dest="outdir", default='.',
321 help="name of directory to create image in")
322 subparser.add_argument("-e", "--image-name", dest="image_name",
323 help="name of the image to use the artifacts from "
324 "e.g. core-image-sato")
325 subparser.add_argument("-r", "--rootfs-dir", action=RootfsArgAction,
326 help="path to the /rootfs dir to use as the "
327 ".wks rootfs source")
328 subparser.add_argument("-b", "--bootimg-dir", dest="bootimg_dir",
329 help="path to the dir containing the boot artifacts "
330 "(e.g. /EFI or /syslinux dirs) to use as the "
331 ".wks bootimg source")
332 subparser.add_argument("-k", "--kernel-dir", dest="kernel_dir",
333 help="path to the dir containing the kernel to use "
334 "in the .wks bootimg")
335 subparser.add_argument("-n", "--native-sysroot", dest="native_sysroot",
336 help="path to the native sysroot containing the tools "
337 "to use to build the image")
338 subparser.add_argument("-s", "--skip-build-check", dest="build_check",
339 action="store_false", default=True, help="skip the build check")
340 subparser.add_argument("-f", "--build-rootfs", action="store_true", help="build rootfs")
341 subparser.add_argument("-c", "--compress-with", choices=("gzip", "bzip2", "xz"),
342 dest='compressor',
343 help="compress image with specified compressor")
344 subparser.add_argument("-m", "--bmap", action="store_true", help="generate .bmap")
345 subparser.add_argument("--no-fstab-update" ,action="store_true",
346 help="Do not change fstab file.")
347 subparser.add_argument("-v", "--vars", dest='vars_dir',
348 help="directory with <image>.env files that store "
349 "bitbake variables")
350 subparser.add_argument("-D", "--debug", dest="debug", action="store_true",
351 default=False, help="output debug information")
352 return
353
354
355def wic_init_parser_list(subparser):
356 subparser.add_argument("list_type",
357 help="can be 'images' or 'source-plugins' "
358 "to obtain a list. "
359 "If value is a valid .wks image file")
360 subparser.add_argument("help_for", default=[], nargs='*',
361 help="If 'list_type' is a valid .wks image file "
362 "this value can be 'help' to show the help information "
363 "defined inside the .wks file")
364 return
365
366def imgtype(arg):
367 """
368 Custom type for ArgumentParser
369 Converts path spec to named tuple: (image, partition, path)
370 """
371 image = arg
372 part = path = None
373 if ':' in image:
374 image, part = image.split(':')
375 if '/' in part:
376 part, path = part.split('/', 1)
377 if not path:
378 path = '/'
379
380 if not os.path.isfile(image):
381 err = "%s is not a regular file or symlink" % image
382 raise argparse.ArgumentTypeError(err)
383
384 return namedtuple('ImgType', 'image part path')(image, part, path)
385
386def wic_init_parser_ls(subparser):
387 subparser.add_argument("path", type=imgtype,
388 help="image spec: <image>[:<vfat partition>[<path>]]")
389 subparser.add_argument("-n", "--native-sysroot",
390 help="path to the native sysroot containing the tools")
391
392def imgpathtype(arg):
393 img = imgtype(arg)
394 if img.part is None:
395 raise argparse.ArgumentTypeError("partition number is not specified")
396 return img
397
398def wic_init_parser_cp(subparser):
399 subparser.add_argument("src",
400 help="source spec")
401 subparser.add_argument("dest", type=imgpathtype,
402 help="image spec: <image>:<vfat partition>[<path>]")
403 subparser.add_argument("-n", "--native-sysroot",
404 help="path to the native sysroot containing the tools")
405
406def wic_init_parser_rm(subparser):
407 subparser.add_argument("path", type=imgpathtype,
408 help="path: <image>:<vfat partition><path>")
409 subparser.add_argument("-n", "--native-sysroot",
410 help="path to the native sysroot containing the tools")
411
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 = {}
420 for rule in rules.split('-'):
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
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],
469 "ls": [wic_ls_subcommand,
470 hlp.wic_ls_usage,
471 hlp.wic_ls_help,
472 wic_init_parser_ls],
473 "cp": [wic_cp_subcommand,
474 hlp.wic_cp_usage,
475 hlp.wic_cp_help,
476 wic_init_parser_cp],
477 "rm": [wic_rm_subcommand,
478 hlp.wic_rm_usage,
479 hlp.wic_rm_help,
480 wic_init_parser_rm],
481 "write": [wic_write_subcommand,
482 hlp.wic_write_usage,
483 hlp.wic_write_help,
484 wic_init_parser_write],
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__))
495 subparsers = parser.add_subparsers(dest='command', help=hlp.wic_usage)
496 for subcmd in subcommands:
497 subparser = subparsers.add_parser(subcmd, help=subcommands[subcmd][2])
498 subcommands[subcmd][3](subparser)
499
500
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500501def main(argv):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500502 parser = argparse.ArgumentParser(
503 description="wic version %s" % __version__)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500504
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500505 init_parser(parser)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500506
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500507 args = parser.parse_args(argv)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500508
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500509 if "command" in vars(args):
510 if args.command == "help":
511 if args.help_topic is None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500512 parser.print_help()
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500513 print()
514 print("Please specify a help topic")
515 elif args.help_topic in helptopics:
516 hlpt = helptopics[args.help_topic]
517 hlpt[0](hlpt[1], hlpt[2])
518 return 0
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500519
520 return hlp.invoke_subcommand(args, parser, hlp.wic_help_usage, subcommands)
521
522
523if __name__ == "__main__":
524 try:
525 sys.exit(main(sys.argv[1:]))
526 except WicError as err:
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500527 print()
528 logger.error(err)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500529 sys.exit(1)