| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 1 | # Utility functions for reading and modifying recipes | 
 | 2 | # | 
 | 3 | # Some code borrowed from the OE layer index | 
 | 4 | # | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 5 | # Copyright (C) 2013-2016 Intel Corporation | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 6 | # | 
 | 7 |  | 
 | 8 | import sys | 
 | 9 | import os | 
 | 10 | import os.path | 
 | 11 | import tempfile | 
 | 12 | import textwrap | 
 | 13 | import difflib | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 14 | from . import utils | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 15 | import shutil | 
 | 16 | import re | 
 | 17 | import fnmatch | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 18 | import glob | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 19 | from collections import OrderedDict, defaultdict | 
 | 20 |  | 
 | 21 |  | 
 | 22 | # Help us to find places to insert values | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 23 | recipe_progression = ['SUMMARY', 'DESCRIPTION', 'HOMEPAGE', 'BUGTRACKER', 'SECTION', 'LICENSE', 'LICENSE_FLAGS', 'LIC_FILES_CHKSUM', 'PROVIDES', 'DEPENDS', 'PR', 'PV', 'SRCREV', 'SRCPV', 'SRC_URI', 'S', 'do_fetch()', 'do_unpack()', 'do_patch()', 'EXTRA_OECONF', 'EXTRA_OECMAKE', 'EXTRA_OESCONS', 'do_configure()', 'EXTRA_OEMAKE', 'do_compile()', 'do_install()', 'do_populate_sysroot()', 'INITSCRIPT', 'USERADD', 'GROUPADD', 'PACKAGES', 'FILES', 'RDEPENDS', 'RRECOMMENDS', 'RSUGGESTS', 'RPROVIDES', 'RREPLACES', 'RCONFLICTS', 'ALLOW_EMPTY', 'populate_packages()', 'do_package()', 'do_deploy()'] | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 24 | # Variables that sometimes are a bit long but shouldn't be wrapped | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 25 | nowrap_vars = ['SUMMARY', 'HOMEPAGE', 'BUGTRACKER', 'SRC_URI[md5sum]', 'SRC_URI[sha256sum]'] | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 26 | list_vars = ['SRC_URI', 'LIC_FILES_CHKSUM'] | 
 | 27 | meta_vars = ['SUMMARY', 'DESCRIPTION', 'HOMEPAGE', 'BUGTRACKER', 'SECTION'] | 
 | 28 |  | 
 | 29 |  | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 30 | def pn_to_recipe(cooker, pn, mc=''): | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 31 |     """Convert a recipe name (PN) to the path to the recipe file""" | 
 | 32 |     import bb.providers | 
 | 33 |  | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 34 |     if pn in cooker.recipecaches[mc].pkg_pn: | 
 | 35 |         best = bb.providers.findBestProvider(pn, cooker.data, cooker.recipecaches[mc], cooker.recipecaches[mc].pkg_pn) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 36 |         return best[3] | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 37 |     elif pn in cooker.recipecaches[mc].providers: | 
 | 38 |         filenames = cooker.recipecaches[mc].providers[pn] | 
 | 39 |         eligible, foundUnique = bb.providers.filterProviders(filenames, pn, cooker.expanded_data, cooker.recipecaches[mc]) | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 40 |         filename = eligible[0] | 
 | 41 |         return filename | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 42 |     else: | 
 | 43 |         return None | 
 | 44 |  | 
 | 45 |  | 
 | 46 | def get_unavailable_reasons(cooker, pn): | 
 | 47 |     """If a recipe could not be found, find out why if possible""" | 
 | 48 |     import bb.taskdata | 
 | 49 |     taskdata = bb.taskdata.TaskData(None, skiplist=cooker.skiplist) | 
 | 50 |     return taskdata.get_reasons(pn) | 
 | 51 |  | 
 | 52 |  | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 53 | def parse_recipe(cooker, fn, appendfiles): | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 54 |     """ | 
 | 55 |     Parse an individual recipe file, optionally with a list of | 
 | 56 |     bbappend files. | 
 | 57 |     """ | 
 | 58 |     import bb.cache | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 59 |     parser = bb.cache.NoCache(cooker.databuilder) | 
 | 60 |     envdata = parser.loadDataFull(fn, appendfiles) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 61 |     return envdata | 
 | 62 |  | 
 | 63 |  | 
 | 64 | def parse_recipe_simple(cooker, pn, d, appends=True): | 
 | 65 |     """ | 
 | 66 |     Parse a recipe and optionally all bbappends that apply to it | 
 | 67 |     in the current configuration. | 
 | 68 |     """ | 
 | 69 |     import bb.providers | 
 | 70 |  | 
 | 71 |     recipefile = pn_to_recipe(cooker, pn) | 
 | 72 |     if not recipefile: | 
 | 73 |         skipreasons = get_unavailable_reasons(cooker, pn) | 
 | 74 |         # We may as well re-use bb.providers.NoProvider here | 
 | 75 |         if skipreasons: | 
 | 76 |             raise bb.providers.NoProvider(skipreasons) | 
 | 77 |         else: | 
 | 78 |             raise bb.providers.NoProvider('Unable to find any recipe file matching %s' % pn) | 
 | 79 |     if appends: | 
 | 80 |         appendfiles = cooker.collection.get_file_appends(recipefile) | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 81 |     else: | 
 | 82 |         appendfiles = None | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 83 |     return parse_recipe(cooker, recipefile, appendfiles) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 84 |  | 
 | 85 |  | 
 | 86 | def get_var_files(fn, varlist, d): | 
 | 87 |     """Find the file in which each of a list of variables is set. | 
 | 88 |     Note: requires variable history to be enabled when parsing. | 
 | 89 |     """ | 
 | 90 |     varfiles = {} | 
 | 91 |     for v in varlist: | 
 | 92 |         history = d.varhistory.variable(v) | 
 | 93 |         files = [] | 
 | 94 |         for event in history: | 
 | 95 |             if 'file' in event and not 'flag' in event: | 
 | 96 |                 files.append(event['file']) | 
 | 97 |         if files: | 
 | 98 |             actualfile = files[-1] | 
 | 99 |         else: | 
 | 100 |             actualfile = None | 
 | 101 |         varfiles[v] = actualfile | 
 | 102 |  | 
 | 103 |     return varfiles | 
 | 104 |  | 
 | 105 |  | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 106 | def split_var_value(value, assignment=True): | 
 | 107 |     """ | 
 | 108 |     Split a space-separated variable's value into a list of items, | 
 | 109 |     taking into account that some of the items might be made up of | 
 | 110 |     expressions containing spaces that should not be split. | 
 | 111 |     Parameters: | 
 | 112 |         value: | 
 | 113 |             The string value to split | 
 | 114 |         assignment: | 
 | 115 |             True to assume that the value represents an assignment | 
 | 116 |             statement, False otherwise. If True, and an assignment | 
 | 117 |             statement is passed in the first item in | 
 | 118 |             the returned list will be the part of the assignment | 
 | 119 |             statement up to and including the opening quote character, | 
 | 120 |             and the last item will be the closing quote. | 
 | 121 |     """ | 
 | 122 |     inexpr = 0 | 
 | 123 |     lastchar = None | 
 | 124 |     out = [] | 
 | 125 |     buf = '' | 
 | 126 |     for char in value: | 
 | 127 |         if char == '{': | 
 | 128 |             if lastchar == '$': | 
 | 129 |                 inexpr += 1 | 
 | 130 |         elif char == '}': | 
 | 131 |             inexpr -= 1 | 
 | 132 |         elif assignment and char in '"\'' and inexpr == 0: | 
 | 133 |             if buf: | 
 | 134 |                 out.append(buf) | 
 | 135 |             out.append(char) | 
 | 136 |             char = '' | 
 | 137 |             buf = '' | 
 | 138 |         elif char.isspace() and inexpr == 0: | 
 | 139 |             char = '' | 
 | 140 |             if buf: | 
 | 141 |                 out.append(buf) | 
 | 142 |             buf = '' | 
 | 143 |         buf += char | 
 | 144 |         lastchar = char | 
 | 145 |     if buf: | 
 | 146 |         out.append(buf) | 
 | 147 |  | 
 | 148 |     # Join together assignment statement and opening quote | 
 | 149 |     outlist = out | 
 | 150 |     if assignment: | 
 | 151 |         assigfound = False | 
 | 152 |         for idx, item in enumerate(out): | 
 | 153 |             if '=' in item: | 
 | 154 |                 assigfound = True | 
 | 155 |             if assigfound: | 
 | 156 |                 if '"' in item or "'" in item: | 
 | 157 |                     outlist = [' '.join(out[:idx+1])] | 
 | 158 |                     outlist.extend(out[idx+1:]) | 
 | 159 |                     break | 
 | 160 |     return outlist | 
 | 161 |  | 
 | 162 |  | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 163 | def patch_recipe_lines(fromlines, values, trailing_newline=True): | 
 | 164 |     """Update or insert variable values into lines from a recipe. | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 165 |        Note that some manual inspection/intervention may be required | 
 | 166 |        since this cannot handle all situations. | 
 | 167 |     """ | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 168 |  | 
 | 169 |     import bb.utils | 
 | 170 |  | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 171 |     if trailing_newline: | 
 | 172 |         newline = '\n' | 
 | 173 |     else: | 
 | 174 |         newline = '' | 
 | 175 |  | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 176 |     recipe_progression_res = [] | 
 | 177 |     recipe_progression_restrs = [] | 
 | 178 |     for item in recipe_progression: | 
 | 179 |         if item.endswith('()'): | 
 | 180 |             key = item[:-2] | 
 | 181 |         else: | 
 | 182 |             key = item | 
 | 183 |         restr = '%s(_[a-zA-Z0-9-_$(){}]+|\[[^\]]*\])?' % key | 
 | 184 |         if item.endswith('()'): | 
 | 185 |             recipe_progression_restrs.append(restr + '()') | 
 | 186 |         else: | 
 | 187 |             recipe_progression_restrs.append(restr) | 
 | 188 |         recipe_progression_res.append(re.compile('^%s$' % restr)) | 
 | 189 |  | 
 | 190 |     def get_recipe_pos(variable): | 
 | 191 |         for i, p in enumerate(recipe_progression_res): | 
 | 192 |             if p.match(variable): | 
 | 193 |                 return i | 
 | 194 |         return -1 | 
 | 195 |  | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 196 |     remainingnames = {} | 
 | 197 |     for k in values.keys(): | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 198 |         remainingnames[k] = get_recipe_pos(k) | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 199 |     remainingnames = OrderedDict(sorted(remainingnames.items(), key=lambda x: x[1])) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 200 |  | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 201 |     modifying = False | 
 | 202 |  | 
 | 203 |     def outputvalue(name, lines, rewindcomments=False): | 
 | 204 |         if values[name] is None: | 
 | 205 |             return | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 206 |         rawtext = '%s = "%s"%s' % (name, values[name], newline) | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 207 |         addlines = [] | 
 | 208 |         if name in nowrap_vars: | 
 | 209 |             addlines.append(rawtext) | 
 | 210 |         elif name in list_vars: | 
 | 211 |             splitvalue = split_var_value(values[name], assignment=False) | 
 | 212 |             if len(splitvalue) > 1: | 
 | 213 |                 linesplit = ' \\\n' + (' ' * (len(name) + 4)) | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 214 |                 addlines.append('%s = "%s%s"%s' % (name, linesplit.join(splitvalue), linesplit, newline)) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 215 |             else: | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 216 |                 addlines.append(rawtext) | 
 | 217 |         else: | 
 | 218 |             wrapped = textwrap.wrap(rawtext) | 
 | 219 |             for wrapline in wrapped[:-1]: | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 220 |                 addlines.append('%s \\%s' % (wrapline, newline)) | 
 | 221 |             addlines.append('%s%s' % (wrapped[-1], newline)) | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 222 |         if rewindcomments: | 
 | 223 |             # Ensure we insert the lines before any leading comments | 
 | 224 |             # (that we'd want to ensure remain leading the next value) | 
 | 225 |             for i, ln in reversed(list(enumerate(lines))): | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 226 |                 if not ln.startswith('#'): | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 227 |                     lines[i+1:i+1] = addlines | 
 | 228 |                     break | 
 | 229 |             else: | 
 | 230 |                 lines.extend(addlines) | 
 | 231 |         else: | 
 | 232 |             lines.extend(addlines) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 233 |  | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 234 |     existingnames = [] | 
 | 235 |     def patch_recipe_varfunc(varname, origvalue, op, newlines): | 
 | 236 |         if modifying: | 
 | 237 |             # Insert anything that should come before this variable | 
 | 238 |             pos = get_recipe_pos(varname) | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 239 |             for k in list(remainingnames): | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 240 |                 if remainingnames[k] > -1 and pos >= remainingnames[k] and not k in existingnames: | 
 | 241 |                     outputvalue(k, newlines, rewindcomments=True) | 
 | 242 |                     del remainingnames[k] | 
 | 243 |             # Now change this variable, if it needs to be changed | 
 | 244 |             if varname in existingnames and op in ['+=', '=', '=+']: | 
 | 245 |                 if varname in remainingnames: | 
 | 246 |                     outputvalue(varname, newlines) | 
 | 247 |                     del remainingnames[varname] | 
 | 248 |                 return None, None, 0, True | 
 | 249 |         else: | 
 | 250 |             if varname in values: | 
 | 251 |                 existingnames.append(varname) | 
 | 252 |         return origvalue, None, 0, True | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 253 |  | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 254 |     # First run - establish which values we want to set are already in the file | 
 | 255 |     varlist = [re.escape(item) for item in values.keys()] | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 256 |     bb.utils.edit_metadata(fromlines, varlist, patch_recipe_varfunc) | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 257 |     # Second run - actually set everything | 
 | 258 |     modifying = True | 
 | 259 |     varlist.extend(recipe_progression_restrs) | 
 | 260 |     changed, tolines = bb.utils.edit_metadata(fromlines, varlist, patch_recipe_varfunc, match_overrides=True) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 261 |  | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 262 |     if remainingnames: | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 263 |         if tolines and tolines[-1].strip() != '': | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 264 |             tolines.append('\n') | 
 | 265 |         for k in remainingnames.keys(): | 
 | 266 |             outputvalue(k, tolines) | 
 | 267 |  | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 268 |     return changed, tolines | 
 | 269 |  | 
 | 270 |  | 
 | 271 | def patch_recipe_file(fn, values, patch=False, relpath=''): | 
 | 272 |     """Update or insert variable values into a recipe file (assuming you | 
 | 273 |        have already identified the exact file you want to update.) | 
 | 274 |        Note that some manual inspection/intervention may be required | 
 | 275 |        since this cannot handle all situations. | 
 | 276 |     """ | 
 | 277 |  | 
 | 278 |     with open(fn, 'r') as f: | 
 | 279 |         fromlines = f.readlines() | 
 | 280 |  | 
 | 281 |     _, tolines = patch_recipe_lines(fromlines, values) | 
 | 282 |  | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 283 |     if patch: | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 284 |         relfn = os.path.relpath(fn, relpath) | 
 | 285 |         diff = difflib.unified_diff(fromlines, tolines, 'a/%s' % relfn, 'b/%s' % relfn) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 286 |         return diff | 
 | 287 |     else: | 
 | 288 |         with open(fn, 'w') as f: | 
 | 289 |             f.writelines(tolines) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 290 |         return None | 
 | 291 |  | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 292 |  | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 293 | def localise_file_vars(fn, varfiles, varlist): | 
 | 294 |     """Given a list of variables and variable history (fetched with get_var_files()) | 
 | 295 |     find where each variable should be set/changed. This handles for example where a | 
 | 296 |     recipe includes an inc file where variables might be changed - in most cases | 
 | 297 |     we want to update the inc file when changing the variable value rather than adding | 
 | 298 |     it to the recipe itself. | 
 | 299 |     """ | 
 | 300 |     fndir = os.path.dirname(fn) + os.sep | 
 | 301 |  | 
 | 302 |     first_meta_file = None | 
 | 303 |     for v in meta_vars: | 
 | 304 |         f = varfiles.get(v, None) | 
 | 305 |         if f: | 
 | 306 |             actualdir = os.path.dirname(f) + os.sep | 
 | 307 |             if actualdir.startswith(fndir): | 
 | 308 |                 first_meta_file = f | 
 | 309 |                 break | 
 | 310 |  | 
 | 311 |     filevars = defaultdict(list) | 
 | 312 |     for v in varlist: | 
 | 313 |         f = varfiles[v] | 
 | 314 |         # Only return files that are in the same directory as the recipe or in some directory below there | 
 | 315 |         # (this excludes bbclass files and common inc files that wouldn't be appropriate to set the variable | 
 | 316 |         # in if we were going to set a value specific to this recipe) | 
 | 317 |         if f: | 
 | 318 |             actualfile = f | 
 | 319 |         else: | 
 | 320 |             # Variable isn't in a file, if it's one of the "meta" vars, use the first file with a meta var in it | 
 | 321 |             if first_meta_file: | 
 | 322 |                 actualfile = first_meta_file | 
 | 323 |             else: | 
 | 324 |                 actualfile = fn | 
 | 325 |  | 
 | 326 |         actualdir = os.path.dirname(actualfile) + os.sep | 
 | 327 |         if not actualdir.startswith(fndir): | 
 | 328 |             actualfile = fn | 
 | 329 |         filevars[actualfile].append(v) | 
 | 330 |  | 
 | 331 |     return filevars | 
 | 332 |  | 
 | 333 | def patch_recipe(d, fn, varvalues, patch=False, relpath=''): | 
 | 334 |     """Modify a list of variable values in the specified recipe. Handles inc files if | 
 | 335 |     used by the recipe. | 
 | 336 |     """ | 
 | 337 |     varlist = varvalues.keys() | 
 | 338 |     varfiles = get_var_files(fn, varlist, d) | 
 | 339 |     locs = localise_file_vars(fn, varfiles, varlist) | 
 | 340 |     patches = [] | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 341 |     for f,v in locs.items(): | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 342 |         vals = {k: varvalues[k] for k in v} | 
 | 343 |         patchdata = patch_recipe_file(f, vals, patch, relpath) | 
 | 344 |         if patch: | 
 | 345 |             patches.append(patchdata) | 
 | 346 |  | 
 | 347 |     if patch: | 
 | 348 |         return patches | 
 | 349 |     else: | 
 | 350 |         return None | 
 | 351 |  | 
 | 352 |  | 
 | 353 |  | 
 | 354 | def copy_recipe_files(d, tgt_dir, whole_dir=False, download=True): | 
 | 355 |     """Copy (local) recipe files, including both files included via include/require, | 
 | 356 |     and files referred to in the SRC_URI variable.""" | 
 | 357 |     import bb.fetch2 | 
 | 358 |     import oe.path | 
 | 359 |  | 
 | 360 |     # FIXME need a warning if the unexpanded SRC_URI value contains variable references | 
 | 361 |  | 
 | 362 |     uris = (d.getVar('SRC_URI', True) or "").split() | 
 | 363 |     fetch = bb.fetch2.Fetch(uris, d) | 
 | 364 |     if download: | 
 | 365 |         fetch.download() | 
 | 366 |  | 
 | 367 |     # Copy local files to target directory and gather any remote files | 
 | 368 |     bb_dir = os.path.dirname(d.getVar('FILE', True)) + os.sep | 
 | 369 |     remotes = [] | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 370 |     copied = [] | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 371 |     includes = [path for path in d.getVar('BBINCLUDED', True).split() if | 
 | 372 |                 path.startswith(bb_dir) and os.path.exists(path)] | 
 | 373 |     for path in fetch.localpaths() + includes: | 
 | 374 |         # Only import files that are under the meta directory | 
 | 375 |         if path.startswith(bb_dir): | 
 | 376 |             if not whole_dir: | 
 | 377 |                 relpath = os.path.relpath(path, bb_dir) | 
 | 378 |                 subdir = os.path.join(tgt_dir, os.path.dirname(relpath)) | 
 | 379 |                 if not os.path.exists(subdir): | 
 | 380 |                     os.makedirs(subdir) | 
 | 381 |                 shutil.copy2(path, os.path.join(tgt_dir, relpath)) | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 382 |                 copied.append(relpath) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 383 |         else: | 
 | 384 |             remotes.append(path) | 
 | 385 |     # Simply copy whole meta dir, if requested | 
 | 386 |     if whole_dir: | 
 | 387 |         shutil.copytree(bb_dir, tgt_dir) | 
 | 388 |  | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 389 |     return copied, remotes | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 390 |  | 
 | 391 |  | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 392 | def get_recipe_local_files(d, patches=False): | 
 | 393 |     """Get a list of local files in SRC_URI within a recipe.""" | 
 | 394 |     uris = (d.getVar('SRC_URI', True) or "").split() | 
 | 395 |     fetch = bb.fetch2.Fetch(uris, d) | 
 | 396 |     ret = {} | 
 | 397 |     for uri in uris: | 
 | 398 |         if fetch.ud[uri].type == 'file': | 
 | 399 |             if (not patches and | 
 | 400 |                     bb.utils.exec_flat_python_func('patch_path', uri, fetch, '')): | 
 | 401 |                 continue | 
 | 402 |             # Skip files that are referenced by absolute path | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 403 |             fname = fetch.ud[uri].basepath | 
 | 404 |             if os.path.isabs(fname): | 
 | 405 |                 continue | 
 | 406 |             # Handle subdir= | 
 | 407 |             subdir = fetch.ud[uri].parm.get('subdir', '') | 
 | 408 |             if subdir: | 
 | 409 |                 if os.path.isabs(subdir): | 
 | 410 |                     continue | 
 | 411 |                 fname = os.path.join(subdir, fname) | 
 | 412 |             ret[fname] = fetch.localpath(uri) | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 413 |     return ret | 
 | 414 |  | 
 | 415 |  | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 416 | def get_recipe_patches(d): | 
 | 417 |     """Get a list of the patches included in SRC_URI within a recipe.""" | 
 | 418 |     patchfiles = [] | 
 | 419 |     # Execute src_patches() defined in patch.bbclass - this works since that class | 
 | 420 |     # is inherited globally | 
 | 421 |     patches = bb.utils.exec_flat_python_func('src_patches', d) | 
 | 422 |     for patch in patches: | 
 | 423 |         _, _, local, _, _, parm = bb.fetch.decodeurl(patch) | 
 | 424 |         patchfiles.append(local) | 
 | 425 |     return patchfiles | 
 | 426 |  | 
 | 427 |  | 
 | 428 | def get_recipe_patched_files(d): | 
 | 429 |     """ | 
 | 430 |     Get the list of patches for a recipe along with the files each patch modifies. | 
 | 431 |     Params: | 
 | 432 |         d: the datastore for the recipe | 
 | 433 |     Returns: | 
 | 434 |         a dict mapping patch file path to a list of tuples of changed files and | 
 | 435 |         change mode ('A' for add, 'D' for delete or 'M' for modify) | 
 | 436 |     """ | 
 | 437 |     import oe.patch | 
 | 438 |     # Execute src_patches() defined in patch.bbclass - this works since that class | 
 | 439 |     # is inherited globally | 
 | 440 |     patches = bb.utils.exec_flat_python_func('src_patches', d) | 
 | 441 |     patchedfiles = {} | 
 | 442 |     for patch in patches: | 
 | 443 |         _, _, patchfile, _, _, parm = bb.fetch.decodeurl(patch) | 
 | 444 |         striplevel = int(parm['striplevel']) | 
 | 445 |         patchedfiles[patchfile] = oe.patch.PatchSet.getPatchedFiles(patchfile, striplevel, os.path.join(d.getVar('S', True), parm.get('patchdir', ''))) | 
 | 446 |     return patchedfiles | 
 | 447 |  | 
 | 448 |  | 
 | 449 | def validate_pn(pn): | 
 | 450 |     """Perform validation on a recipe name (PN) for a new recipe.""" | 
 | 451 |     reserved_names = ['forcevariable', 'append', 'prepend', 'remove'] | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 452 |     if not re.match('^[0-9a-z-.+]+$', pn): | 
 | 453 |         return 'Recipe name "%s" is invalid: only characters 0-9, a-z, -, + and . are allowed' % pn | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 454 |     elif pn in reserved_names: | 
 | 455 |         return 'Recipe name "%s" is invalid: is a reserved keyword' % pn | 
 | 456 |     elif pn.startswith('pn-'): | 
 | 457 |         return 'Recipe name "%s" is invalid: names starting with "pn-" are reserved' % pn | 
