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