| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 1 | #!/usr/bin/env python3 | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 2 | # 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 Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 31 | __version__ = "0.2.0" | 
|  | 32 |  | 
|  | 33 | # Python Standard Library modules | 
|  | 34 | import os | 
|  | 35 | import sys | 
|  | 36 | import optparse | 
|  | 37 | import logging | 
|  | 38 | from distutils import spawn | 
|  | 39 |  | 
|  | 40 | # External modules | 
|  | 41 | scripts_path = os.path.abspath(os.path.dirname(__file__)) | 
|  | 42 | lib_path = scripts_path + '/lib' | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 43 | sys.path.insert(0, lib_path) | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 44 | oe_lib_path = os.path.join(os.path.dirname(scripts_path), 'meta', 'lib') | 
|  | 45 | sys.path.insert(0, oe_lib_path) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 46 |  | 
|  | 47 | bitbake_exe = spawn.find_executable('bitbake') | 
|  | 48 | if bitbake_exe: | 
|  | 49 | bitbake_path = os.path.join(os.path.dirname(bitbake_exe), '../lib') | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 50 | sys.path.insert(0, bitbake_path) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 51 | from bb import cookerdata | 
|  | 52 | from bb.main import bitbake_main, BitBakeConfigParameters | 
|  | 53 | else: | 
|  | 54 | bitbake_main = None | 
|  | 55 |  | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 56 | from wic import WicError | 
|  | 57 | from wic.utils.misc import get_bitbake_var, BB_VARS | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 58 | from wic import engine | 
|  | 59 | from wic import help as hlp | 
|  | 60 |  | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 61 |  | 
|  | 62 | def wic_logger(): | 
|  | 63 | """Create and convfigure wic logger.""" | 
|  | 64 | logger = logging.getLogger('wic') | 
|  | 65 | logger.setLevel(logging.INFO) | 
|  | 66 |  | 
|  | 67 | handler = logging.StreamHandler() | 
|  | 68 |  | 
|  | 69 | formatter = logging.Formatter('%(levelname)s: %(message)s') | 
|  | 70 | handler.setFormatter(formatter) | 
|  | 71 |  | 
|  | 72 | logger.addHandler(handler) | 
|  | 73 |  | 
|  | 74 | return logger | 
|  | 75 |  | 
|  | 76 | logger = wic_logger() | 
|  | 77 |  | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 78 | def rootfs_dir_to_args(krootfs_dir): | 
|  | 79 | """ | 
|  | 80 | Get a rootfs_dir dict and serialize to string | 
|  | 81 | """ | 
|  | 82 | rootfs_dir = '' | 
|  | 83 | for key, val in krootfs_dir.items(): | 
|  | 84 | rootfs_dir += ' ' | 
|  | 85 | rootfs_dir += '='.join([key, val]) | 
|  | 86 | return rootfs_dir.strip() | 
|  | 87 |  | 
|  | 88 | def callback_rootfs_dir(option, opt, value, parser): | 
|  | 89 | """ | 
|  | 90 | Build a dict using --rootfs_dir connection=dir | 
|  | 91 | """ | 
|  | 92 | if not type(parser.values.rootfs_dir) is dict: | 
|  | 93 | parser.values.rootfs_dir = dict() | 
|  | 94 |  | 
|  | 95 | if '=' in value: | 
|  | 96 | (key, rootfs_dir) = value.split('=') | 
|  | 97 | else: | 
|  | 98 | key = 'ROOTFS_DIR' | 
|  | 99 | rootfs_dir = value | 
|  | 100 |  | 
|  | 101 | parser.values.rootfs_dir[key] = rootfs_dir | 
|  | 102 |  | 
|  | 103 | def wic_create_subcommand(args, usage_str): | 
|  | 104 | """ | 
|  | 105 | Command-line handling for image creation.  The real work is done | 
|  | 106 | by image.engine.wic_create() | 
|  | 107 | """ | 
|  | 108 | parser = optparse.OptionParser(usage=usage_str) | 
|  | 109 |  | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 110 | parser.add_option("-o", "--outdir", dest="outdir", default='.', | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 111 | help="name of directory to create image in") | 
|  | 112 | parser.add_option("-e", "--image-name", dest="image_name", | 
|  | 113 | help="name of the image to use the artifacts from " | 
|  | 114 | "e.g. core-image-sato") | 
|  | 115 | parser.add_option("-r", "--rootfs-dir", dest="rootfs_dir", type="string", | 
|  | 116 | action="callback", callback=callback_rootfs_dir, | 
|  | 117 | help="path to the /rootfs dir to use as the " | 
|  | 118 | ".wks rootfs source") | 
|  | 119 | parser.add_option("-b", "--bootimg-dir", dest="bootimg_dir", | 
|  | 120 | help="path to the dir containing the boot artifacts " | 
|  | 121 | "(e.g. /EFI or /syslinux dirs) to use as the " | 
|  | 122 | ".wks bootimg source") | 
|  | 123 | parser.add_option("-k", "--kernel-dir", dest="kernel_dir", | 
|  | 124 | help="path to the dir containing the kernel to use " | 
|  | 125 | "in the .wks bootimg") | 
|  | 126 | parser.add_option("-n", "--native-sysroot", dest="native_sysroot", | 
|  | 127 | help="path to the native sysroot containing the tools " | 
|  | 128 | "to use to build the image") | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 129 | parser.add_option("-s", "--skip-build-check", dest="build_check", | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 130 | action="store_false", default=True, help="skip the build check") | 
|  | 131 | parser.add_option("-f", "--build-rootfs", action="store_true", help="build rootfs") | 
|  | 132 | parser.add_option("-c", "--compress-with", choices=("gzip", "bzip2", "xz"), | 
|  | 133 | dest='compressor', | 
|  | 134 | help="compress image with specified compressor") | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 135 | parser.add_option("-m", "--bmap", action="store_true", help="generate .bmap") | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 136 | parser.add_option("-v", "--vars", dest='vars_dir', | 
|  | 137 | help="directory with <image>.env files that store " | 
|  | 138 | "bitbake variables") | 
|  | 139 | parser.add_option("-D", "--debug", dest="debug", action="store_true", | 
|  | 140 | default=False, help="output debug information") | 
|  | 141 |  | 
|  | 142 | (options, args) = parser.parse_args(args) | 
|  | 143 |  | 
|  | 144 | if len(args) != 1: | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 145 | parser.print_help() | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 146 | raise WicError("Wrong number of arguments, exiting") | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 147 |  | 
|  | 148 | if options.build_rootfs and not bitbake_main: | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 149 | raise WicError("Can't build rootfs as bitbake is not in the $PATH") | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 150 |  | 
|  | 151 | if not options.image_name: | 
|  | 152 | missed = [] | 
|  | 153 | for val, opt in [(options.rootfs_dir, 'rootfs-dir'), | 
|  | 154 | (options.bootimg_dir, 'bootimg-dir'), | 
|  | 155 | (options.kernel_dir, 'kernel-dir'), | 
|  | 156 | (options.native_sysroot, 'native-sysroot')]: | 
|  | 157 | if not val: | 
|  | 158 | missed.append(opt) | 
|  | 159 | if missed: | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 160 | raise WicError("The following build artifacts are not specified: %s" % | 
|  | 161 | ", ".join(missed)) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 162 |  | 
|  | 163 | if options.image_name: | 
|  | 164 | BB_VARS.default_image = options.image_name | 
|  | 165 | else: | 
|  | 166 | options.build_check = False | 
|  | 167 |  | 
|  | 168 | if options.vars_dir: | 
|  | 169 | BB_VARS.vars_dir = options.vars_dir | 
|  | 170 |  | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 171 | if options.build_check and not engine.verify_build_env(): | 
|  | 172 | raise WicError("Couldn't verify build environment, exiting") | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 173 |  | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 174 | if options.debug: | 
|  | 175 | logger.setLevel(logging.DEBUG) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 176 |  | 
|  | 177 | if options.image_name: | 
|  | 178 | if options.build_rootfs: | 
|  | 179 | argv = ["bitbake", options.image_name] | 
|  | 180 | if options.debug: | 
|  | 181 | argv.append("--debug") | 
|  | 182 |  | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 183 | logger.info("Building rootfs...\n") | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 184 | if bitbake_main(BitBakeConfigParameters(argv), | 
|  | 185 | cookerdata.CookerConfiguration()): | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 186 | raise WicError("bitbake exited with error") | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 187 |  | 
|  | 188 | rootfs_dir = get_bitbake_var("IMAGE_ROOTFS", options.image_name) | 
|  | 189 | kernel_dir = get_bitbake_var("DEPLOY_DIR_IMAGE", options.image_name) | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 190 | bootimg_dir = get_bitbake_var("STAGING_DATADIR", options.image_name) | 
|  | 191 | native_sysroot = get_bitbake_var("RECIPE_SYSROOT_NATIVE", | 
|  | 192 | options.image_name) #, cache=False) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 193 | else: | 
|  | 194 | if options.build_rootfs: | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 195 | raise WicError("Image name is not specified, exiting. " | 
|  | 196 | "(Use -e/--image-name to specify it)") | 
|  | 197 | native_sysroot = options.native_sysroot | 
|  | 198 |  | 
|  | 199 | if not native_sysroot or not os.path.isdir(native_sysroot): | 
|  | 200 | logger.info("Building wic-tools...\n") | 
|  | 201 | if bitbake_main(BitBakeConfigParameters("bitbake wic-tools".split()), | 
|  | 202 | cookerdata.CookerConfiguration()): | 
|  | 203 | raise WicError("bitbake wic-tools failed") | 
|  | 204 | native_sysroot = get_bitbake_var("RECIPE_SYSROOT_NATIVE", "wic-tools") | 
|  | 205 | if not native_sysroot: | 
|  | 206 | raise WicError("Unable to find the location of the native " | 
|  | 207 | "tools sysroot to use") | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 208 |  | 
|  | 209 | wks_file = args[0] | 
|  | 210 |  | 
|  | 211 | if not wks_file.endswith(".wks"): | 
|  | 212 | wks_file = engine.find_canned_image(scripts_path, wks_file) | 
|  | 213 | if not wks_file: | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 214 | raise WicError("No image named %s found, exiting.  (Use 'wic list images' " | 
|  | 215 | "to list available images, or specify a fully-qualified OE " | 
|  | 216 | "kickstart (.wks) filename)" % args[0]) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 217 |  | 
|  | 218 | if not options.image_name: | 
|  | 219 | rootfs_dir = '' | 
|  | 220 | if 'ROOTFS_DIR' in options.rootfs_dir: | 
|  | 221 | rootfs_dir = options.rootfs_dir['ROOTFS_DIR'] | 
|  | 222 | bootimg_dir = options.bootimg_dir | 
|  | 223 | kernel_dir = options.kernel_dir | 
|  | 224 | native_sysroot = options.native_sysroot | 
|  | 225 | if rootfs_dir and not os.path.isdir(rootfs_dir): | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 226 | raise WicError("--rootfs-dir (-r) not found, exiting") | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 227 | if not os.path.isdir(bootimg_dir): | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 228 | raise WicError("--bootimg-dir (-b) not found, exiting") | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 229 | if not os.path.isdir(kernel_dir): | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 230 | raise WicError("--kernel-dir (-k) not found, exiting") | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 231 | if not os.path.isdir(native_sysroot): | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 232 | raise WicError("--native-sysroot (-n) not found, exiting") | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 233 | else: | 
|  | 234 | not_found = not_found_dir = "" | 
|  | 235 | if not os.path.isdir(rootfs_dir): | 
|  | 236 | (not_found, not_found_dir) = ("rootfs-dir", rootfs_dir) | 
|  | 237 | elif not os.path.isdir(kernel_dir): | 
|  | 238 | (not_found, not_found_dir) = ("kernel-dir", kernel_dir) | 
|  | 239 | elif not os.path.isdir(native_sysroot): | 
|  | 240 | (not_found, not_found_dir) = ("native-sysroot", native_sysroot) | 
|  | 241 | if not_found: | 
|  | 242 | if not not_found_dir: | 
|  | 243 | not_found_dir = "Completely missing artifact - wrong image (.wks) used?" | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 244 | logger.info("Build artifacts not found, exiting.") | 
|  | 245 | logger.info("  (Please check that the build artifacts for the machine") | 
|  | 246 | logger.info("   selected in local.conf actually exist and that they") | 
|  | 247 | logger.info("   are the correct artifacts for the image (.wks file)).\n") | 
|  | 248 | raise WicError("The artifact that couldn't be found was %s:\n  %s", not_found, not_found_dir) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 249 |  | 
|  | 250 | krootfs_dir = options.rootfs_dir | 
|  | 251 | if krootfs_dir is None: | 
|  | 252 | krootfs_dir = {} | 
|  | 253 | krootfs_dir['ROOTFS_DIR'] = rootfs_dir | 
|  | 254 |  | 
|  | 255 | rootfs_dir = rootfs_dir_to_args(krootfs_dir) | 
|  | 256 |  | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 257 | logger.info("Creating image(s)...\n") | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 258 | engine.wic_create(wks_file, rootfs_dir, bootimg_dir, kernel_dir, | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 259 | native_sysroot, options) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 260 |  | 
|  | 261 |  | 
|  | 262 | def wic_list_subcommand(args, usage_str): | 
|  | 263 | """ | 
|  | 264 | Command-line handling for listing available images. | 
|  | 265 | The real work is done by image.engine.wic_list() | 
|  | 266 | """ | 
|  | 267 | parser = optparse.OptionParser(usage=usage_str) | 
|  | 268 | args = parser.parse_args(args)[1] | 
|  | 269 |  | 
|  | 270 | if not engine.wic_list(args, scripts_path): | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 271 | parser.print_help() | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 272 | raise WicError("Bad list arguments, exiting") | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 273 |  | 
|  | 274 |  | 
|  | 275 | def wic_help_topic_subcommand(args, usage_str): | 
|  | 276 | """ | 
|  | 277 | Command-line handling for help-only 'subcommands'.  This is | 
|  | 278 | essentially a dummy command that doesn nothing but allow users to | 
|  | 279 | use the existing subcommand infrastructure to display help on a | 
|  | 280 | particular topic not attached to any particular subcommand. | 
|  | 281 | """ | 
|  | 282 | pass | 
|  | 283 |  | 
|  | 284 |  | 
|  | 285 | wic_help_topic_usage = """ | 
|  | 286 | """ | 
|  | 287 |  | 
|  | 288 | subcommands = { | 
|  | 289 | "create":    [wic_create_subcommand, | 
|  | 290 | hlp.wic_create_usage, | 
|  | 291 | hlp.wic_create_help], | 
|  | 292 | "list":      [wic_list_subcommand, | 
|  | 293 | hlp.wic_list_usage, | 
|  | 294 | hlp.wic_list_help], | 
|  | 295 | "plugins":   [wic_help_topic_subcommand, | 
|  | 296 | wic_help_topic_usage, | 
|  | 297 | hlp.get_wic_plugins_help], | 
|  | 298 | "overview":  [wic_help_topic_subcommand, | 
|  | 299 | wic_help_topic_usage, | 
|  | 300 | hlp.wic_overview_help], | 
|  | 301 | "kickstart": [wic_help_topic_subcommand, | 
|  | 302 | wic_help_topic_usage, | 
|  | 303 | hlp.wic_kickstart_help], | 
|  | 304 | } | 
|  | 305 |  | 
|  | 306 |  | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 307 | def main(argv): | 
|  | 308 | parser = optparse.OptionParser(version="wic version %s" % __version__, | 
|  | 309 | usage=hlp.wic_usage) | 
|  | 310 |  | 
|  | 311 | parser.disable_interspersed_args() | 
|  | 312 |  | 
|  | 313 | args = parser.parse_args(argv)[1] | 
|  | 314 |  | 
|  | 315 | if len(args): | 
|  | 316 | if args[0] == "help": | 
|  | 317 | if len(args) == 1: | 
|  | 318 | parser.print_help() | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 319 | raise WicError("help command requires parameter") | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 320 |  | 
|  | 321 | return hlp.invoke_subcommand(args, parser, hlp.wic_help_usage, subcommands) | 
|  | 322 |  | 
|  | 323 |  | 
|  | 324 | if __name__ == "__main__": | 
|  | 325 | try: | 
|  | 326 | sys.exit(main(sys.argv[1:])) | 
|  | 327 | except WicError as err: | 
| Brad Bishop | 6e60e8b | 2018-02-01 10:27:11 -0500 | [diff] [blame] | 328 | print() | 
|  | 329 | logger.error(err) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 330 | sys.exit(1) |