| Patrick Williams | d8c66bc | 2016-06-20 12:57:21 -0500 | [diff] [blame] | 458 |     elif pn.endswith(('.bb', '.bbappend', '.bbclass', '.inc', '.conf')): | 
 | 459 |         return 'Recipe name "%s" is invalid: should be just a name, not a file name' % pn | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 460 |     return '' | 
 | 461 |  | 
 | 462 |  | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 463 | def get_bbfile_path(d, destdir, extrapathhint=None): | 
 | 464 |     """ | 
 | 465 |     Determine the correct path for a recipe within a layer | 
 | 466 |     Parameters: | 
 | 467 |         d: Recipe-specific datastore | 
 | 468 |         destdir: destination directory. Can be the path to the base of the layer or a | 
 | 469 |             partial path somewhere within the layer. | 
 | 470 |         extrapathhint: a path relative to the base of the layer to try | 
 | 471 |     """ | 
 | 472 |     import bb.cookerdata | 
 | 473 |  | 
 | 474 |     destdir = os.path.abspath(destdir) | 
 | 475 |     destlayerdir = find_layerdir(destdir) | 
 | 476 |  | 
 | 477 |     # Parse the specified layer's layer.conf file directly, in case the layer isn't in bblayers.conf | 
 | 478 |     confdata = d.createCopy() | 
 | 479 |     confdata.setVar('BBFILES', '') | 
 | 480 |     confdata.setVar('LAYERDIR', destlayerdir) | 
 | 481 |     destlayerconf = os.path.join(destlayerdir, "conf", "layer.conf") | 
 | 482 |     confdata = bb.cookerdata.parse_config_file(destlayerconf, confdata) | 
 | 483 |     pn = d.getVar('PN', True) | 
 | 484 |  | 
 | 485 |     bbfilespecs = (confdata.getVar('BBFILES', True) or '').split() | 
 | 486 |     if destdir == destlayerdir: | 
 | 487 |         for bbfilespec in bbfilespecs: | 
 | 488 |             if not bbfilespec.endswith('.bbappend'): | 
 | 489 |                 for match in glob.glob(bbfilespec): | 
 | 490 |                     splitext = os.path.splitext(os.path.basename(match)) | 
 | 491 |                     if splitext[1] == '.bb': | 
 | 492 |                         mpn = splitext[0].split('_')[0] | 
 | 493 |                         if mpn == pn: | 
 | 494 |                             return os.path.dirname(match) | 
 | 495 |  | 
 | 496 |     # Try to make up a path that matches BBFILES | 
 | 497 |     # this is a little crude, but better than nothing | 
 | 498 |     bpn = d.getVar('BPN', True) | 
 | 499 |     recipefn = os.path.basename(d.getVar('FILE', True)) | 
 | 500 |     pathoptions = [destdir] | 
 | 501 |     if extrapathhint: | 
 | 502 |         pathoptions.append(os.path.join(destdir, extrapathhint)) | 
 | 503 |     if destdir == destlayerdir: | 
 | 504 |         pathoptions.append(os.path.join(destdir, 'recipes-%s' % bpn, bpn)) | 
 | 505 |         pathoptions.append(os.path.join(destdir, 'recipes', bpn)) | 
 | 506 |         pathoptions.append(os.path.join(destdir, bpn)) | 
 | 507 |     elif not destdir.endswith(('/' + pn, '/' + bpn)): | 
 | 508 |         pathoptions.append(os.path.join(destdir, bpn)) | 
 | 509 |     closepath = '' | 
 | 510 |     for pathoption in pathoptions: | 
 | 511 |         bbfilepath = os.path.join(pathoption, 'test.bb') | 
 | 512 |         for bbfilespec in bbfilespecs: | 
 | 513 |             if fnmatch.fnmatchcase(bbfilepath, bbfilespec): | 
 | 514 |                 return pathoption | 
 | 515 |     return None | 
 | 516 |  | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 517 | def get_bbappend_path(d, destlayerdir, wildcardver=False): | 
 | 518 |     """Determine how a bbappend for a recipe should be named and located within another layer""" | 
 | 519 |  | 
 | 520 |     import bb.cookerdata | 
 | 521 |  | 
 | 522 |     destlayerdir = os.path.abspath(destlayerdir) | 
 | 523 |     recipefile = d.getVar('FILE', True) | 
 | 524 |     recipefn = os.path.splitext(os.path.basename(recipefile))[0] | 
 | 525 |     if wildcardver and '_' in recipefn: | 
 | 526 |         recipefn = recipefn.split('_', 1)[0] + '_%' | 
 | 527 |     appendfn = recipefn + '.bbappend' | 
 | 528 |  | 
 | 529 |     # Parse the specified layer's layer.conf file directly, in case the layer isn't in bblayers.conf | 
 | 530 |     confdata = d.createCopy() | 
 | 531 |     confdata.setVar('BBFILES', '') | 
 | 532 |     confdata.setVar('LAYERDIR', destlayerdir) | 
 | 533 |     destlayerconf = os.path.join(destlayerdir, "conf", "layer.conf") | 
 | 534 |     confdata = bb.cookerdata.parse_config_file(destlayerconf, confdata) | 
 | 535 |  | 
 | 536 |     origlayerdir = find_layerdir(recipefile) | 
 | 537 |     if not origlayerdir: | 
 | 538 |         return (None, False) | 
 | 539 |     # Now join this to the path where the bbappend is going and check if it is covered by BBFILES | 
 | 540 |     appendpath = os.path.join(destlayerdir, os.path.relpath(os.path.dirname(recipefile), origlayerdir), appendfn) | 
 | 541 |     closepath = '' | 
 | 542 |     pathok = True | 
 | 543 |     for bbfilespec in confdata.getVar('BBFILES', True).split(): | 
 | 544 |         if fnmatch.fnmatchcase(appendpath, bbfilespec): | 
 | 545 |             # Our append path works, we're done | 
 | 546 |             break | 
 | 547 |         elif bbfilespec.startswith(destlayerdir) and fnmatch.fnmatchcase('test.bbappend', os.path.basename(bbfilespec)): | 
 | 548 |             # Try to find the longest matching path | 
 | 549 |             if len(bbfilespec) > len(closepath): | 
 | 550 |                 closepath = bbfilespec | 
 | 551 |     else: | 
 | 552 |         # Unfortunately the bbappend layer and the original recipe's layer don't have the same structure | 
 | 553 |         if closepath: | 
 | 554 |             # bbappend layer's layer.conf at least has a spec that picks up .bbappend files | 
 | 555 |             # Now we just need to substitute out any wildcards | 
 | 556 |             appendsubdir = os.path.relpath(os.path.dirname(closepath), destlayerdir) | 
 | 557 |             if 'recipes-*' in appendsubdir: | 
 | 558 |                 # Try to copy this part from the original recipe path | 
 | 559 |                 res = re.search('/recipes-[^/]+/', recipefile) | 
 | 560 |                 if res: | 
 | 561 |                     appendsubdir = appendsubdir.replace('/recipes-*/', res.group(0)) | 
 | 562 |             # This is crude, but we have to do something | 
 | 563 |             appendsubdir = appendsubdir.replace('*', recipefn.split('_')[0]) | 
 | 564 |             appendsubdir = appendsubdir.replace('?', 'a') | 
 | 565 |             appendpath = os.path.join(destlayerdir, appendsubdir, appendfn) | 
 | 566 |         else: | 
 | 567 |             pathok = False | 
 | 568 |     return (appendpath, pathok) | 
 | 569 |  | 
 | 570 |  | 
 | 571 | def bbappend_recipe(rd, destlayerdir, srcfiles, install=None, wildcardver=False, machine=None, extralines=None, removevalues=None): | 
 | 572 |     """ | 
 | 573 |     Writes a bbappend file for a recipe | 
 | 574 |     Parameters: | 
 | 575 |         rd: data dictionary for the recipe | 
 | 576 |         destlayerdir: base directory of the layer to place the bbappend in | 
 | 577 |             (subdirectory path from there will be determined automatically) | 
 | 578 |         srcfiles: dict of source files to add to SRC_URI, where the value | 
 | 579 |             is the full path to the file to be added, and the value is the | 
 | 580 |             original filename as it would appear in SRC_URI or None if it | 
 | 581 |             isn't already present. You may pass None for this parameter if | 
 | 582 |             you simply want to specify your own content via the extralines | 
 | 583 |             parameter. | 
 | 584 |         install: dict mapping entries in srcfiles to a tuple of two elements: | 
 | 585 |             install path (*without* ${D} prefix) and permission value (as a | 
 | 586 |             string, e.g. '0644'). | 
 | 587 |         wildcardver: True to use a % wildcard in the bbappend filename, or | 
 | 588 |             False to make the bbappend specific to the recipe version. | 
 | 589 |         machine: | 
 | 590 |             If specified, make the changes in the bbappend specific to this | 
 | 591 |             machine. This will also cause PACKAGE_ARCH = "${MACHINE_ARCH}" | 
 | 592 |             to be added to the bbappend. | 
 | 593 |         extralines: | 
 | 594 |             Extra lines to add to the bbappend. This may be a dict of name | 
 | 595 |             value pairs, or simply a list of the lines. | 
 | 596 |         removevalues: | 
 | 597 |             Variable values to remove - a dict of names/values. | 
 | 598 |     """ | 
 | 599 |  | 
 | 600 |     if not removevalues: | 
 | 601 |         removevalues = {} | 
 | 602 |  | 
 | 603 |     # Determine how the bbappend should be named | 
 | 604 |     appendpath, pathok = get_bbappend_path(rd, destlayerdir, wildcardver) | 
 | 605 |     if not appendpath: | 
 | 606 |         bb.error('Unable to determine layer directory containing %s' % recipefile) | 
 | 607 |         return (None, None) | 
 | 608 |     if not pathok: | 
 | 609 |         bb.warn('Unable to determine correct subdirectory path for bbappend file - check that what %s adds to BBFILES also matches .bbappend files. Using %s for now, but until you fix this the bbappend will not be applied.' % (os.path.join(destlayerdir, 'conf', 'layer.conf'), os.path.dirname(appendpath))) | 
 | 610 |  | 
 | 611 |     appenddir = os.path.dirname(appendpath) | 
 | 612 |     bb.utils.mkdirhier(appenddir) | 
 | 613 |  | 
 | 614 |     # FIXME check if the bbappend doesn't get overridden by a higher priority layer? | 
 | 615 |  | 
 | 616 |     layerdirs = [os.path.abspath(layerdir) for layerdir in rd.getVar('BBLAYERS', True).split()] | 
 | 617 |     if not os.path.abspath(destlayerdir) in layerdirs: | 
 | 618 |         bb.warn('Specified layer is not currently enabled in bblayers.conf, you will need to add it before this bbappend will be active') | 
 | 619 |  | 
 | 620 |     bbappendlines = [] | 
 | 621 |     if extralines: | 
 | 622 |         if isinstance(extralines, dict): | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 623 |             for name, value in extralines.items(): | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 624 |                 bbappendlines.append((name, '=', value)) | 
 | 625 |         else: | 
 | 626 |             # Do our best to split it | 
 | 627 |             for line in extralines: | 
 | 628 |                 if line[-1] == '\n': | 
 | 629 |                     line = line[:-1] | 
 | 630 |                 splitline = line.split(None, 2) | 
 | 631 |                 if len(splitline) == 3: | 
 | 632 |                     bbappendlines.append(tuple(splitline)) | 
 | 633 |                 else: | 
 | 634 |                     raise Exception('Invalid extralines value passed') | 
 | 635 |  | 
 | 636 |     def popline(varname): | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 637 |         for i in range(0, len(bbappendlines)): | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 638 |             if bbappendlines[i][0] == varname: | 
 | 639 |                 line = bbappendlines.pop(i) | 
 | 640 |                 return line | 
 | 641 |         return None | 
 | 642 |  | 
 | 643 |     def appendline(varname, op, value): | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 644 |         for i in range(0, len(bbappendlines)): | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 645 |             item = bbappendlines[i] | 
 | 646 |             if item[0] == varname: | 
 | 647 |                 bbappendlines[i] = (item[0], item[1], item[2] + ' ' + value) | 
 | 648 |                 break | 
 | 649 |         else: | 
 | 650 |             bbappendlines.append((varname, op, value)) | 
 | 651 |  | 
 | 652 |     destsubdir = rd.getVar('PN', True) | 
 | 653 |     if srcfiles: | 
 | 654 |         bbappendlines.append(('FILESEXTRAPATHS_prepend', ':=', '${THISDIR}/${PN}:')) | 
 | 655 |  | 
 | 656 |     appendoverride = '' | 
 | 657 |     if machine: | 
 | 658 |         bbappendlines.append(('PACKAGE_ARCH', '=', '${MACHINE_ARCH}')) | 
 | 659 |         appendoverride = '_%s' % machine | 
 | 660 |     copyfiles = {} | 
 | 661 |     if srcfiles: | 
 | 662 |         instfunclines = [] | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 663 |         for newfile, origsrcfile in srcfiles.items(): | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 664 |             srcfile = origsrcfile | 
 | 665 |             srcurientry = None | 
 | 666 |             if not srcfile: | 
 | 667 |                 srcfile = os.path.basename(newfile) | 
 | 668 |                 srcurientry = 'file://%s' % srcfile | 
 | 669 |                 # Double-check it's not there already | 
 | 670 |                 # FIXME do we care if the entry is added by another bbappend that might go away? | 
 | 671 |                 if not srcurientry in rd.getVar('SRC_URI', True).split(): | 
 | 672 |                     if machine: | 
 | 673 |                         appendline('SRC_URI_append%s' % appendoverride, '=', ' ' + srcurientry) | 
 | 674 |                     else: | 
 | 675 |                         appendline('SRC_URI', '+=', srcurientry) | 
 | 676 |             copyfiles[newfile] = srcfile | 
 | 677 |             if install: | 
 | 678 |                 institem = install.pop(newfile, None) | 
 | 679 |                 if institem: | 
 | 680 |                     (destpath, perms) = institem | 
 | 681 |                     instdestpath = replace_dir_vars(destpath, rd) | 
 | 682 |                     instdirline = 'install -d ${D}%s' % os.path.dirname(instdestpath) | 
 | 683 |                     if not instdirline in instfunclines: | 
 | 684 |                         instfunclines.append(instdirline) | 
 | 685 |                     instfunclines.append('install -m %s ${WORKDIR}/%s ${D}%s' % (perms, os.path.basename(srcfile), instdestpath)) | 
 | 686 |         if instfunclines: | 
 | 687 |             bbappendlines.append(('do_install_append%s()' % appendoverride, '', instfunclines)) | 
 | 688 |  | 
 | 689 |     bb.note('Writing append file %s' % appendpath) | 
 | 690 |  | 
 | 691 |     if os.path.exists(appendpath): | 
 | 692 |         # Work around lack of nonlocal in python 2 | 
 | 693 |         extvars = {'destsubdir': destsubdir} | 
 | 694 |  | 
 | 695 |         def appendfile_varfunc(varname, origvalue, op, newlines): | 
 | 696 |             if varname == 'FILESEXTRAPATHS_prepend': | 
 | 697 |                 if origvalue.startswith('${THISDIR}/'): | 
 | 698 |                     popline('FILESEXTRAPATHS_prepend') | 
 | 699 |                     extvars['destsubdir'] = rd.expand(origvalue.split('${THISDIR}/', 1)[1].rstrip(':')) | 
 | 700 |             elif varname == 'PACKAGE_ARCH': | 
 | 701 |                 if machine: | 
 | 702 |                     popline('PACKAGE_ARCH') | 
 | 703 |                     return (machine, None, 4, False) | 
 | 704 |             elif varname.startswith('do_install_append'): | 
 | 705 |                 func = popline(varname) | 
 | 706 |                 if func: | 
 | 707 |                     instfunclines = [line.strip() for line in origvalue.strip('\n').splitlines()] | 
 | 708 |                     for line in func[2]: | 
 | 709 |                         if not line in instfunclines: | 
 | 710 |                             instfunclines.append(line) | 
 | 711 |                     return (instfunclines, None, 4, False) | 
 | 712 |             else: | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 713 |                 splitval = split_var_value(origvalue, assignment=False) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 714 |                 changed = False | 
 | 715 |                 removevar = varname | 
 | 716 |                 if varname in ['SRC_URI', 'SRC_URI_append%s' % appendoverride]: | 
 | 717 |                     removevar = 'SRC_URI' | 
 | 718 |                     line = popline(varname) | 
 | 719 |                     if line: | 
 | 720 |                         if line[2] not in splitval: | 
 | 721 |                             splitval.append(line[2]) | 
 | 722 |                             changed = True | 
 | 723 |                 else: | 
 | 724 |                     line = popline(varname) | 
 | 725 |                     if line: | 
 | 726 |                         splitval = [line[2]] | 
 | 727 |                         changed = True | 
 | 728 |  | 
 | 729 |                 if removevar in removevalues: | 
 | 730 |                     remove = removevalues[removevar] | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 731 |                     if isinstance(remove, str): | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 732 |                         if remove in splitval: | 
 | 733 |                             splitval.remove(remove) | 
 | 734 |                             changed = True | 
 | 735 |                     else: | 
 | 736 |                         for removeitem in remove: | 
 | 737 |                             if removeitem in splitval: | 
 | 738 |                                 splitval.remove(removeitem) | 
 | 739 |                                 changed = True | 
 | 740 |  | 
 | 741 |                 if changed: | 
 | 742 |                     newvalue = splitval | 
 | 743 |                     if len(newvalue) == 1: | 
 | 744 |                         # Ensure it's written out as one line | 
 | 745 |                         if '_append' in varname: | 
 | 746 |                             newvalue = ' ' + newvalue[0] | 
 | 747 |                         else: | 
 | 748 |                             newvalue = newvalue[0] | 
 | 749 |                     if not newvalue and (op in ['+=', '.='] or '_append' in varname): | 
 | 750 |                         # There's no point appending nothing | 
 | 751 |                         newvalue = None | 
 | 752 |                     if varname.endswith('()'): | 
 | 753 |                         indent = 4 | 
 | 754 |                     else: | 
 | 755 |                         indent = -1 | 
 | 756 |                     return (newvalue, None, indent, True) | 
 | 757 |             return (origvalue, None, 4, False) | 
 | 758 |  | 
 | 759 |         varnames = [item[0] for item in bbappendlines] | 
 | 760 |         if removevalues: | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 761 |             varnames.extend(list(removevalues.keys())) | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 762 |  | 
 | 763 |         with open(appendpath, 'r') as f: | 
 | 764 |             (updated, newlines) = bb.utils.edit_metadata(f, varnames, appendfile_varfunc) | 
 | 765 |  | 
 | 766 |         destsubdir = extvars['destsubdir'] | 
 | 767 |     else: | 
 | 768 |         updated = False | 
 | 769 |         newlines = [] | 
 | 770 |  | 
 | 771 |     if bbappendlines: | 
 | 772 |         for line in bbappendlines: | 
 | 773 |             if line[0].endswith('()'): | 
 | 774 |                 newlines.append('%s {\n    %s\n}\n' % (line[0], '\n    '.join(line[2]))) | 
 | 775 |             else: | 
 | 776 |                 newlines.append('%s %s "%s"\n\n' % line) | 
 | 777 |         updated = True | 
 | 778 |  | 
 | 779 |     if updated: | 
 | 780 |         with open(appendpath, 'w') as f: | 
 | 781 |             f.writelines(newlines) | 
 | 782 |  | 
 | 783 |     if copyfiles: | 
 | 784 |         if machine: | 
 | 785 |             destsubdir = os.path.join(destsubdir, machine) | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 786 |         for newfile, srcfile in copyfiles.items(): | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 787 |             filedest = os.path.join(appenddir, destsubdir, os.path.basename(srcfile)) | 
 | 788 |             if os.path.abspath(newfile) != os.path.abspath(filedest): | 
 | 789 |                 bb.note('Copying %s to %s' % (newfile, filedest)) | 
 | 790 |                 bb.utils.mkdirhier(os.path.dirname(filedest)) | 
 | 791 |                 shutil.copyfile(newfile, filedest) | 
 | 792 |  | 
 | 793 |     return (appendpath, os.path.join(appenddir, destsubdir)) | 
 | 794 |  | 
 | 795 |  | 
 | 796 | def find_layerdir(fn): | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 797 |     """ Figure out the path to the base of the layer containing a file (e.g. a recipe)""" | 
 | 798 |     pth = fn | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 799 |     layerdir = '' | 
 | 800 |     while pth: | 
 | 801 |         if os.path.exists(os.path.join(pth, 'conf', 'layer.conf')): | 
 | 802 |             layerdir = pth | 
 | 803 |             break | 
 | 804 |         pth = os.path.dirname(pth) | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 805 |         if pth == '/': | 
 | 806 |             return None | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 807 |     return layerdir | 
 | 808 |  | 
 | 809 |  | 
 | 810 | def replace_dir_vars(path, d): | 
 | 811 |     """Replace common directory paths with appropriate variable references (e.g. /etc becomes ${sysconfdir})""" | 
 | 812 |     dirvars = {} | 
 | 813 |     # Sort by length so we get the variables we're interested in first | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 814 |     for var in sorted(list(d.keys()), key=len): | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 815 |         if var.endswith('dir') and var.lower() == var: | 
 | 816 |             value = d.getVar(var, True) | 
 | 817 |             if value.startswith('/') and not '\n' in value and value not in dirvars: | 
 | 818 |                 dirvars[value] = var | 
