blob: 81239ac357aec4345ed3ffa96103dd8e2da9a01f [file] [log] [blame]
# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
#
# Copyright (c) 2013, Intel Corporation.
# All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# DESCRIPTION
# This module provides a place to collect various wic-related utils
# for the OpenEmbedded Image Tools.
#
# AUTHORS
# Tom Zanussi <tom.zanussi (at] linux.intel.com>
#
"""Miscellaneous functions."""
import os
from collections import defaultdict
from wic import msger
from wic.utils import runner
# executable -> recipe pairs for exec_native_cmd
NATIVE_RECIPES = {"mcopy": "mtools",
"mkdosfs": "dosfstools",
"mkfs.btrfs": "btrfs-tools",
"mkfs.ext2": "e2fsprogs",
"mkfs.ext3": "e2fsprogs",
"mkfs.ext4": "e2fsprogs",
"mkfs.vfat": "dosfstools",
"mksquashfs": "squashfs-tools",
"mkswap": "util-linux",
"parted": "parted",
"sgdisk": "gptfdisk",
"syslinux": "syslinux"
}
def _exec_cmd(cmd_and_args, as_shell=False, catch=3):
"""
Execute command, catching stderr, stdout
Need to execute as_shell if the command uses wildcards
"""
msger.debug("_exec_cmd: %s" % cmd_and_args)
args = cmd_and_args.split()
msger.debug(args)
if as_shell:
ret, out = runner.runtool(cmd_and_args, catch)
else:
ret, out = runner.runtool(args, catch)
out = out.strip()
msger.debug("_exec_cmd: output for %s (rc = %d): %s" % \
(cmd_and_args, ret, out))
return (ret, out)
def exec_cmd(cmd_and_args, as_shell=False, catch=3):
"""
Execute command, catching stderr, stdout
Exits if rc non-zero
"""
ret, out = _exec_cmd(cmd_and_args, as_shell, catch)
if ret != 0:
msger.error("exec_cmd: %s returned '%s' instead of 0" % \
(cmd_and_args, ret))
return out
def cmd_in_path(cmd, path):
import scriptpath
scriptpath.add_bitbake_lib_path()
return bb.utils.which(path, cmd) != "" or False
def exec_native_cmd(cmd_and_args, native_sysroot, catch=3, pseudo=""):
"""
Execute native command, catching stderr, stdout
Need to execute as_shell if the command uses wildcards
Always need to execute native commands as_shell
"""
# The reason -1 is used is because there may be "export" commands.
args = cmd_and_args.split(';')[-1].split()
msger.debug(args)
if pseudo:
cmd_and_args = pseudo + cmd_and_args
native_paths = \
"%s/sbin:%s/usr/sbin:%s/usr/bin" % \
(native_sysroot, native_sysroot, native_sysroot)
native_cmd_and_args = "export PATH=%s:$PATH;%s" % \
(native_paths, cmd_and_args)
msger.debug("exec_native_cmd: %s" % cmd_and_args)
# If the command isn't in the native sysroot say we failed.
if cmd_in_path(args[0], native_paths):
ret, out = _exec_cmd(native_cmd_and_args, True, catch)
else:
ret = 127
prog = args[0]
# shell command-not-found
if ret == 127 \
or (pseudo and ret == 1 and out == "Can't find '%s' in $PATH." % prog):
msg = "A native program %s required to build the image "\
"was not found (see details above).\n\n" % prog
recipe = NATIVE_RECIPES.get(prog)
if recipe:
msg += "Please bake it with 'bitbake %s-native' "\
"and try again.\n" % recipe
else:
msg += "Wic failed to find a recipe to build native %s. Please "\
"file a bug against wic.\n" % prog
msger.error(msg)
if out:
msger.debug('"%s" output: %s' % (args[0], out))
if ret != 0:
msger.error("exec_cmd: '%s' returned '%s' instead of 0" % \
(cmd_and_args, ret))
return ret, out
BOOTDD_EXTRA_SPACE = 16384
class BitbakeVars(defaultdict):
"""
Container for Bitbake variables.
"""
def __init__(self):
defaultdict.__init__(self, dict)
# default_image and vars_dir attributes should be set from outside
self.default_image = None
self.vars_dir = None
def _parse_line(self, line, image):
"""
Parse one line from bitbake -e output or from .env file.
Put result key-value pair into the storage.
"""
if "=" not in line:
return
try:
key, val = line.split("=")
except ValueError:
return
key = key.strip()
val = val.strip()
if key.replace('_', '').isalnum():
self[image][key] = val.strip('"')
def get_var(self, var, image=None):
"""
Get bitbake variable from 'bitbake -e' output or from .env file.
This is a lazy method, i.e. it runs bitbake or parses file only when
only when variable is requested. It also caches results.
"""
if not image:
image = self.default_image
if image not in self:
if image and self.vars_dir:
fname = os.path.join(self.vars_dir, image + '.env')
if os.path.isfile(fname):
# parse .env file
with open(fname) as varsfile:
for line in varsfile:
self._parse_line(line, image)
else:
print "Couldn't get bitbake variable from %s." % fname
print "File %s doesn't exist." % fname
return
else:
# Get bitbake -e output
cmd = "bitbake -e"
if image:
cmd += " %s" % image
log_level = msger.get_loglevel()
msger.set_loglevel('normal')
ret, lines = _exec_cmd(cmd)
msger.set_loglevel(log_level)
if ret:
print "Couldn't get '%s' output." % cmd
print "Bitbake failed with error:\n%s\n" % lines
return
# Parse bitbake -e output
for line in lines.split('\n'):
self._parse_line(line, image)
# Make first image a default set of variables
images = [key for key in self if key]
if len(images) == 1:
self[None] = self[image]
return self[image].get(var)
# Create BB_VARS singleton
BB_VARS = BitbakeVars()
def get_bitbake_var(var, image=None):
"""
Provide old get_bitbake_var API by wrapping
get_var method of BB_VARS singleton.
"""
return BB_VARS.get_var(var, image)
def parse_sourceparams(sourceparams):
"""
Split sourceparams string of the form key1=val1[,key2=val2,...]
into a dict. Also accepts valueless keys i.e. without =.
Returns dict of param key/val pairs (note that val may be None).
"""
params_dict = {}
params = sourceparams.split(',')
if params:
for par in params:
if not par:
continue
if not '=' in par:
key = par
val = None
else:
key, val = par.split('=')
params_dict[key] = val
return params_dict