blob: 81239ac357aec4345ed3ffa96103dd8e2da9a01f [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3#
4# Copyright (c) 2013, Intel Corporation.
5# All rights reserved.
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License version 2 as
9# published by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19#
20# DESCRIPTION
21# This module provides a place to collect various wic-related utils
22# for the OpenEmbedded Image Tools.
23#
24# AUTHORS
25# Tom Zanussi <tom.zanussi (at] linux.intel.com>
26#
27"""Miscellaneous functions."""
28
29import os
30from collections import defaultdict
31
32from wic import msger
33from wic.utils import runner
34
35# executable -> recipe pairs for exec_native_cmd
36NATIVE_RECIPES = {"mcopy": "mtools",
37 "mkdosfs": "dosfstools",
38 "mkfs.btrfs": "btrfs-tools",
39 "mkfs.ext2": "e2fsprogs",
40 "mkfs.ext3": "e2fsprogs",
41 "mkfs.ext4": "e2fsprogs",
42 "mkfs.vfat": "dosfstools",
43 "mksquashfs": "squashfs-tools",
44 "mkswap": "util-linux",
45 "parted": "parted",
46 "sgdisk": "gptfdisk",
47 "syslinux": "syslinux"
48 }
49
50def _exec_cmd(cmd_and_args, as_shell=False, catch=3):
51 """
52 Execute command, catching stderr, stdout
53
54 Need to execute as_shell if the command uses wildcards
55 """
56 msger.debug("_exec_cmd: %s" % cmd_and_args)
57 args = cmd_and_args.split()
58 msger.debug(args)
59
60 if as_shell:
61 ret, out = runner.runtool(cmd_and_args, catch)
62 else:
63 ret, out = runner.runtool(args, catch)
64 out = out.strip()
65 msger.debug("_exec_cmd: output for %s (rc = %d): %s" % \
66 (cmd_and_args, ret, out))
67
68 return (ret, out)
69
70
71def exec_cmd(cmd_and_args, as_shell=False, catch=3):
72 """
73 Execute command, catching stderr, stdout
74
75 Exits if rc non-zero
76 """
77 ret, out = _exec_cmd(cmd_and_args, as_shell, catch)
78
79 if ret != 0:
80 msger.error("exec_cmd: %s returned '%s' instead of 0" % \
81 (cmd_and_args, ret))
82
83 return out
84
Patrick Williamsf1e5d692016-03-30 15:21:19 -050085def cmd_in_path(cmd, path):
86 import scriptpath
87
88 scriptpath.add_bitbake_lib_path()
89
90 return bb.utils.which(path, cmd) != "" or False
Patrick Williamsc124f4f2015-09-15 14:41:29 -050091
Patrick Williamsd8c66bc2016-06-20 12:57:21 -050092def exec_native_cmd(cmd_and_args, native_sysroot, catch=3, pseudo=""):
Patrick Williamsc124f4f2015-09-15 14:41:29 -050093 """
94 Execute native command, catching stderr, stdout
95
96 Need to execute as_shell if the command uses wildcards
97
98 Always need to execute native commands as_shell
99 """
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500100 # The reason -1 is used is because there may be "export" commands.
101 args = cmd_and_args.split(';')[-1].split()
102 msger.debug(args)
103
104 if pseudo:
105 cmd_and_args = pseudo + cmd_and_args
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500106 native_paths = \
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500107 "%s/sbin:%s/usr/sbin:%s/usr/bin" % \
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500108 (native_sysroot, native_sysroot, native_sysroot)
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500109 native_cmd_and_args = "export PATH=%s:$PATH;%s" % \
110 (native_paths, cmd_and_args)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500111 msger.debug("exec_native_cmd: %s" % cmd_and_args)
112
Patrick Williamsf1e5d692016-03-30 15:21:19 -0500113 # If the command isn't in the native sysroot say we failed.
114 if cmd_in_path(args[0], native_paths):
115 ret, out = _exec_cmd(native_cmd_and_args, True, catch)
116 else:
117 ret = 127
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500118
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500119 prog = args[0]
120 # shell command-not-found
121 if ret == 127 \
122 or (pseudo and ret == 1 and out == "Can't find '%s' in $PATH." % prog):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500123 msg = "A native program %s required to build the image "\
124 "was not found (see details above).\n\n" % prog
125 recipe = NATIVE_RECIPES.get(prog)
126 if recipe:
127 msg += "Please bake it with 'bitbake %s-native' "\
128 "and try again.\n" % recipe
129 else:
130 msg += "Wic failed to find a recipe to build native %s. Please "\
131 "file a bug against wic.\n" % prog
132 msger.error(msg)
133 if out:
134 msger.debug('"%s" output: %s' % (args[0], out))
135
136 if ret != 0:
137 msger.error("exec_cmd: '%s' returned '%s' instead of 0" % \
138 (cmd_and_args, ret))
139
140 return ret, out
141
142BOOTDD_EXTRA_SPACE = 16384
143
144class BitbakeVars(defaultdict):
145 """
146 Container for Bitbake variables.
147 """
148 def __init__(self):
149 defaultdict.__init__(self, dict)
150
151 # default_image and vars_dir attributes should be set from outside
152 self.default_image = None
153 self.vars_dir = None
154
155 def _parse_line(self, line, image):
156 """
157 Parse one line from bitbake -e output or from .env file.
158 Put result key-value pair into the storage.
159 """
160 if "=" not in line:
161 return
162 try:
163 key, val = line.split("=")
164 except ValueError:
165 return
166 key = key.strip()
167 val = val.strip()
168 if key.replace('_', '').isalnum():
169 self[image][key] = val.strip('"')
170
171 def get_var(self, var, image=None):
172 """
173 Get bitbake variable from 'bitbake -e' output or from .env file.
174 This is a lazy method, i.e. it runs bitbake or parses file only when
175 only when variable is requested. It also caches results.
176 """
177 if not image:
178 image = self.default_image
179
180 if image not in self:
181 if image and self.vars_dir:
182 fname = os.path.join(self.vars_dir, image + '.env')
183 if os.path.isfile(fname):
184 # parse .env file
185 with open(fname) as varsfile:
186 for line in varsfile:
187 self._parse_line(line, image)
188 else:
189 print "Couldn't get bitbake variable from %s." % fname
190 print "File %s doesn't exist." % fname
191 return
192 else:
193 # Get bitbake -e output
194 cmd = "bitbake -e"
195 if image:
196 cmd += " %s" % image
197
198 log_level = msger.get_loglevel()
199 msger.set_loglevel('normal')
200 ret, lines = _exec_cmd(cmd)
201 msger.set_loglevel(log_level)
202
203 if ret:
204 print "Couldn't get '%s' output." % cmd
205 print "Bitbake failed with error:\n%s\n" % lines
206 return
207
208 # Parse bitbake -e output
209 for line in lines.split('\n'):
210 self._parse_line(line, image)
211
212 # Make first image a default set of variables
213 images = [key for key in self if key]
214 if len(images) == 1:
215 self[None] = self[image]
216
217 return self[image].get(var)
218
219# Create BB_VARS singleton
220BB_VARS = BitbakeVars()
221
222def get_bitbake_var(var, image=None):
223 """
224 Provide old get_bitbake_var API by wrapping
225 get_var method of BB_VARS singleton.
226 """
227 return BB_VARS.get_var(var, image)
228
229def parse_sourceparams(sourceparams):
230 """
231 Split sourceparams string of the form key1=val1[,key2=val2,...]
232 into a dict. Also accepts valueless keys i.e. without =.
233
234 Returns dict of param key/val pairs (note that val may be None).
235 """
236 params_dict = {}
237
238 params = sourceparams.split(',')
239 if params:
240 for par in params:
241 if not par:
242 continue
243 if not '=' in par:
244 key = par
245 val = None
246 else:
247 key, val = par.split('=')
248 params_dict[key] = val
249
250 return params_dict