| Patrick Williams | c0f7c04 | 2017-02-23 20:41:17 -0600 | [diff] [blame] | 819 |     for dirpath in sorted(list(dirvars.keys()), reverse=True): | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 820 |         path = path.replace(dirpath, '${%s}' % dirvars[dirpath]) | 
 | 821 |     return path | 
 | 822 |  | 
 | 823 | def get_recipe_pv_without_srcpv(pv, uri_type): | 
 | 824 |     """ | 
 | 825 |     Get PV without SRCPV common in SCM's for now only | 
 | 826 |     support git. | 
 | 827 |  | 
 | 828 |     Returns tuple with pv, prefix and suffix. | 
 | 829 |     """ | 
 | 830 |     pfx = '' | 
 | 831 |     sfx = '' | 
 | 832 |  | 
 | 833 |     if uri_type == 'git': | 
 | 834 |         git_regex = re.compile("(?P<pfx>v?)(?P<ver>[^\+]*)((?P<sfx>\+(git)?r?(AUTOINC\+))(?P<rev>.*))?") | 
 | 835 |         m = git_regex.match(pv) | 
 | 836 |  | 
 | 837 |         if m: | 
 | 838 |             pv = m.group('ver') | 
 | 839 |             pfx = m.group('pfx') | 
 | 840 |             sfx = m.group('sfx') | 
 | 841 |     else: | 
 | 842 |         regex = re.compile("(?P<pfx>(v|r)?)(?P<ver>.*)") | 
 | 843 |         m = regex.match(pv) | 
 | 844 |         if m: | 
 | 845 |             pv = m.group('ver') | 
 | 846 |             pfx = m.group('pfx') | 
 | 847 |  | 
 | 848 |     return (pv, pfx, sfx) | 
 | 849 |  | 
 | 850 | def get_recipe_upstream_version(rd): | 
 | 851 |     """ | 
 | 852 |         Get upstream version of recipe using bb.fetch2 methods with support for | 
 | 853 |         http, https, ftp and git. | 
 | 854 |  | 
 | 855 |         bb.fetch2 exceptions can be raised, | 
 | 856 |             FetchError when don't have network access or upstream site don't response. | 
 | 857 |             NoMethodError when uri latest_versionstring method isn't implemented. | 
 | 858 |  | 
 | 859 |         Returns a dictonary with version, type and datetime. | 
 | 860 |         Type can be A for Automatic, M for Manual and U for Unknown. | 
 | 861 |     """ | 
 | 862 |     from bb.fetch2 import decodeurl | 
 | 863 |     from datetime import datetime | 
 | 864 |  | 
 | 865 |     ru = {} | 
 | 866 |     ru['version'] = '' | 
 | 867 |     ru['type'] = 'U' | 
 | 868 |     ru['datetime'] = '' | 
 | 869 |  | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 870 |     pv = rd.getVar('PV', True) | 
 | 871 |  | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 872 |     # XXX: If don't have SRC_URI means that don't have upstream sources so | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 873 |     # returns the current recipe version, so that upstream version check | 
 | 874 |     # declares a match. | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 875 |     src_uris = rd.getVar('SRC_URI', True) | 
 | 876 |     if not src_uris: | 
| Patrick Williams | f1e5d69 | 2016-03-30 15:21:19 -0500 | [diff] [blame] | 877 |         ru['version'] = pv | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 878 |         ru['type'] = 'M' | 
 | 879 |         ru['datetime'] = datetime.now() | 
 | 880 |         return ru | 
 | 881 |  | 
 | 882 |     # XXX: we suppose that the first entry points to the upstream sources | 
 | 883 |     src_uri = src_uris.split()[0] | 
 | 884 |     uri_type, _, _, _, _, _ =  decodeurl(src_uri) | 
 | 885 |  | 
| Patrick Williams | c124f4f | 2015-09-15 14:41:29 -0500 | [diff] [blame] | 886 |     manual_upstream_version = rd.getVar("RECIPE_UPSTREAM_VERSION", True) | 
 | 887 |     if manual_upstream_version: | 
 | 888 |         # manual tracking of upstream version. | 
 | 889 |         ru['version'] = manual_upstream_version | 
 | 890 |         ru['type'] = 'M' | 
 | 891 |  | 
 | 892 |         manual_upstream_date = rd.getVar("CHECK_DATE", True) | 
 | 893 |         if manual_upstream_date: | 
 | 894 |             date = datetime.strptime(manual_upstream_date, "%b %d, %Y") | 
 | 895 |         else: | 
 | 896 |             date = datetime.now() | 
 | 897 |         ru['datetime'] = date | 
 | 898 |  | 
 | 899 |     elif uri_type == "file": | 
 | 900 |         # files are always up-to-date | 
 | 901 |         ru['version'] =  pv | 
 | 902 |         ru['type'] = 'A' | 
 | 903 |         ru['datetime'] = datetime.now() | 
 | 904 |     else: | 
 | 905 |         ud = bb.fetch2.FetchData(src_uri, rd) | 
 | 906 |         pupver = ud.method.latest_versionstring(ud, rd) | 
 | 907 |         (upversion, revision) = pupver | 
 | 908 |  | 
 | 909 |         # format git version version+gitAUTOINC+HASH | 
 | 910 |         if uri_type == 'git': | 
 | 911 |             (pv, pfx, sfx) = get_recipe_pv_without_srcpv(pv, uri_type) | 
 | 912 |  | 
 | 913 |             # if contains revision but not upversion use current pv | 
 | 914 |             if upversion == '' and revision: | 
 | 915 |                 upversion = pv | 
 | 916 |  | 
 | 917 |             if upversion: | 
 | 918 |                 tmp = upversion | 
 | 919 |                 upversion = '' | 
 | 920 |  | 
 | 921 |                 if pfx: | 
 | 922 |                     upversion = pfx + tmp | 
 | 923 |                 else: | 
 | 924 |                     upversion = tmp | 
 | 925 |  | 
 | 926 |                 if sfx: | 
 | 927 |                     upversion = upversion + sfx + revision[:10] | 
 | 928 |  | 
 | 929 |         if upversion: | 
 | 930 |             ru['version'] = upversion | 
 | 931 |             ru['type'] = 'A' | 
 | 932 |  | 
 | 933 |         ru['datetime'] = datetime.now() | 
 | 934 |  | 
 | 935 |     return ru |